From 6f628374953ff9d032e6f980f6497ca343b86026 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 2 Feb 2024 23:12:04 +0800 Subject: [PATCH 001/438] =?UTF-8?q?mod:=20media=5Fkit=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 27 +++++++++++++++------------ pubspec.yaml | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d9b6a01d..e98d7443 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -848,10 +848,11 @@ packages: media_kit: dependency: "direct main" description: - name: media_kit - sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive @@ -888,10 +889,11 @@ packages: media_kit_libs_video: dependency: "direct main" description: - name: media_kit_libs_video - sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" - url: "https://pub.flutter-io.cn" - source: hosted + path: "libs/universal/media_kit_libs_video" + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.0.4" media_kit_libs_windows_video: dependency: transitive @@ -912,10 +914,11 @@ packages: media_kit_video: dependency: "direct main" description: - name: media_kit_video - sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit_video + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.2.4" meta: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index f4b8fe61..41cc829d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,9 +85,21 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: ^1.1.10 # Primary package. - media_kit_video: ^1.2.4 # For video rendering. - media_kit_libs_video: ^1.0.4 + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./libs/universal/media_kit_libs_video # 媒体通知 audio_service: ^0.18.12 @@ -156,6 +168,23 @@ dev_dependencies: hive_generator: ^2.0.0 build_runner: ^2.3.3 +dependency_overrides: + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./libs/universal/media_kit_libs_video + flutter_launcher_icons: android: true ios: true From 2dbef3fee24ff5e9320b2c28f4e2e1835aa38416 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 19 Mar 2024 23:23:14 +0800 Subject: [PATCH 002/438] =?UTF-8?q?mod:=20media=5Fkit=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 12 ++++++------ pubspec.yaml | 27 ++++++--------------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index a42bd421..fc479e1a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -858,8 +858,8 @@ packages: dependency: "direct main" description: path: media_kit - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.1.10+1" @@ -899,8 +899,8 @@ packages: dependency: "direct main" description: path: "libs/universal/media_kit_libs_video" - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.0.4" @@ -924,8 +924,8 @@ packages: dependency: "direct main" description: path: media_kit_video - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.2.4" diff --git a/pubspec.yaml b/pubspec.yaml index 23cc509f..e6690a37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,21 +85,9 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit - media_kit_video: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit_video - media_kit_libs_video: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./libs/universal/media_kit_libs_video + media_kit: ^1.1.10 # Primary package. + media_kit_video: ^1.2.4 # For video rendering. + media_kit_libs_video: ^1.0.4 # 媒体通知 audio_service: ^0.18.12 @@ -177,18 +165,15 @@ dependency_overrides: media_kit: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit + path: media_kit media_kit_video: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit_video + path: media_kit_video media_kit_libs_video: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./libs/universal/media_kit_libs_video + path: libs/universal/media_kit_libs_video flutter_launcher_icons: android: true From 76784ee6647893098512226f39e41c57c22b2dc9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 19 Mar 2024 23:33:32 +0800 Subject: [PATCH 003/438] mod: seekTo --- lib/plugin/pl_player/controller.dart | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index aaded67d..465b3003 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -375,7 +375,13 @@ class PlPlayerController { } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( - dataSource, _looping, enableHA, width, height); + dataSource, + _looping, + enableHA, + width, + height, + seekTo, + ); // 获取视频时长 00:00 _duration.value = duration ?? _videoPlayerController!.state.duration; updateDurationSecond(); @@ -386,7 +392,7 @@ class PlPlayerController { if (!_listenersInitialized) { startListeners(); } - await _initializePlayer(seekTo: seekTo, duration: _duration.value); + await _initializePlayer(duration: _duration.value); bool autoEnterFullcreen = setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); if (autoEnterFullcreen && _isFirstTime) { @@ -406,6 +412,7 @@ class PlPlayerController { bool enableHA, double? width, double? height, + Duration? seekTo, ) async { // 每次配置时先移除监听 removeListeners(); @@ -488,7 +495,11 @@ class PlPlayerController { ); } player.open( - Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), + Media( + dataSource.videoSource!, + httpHeaders: dataSource.httpHeaders, + start: seekTo ?? Duration.zero, + ), play: false, ); // 音轨 @@ -501,7 +512,6 @@ class PlPlayerController { // 开始播放 Future _initializePlayer({ - Duration seekTo = Duration.zero, Duration? duration, }) async { // 设置倍速 @@ -519,11 +529,6 @@ class PlPlayerController { // await setLooping(_looping); // } - // 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } - // 自动播放 if (_autoPlay) { await play(duration: duration); @@ -637,21 +642,14 @@ class PlPlayerController { await _videoPlayerController?.stream.buffer.first; } await _videoPlayerController?.seek(position); - // if (playerStatus.stopped) { - // play(); - // } } else { print('seek duration else'); _timerForSeek?.cancel(); _timerForSeek = Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { - //_timerForSeek = null; if (duration.value.inSeconds != 0) { await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); - // if (playerStatus.status.value == PlayerStatus.paused) { - // play(); - // } t.cancel(); _timerForSeek = null; } From 53b72bec2543218e638bb62447ec9502c1311765 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 13:16:13 +0800 Subject: [PATCH 004/438] =?UTF-8?q?mod:=20read=20schame=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 13 +++++++------ lib/common/widgets/network_img_layer.dart | 2 +- lib/utils/app_scheme.dart | 18 ++++++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8db59815..2c1a635b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,7 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter + - FlutterMacOS - ReachabilitySwift - device_info_plus (0.0.1): - Flutter @@ -38,7 +39,7 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - ReachabilitySwift (5.0.0) - saver_gallery (0.0.1): @@ -71,7 +72,7 @@ DEPENDENCIES: - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`) @@ -113,7 +114,7 @@ EXTERNAL SOURCES: auto_orientation: :path: ".symlinks/plugins/auto_orientation/ios" connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/ios" + :path: ".symlinks/plugins/connectivity_plus/darwin" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -166,7 +167,7 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a + connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 @@ -180,7 +181,7 @@ SPEC CHECKSUMS: media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 @@ -193,7 +194,7 @@ SPEC CHECKSUMS: volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 - webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a + webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 06c35974..173db853 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -36,7 +36,7 @@ class NetworkImgLayer extends StatelessWidget { final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; - print(imageUrl); + // print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index bb9d556f..ffe90e35 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -20,7 +20,7 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { if (value != null) { - _fullPathPush(value); + _routePush(value); } }); @@ -37,7 +37,6 @@ class PiliSchame { final String scheme = value.scheme; final String host = value.host; final String path = value.path; - if (scheme == 'bilibili') { if (host == 'root') { Navigator.popUntil( @@ -85,6 +84,14 @@ class PiliSchame { } } else if (host == 'search') { Get.toNamed('/searchResult', parameters: {'keyword': ''}); + } else if (host == 'article') { + final String id = path.split('/').last.split('?').first; + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/cv$id', + 'title': 'cv$id', + 'id': 'cv$id', + 'dynamicType': 'read' + }); } } if (scheme == 'https') { @@ -226,6 +233,13 @@ class PiliSchame { break; case 'read': print('专栏'); + String id = 'cv${matchNum(query!['id']!).first}'; + Get.toNamed('/htmlRender', parameters: { + 'url': value.dataString!, + 'title': '', + 'id': id, + 'dynamicType': 'read' + }); break; case 'space': print('个人空间'); From 53941469cec5405c36ed23acb112e7410cbe0b6c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 28 Mar 2024 00:29:22 +0800 Subject: [PATCH 005/438] =?UTF-8?q?opt:=20=E5=90=91=E4=B8=8B=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E5=8F=AF=E7=94=A8=E8=A7=86=E9=A2=91=E6=B8=85=E6=99=B0?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/utils.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index adcc7b5a..bc3252b8 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -208,14 +208,16 @@ class Utils { static int findClosestNumber(int target, List numbers) { int minDiff = 127; - late int closestNumber; + int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 try { for (int number in numbers) { - int diff = (number - target).abs(); + if (number < target) { + int diff = target - number; // 计算目标值与当前整数的差值 - if (diff < minDiff) { - minDiff = diff; - closestNumber = number; + if (diff < minDiff) { + minDiff = diff; + closestNumber = number; + } } } } catch (_) {} From 8897c4dd5b523599a3a3af75a03aac9c28ef51de Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 31 Mar 2024 11:11:50 +0800 Subject: [PATCH 006/438] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E6=97=B6?= =?UTF-8?q?=E6=B8=85=E9=99=A4=E6=97=A5=E5=BF=97=20issues=20#656?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 44bb1dcd..7fdaeeb0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ void main() async { .then((_) async { await GStrorage.init(); await setupServiceLocator(); + clearLogs(); Request(); await Request.setCookie(); RecommendFilter(); From 469a5ec691f9f96e0a7250ee1d258dde502099a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Apr 2024 23:03:31 +0800 Subject: [PATCH 007/438] =?UTF-8?q?mod:=20=E4=B8=AA=E4=BA=BA=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/info.dart | 5 +++ lib/pages/member/view.dart | 13 +++++-- lib/pages/member/widgets/seasons.dart | 53 ++++++++++----------------- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/lib/models/member/info.dart b/lib/models/member/info.dart index 789131ee..83f94c54 100644 --- a/lib/models/member/info.dart +++ b/lib/models/member/info.dart @@ -47,18 +47,23 @@ class Vip { this.status, this.dueDate, this.label, + this.nicknameColor, }); int? type; int? status; int? dueDate; Map? label; + int? nicknameColor; Vip.fromJson(Map json) { type = json['type']; status = json['status']; dueDate = json['due_date']; label = json['label']; + nicknameColor = json['nickname_color'] == '' + ? null + : int.parse("0xFF${json['nickname_color'].replaceAll('#', '')}"); } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 0663e94e..c8a9f406 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -281,8 +281,8 @@ class _MemberPageState extends State future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data!; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => Stack( alignment: AlignmentDirectional.center, @@ -302,7 +302,14 @@ class _MemberPageState extends State style: Theme.of(context) .textTheme .titleMedium! - .copyWith(fontWeight: FontWeight.bold), + .copyWith( + fontWeight: FontWeight.bold, + color: _memberController.memberInfo.value + .vip!.nicknameColor != + null + ? Color(_memberController.memberInfo + .value.vip!.nicknameColor!) + : null), )), const SizedBox(width: 2), if (_memberController.memberInfo.value.sex == '女') diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 68c4077f..125c978f 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -18,45 +18,32 @@ class MemberSeasonsPanel extends StatelessWidget { itemBuilder: (context, index) { MemberSeasonsList item = data!.seasonsList![index]; return Padding( - padding: const EdgeInsets.only(bottom: 12, right: 4), + padding: const EdgeInsets.only(bottom: 12), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(bottom: 12, left: 4), - child: Row( - children: [ - Text( - item.meta!.name!, - maxLines: 1, - style: Theme.of(context).textTheme.titleSmall!, - ), - const SizedBox(width: 10), - PBadge( - stack: 'relative', - size: 'small', - text: item.meta!.total.toString(), - ), - const Spacer(), - SizedBox( - width: 35, - height: 35, - child: IconButton( - onPressed: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - icon: const Icon( - Icons.arrow_forward, - size: 20, - ), - ), - ) - ], + ListTile( + onTap: () => Get.toNamed( + '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), + title: Text( + item.meta!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall!, + ), + dense: true, + leading: PBadge( + stack: 'relative', + size: 'small', + text: item.meta!.total.toString(), + ), + trailing: const Icon( + Icons.arrow_forward, + size: 20, ), ), + const SizedBox(height: 10), LayoutBuilder( builder: (context, boxConstraints) { return GridView.builder( From b58df720fd7d8d45836662f2f39ecc42f3300c97 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 4 Apr 2024 23:41:16 +0800 Subject: [PATCH 008/438] =?UTF-8?q?mod:=20=E4=BC=98=E5=8C=96selectDialog?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/widgets/select_dialog.dart | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index 72119755..50229f9e 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -44,6 +44,7 @@ class _SelectDialogState extends State> { setState(() { _tempValue = value as T; }); + Navigator.pop(context, _tempValue); }, ), ] @@ -51,19 +52,6 @@ class _SelectDialogState extends State> { ), ); }), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, _tempValue), - child: const Text('确定'), - ) - ], ); } } From ca37d45eb94007be0fa666ab993fcf3a0049fe7b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:22:57 +0800 Subject: [PATCH 009/438] =?UTF-8?q?fix:=20=E5=85=A8=E5=B1=8F=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=90=88=E9=9B=86=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E6=9C=AA=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 7a12b0cf..e6a324cb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -83,7 +83,6 @@ class _HeaderControlState extends State { /// 设置面板 void showSettingSheet() { - // Scaffold.of(context).openDrawer(); showModalBottomSheet( elevation: 0, context: context, @@ -732,9 +731,12 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( - height: 45, - child: Center(child: Text('选择解码格式', style: titleStyle))), + const SizedBox( + height: 45, + child: Center( + child: Text('选择解码格式', style: titleStyle), + ), + ), Expanded( child: Material( child: ListView( @@ -1079,9 +1081,12 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( - height: 45, - child: Center(child: Text('选择播放顺序', style: titleStyle))), + const SizedBox( + height: 45, + child: Center( + child: Text('选择播放顺序', style: titleStyle), + ), + ), Expanded( child: Material( child: ListView( @@ -1166,11 +1171,13 @@ class _HeaderControlState extends State { children: [ ConstrainedBox( constraints: const BoxConstraints(maxWidth: 200), - child: Text( - videoIntroController.videoDetail.value.title ?? '', - style: const TextStyle( - color: Colors.white, - fontSize: 16, + child: Obx( + () => Text( + videoIntroController.videoDetail.value.title ?? '', + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), ), ), ), From 04bfc294525d47511ea9c005fbcfa2c9addae142 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:33:13 +0800 Subject: [PATCH 010/438] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/view.dart | 5 ++++ .../live_room/widgets/bottom_control.dart | 28 +++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 1e5c29c5..37981b1d 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -62,6 +62,11 @@ class _LiveRoomPageState extends State { controller: plPlayerController, liveRoomCtr: _liveRoomController, floating: floating, + onRefresh: () { + setState(() { + _futureBuilderFuture = _liveRoomController.queryLiveInfo(); + }); + }, ), ); } else { diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 3c908d71..e5a9d6c9 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -14,10 +14,12 @@ class BottomControl extends StatefulWidget implements PreferredSizeWidget { final PlPlayerController? controller; final LiveRoomController? liveRoomCtr; final Floating? floating; + final Function? onRefresh; const BottomControl({ this.controller, this.liveRoomCtr, this.floating, + this.onRefresh, Key? key, }) : super(key: key); @@ -61,6 +63,14 @@ class _BottomControlState extends State { // ), // fuc: () => Get.back(), // ), + ComBtn( + icon: const Icon( + Icons.refresh_outlined, + size: 18, + color: Colors.white, + ), + fuc: widget.onRefresh, + ), const Spacer(), // ComBtn( // icon: const Icon( @@ -150,21 +160,3 @@ class _BottomControlState extends State { ); } } - -class MSliderTrackShape extends RoundedRectSliderTrackShape { - @override - Rect getPreferredRect({ - required RenderBox parentBox, - Offset offset = Offset.zero, - SliderThemeData? sliderTheme, - bool isEnabled = false, - bool isDiscrete = false, - }) { - const double trackHeight = 3; - final double trackLeft = offset.dx; - final double trackTop = - offset.dy + (parentBox.size.height - trackHeight) / 2 + 4; - final double trackWidth = parentBox.size.width; - return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); - } -} From 9f9471d7f9961332077b98380c5f8afa096e94cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:42:09 +0800 Subject: [PATCH 011/438] =?UTF-8?q?fix:=20up=E4=B8=BB=E9=A1=B5=E4=B8=93?= =?UTF-8?q?=E6=A0=8F=E8=A7=86=E9=A2=91=E6=97=B6=E9=95=BF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_seasons/widgets/item.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 6398c5eb..4df74b70 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -25,7 +25,7 @@ class MemberSeasonsItem extends StatelessWidget { child: InkWell( onTap: () async { int cid = - await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); + await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); }, @@ -51,8 +51,7 @@ class MemberSeasonsItem extends StatelessWidget { bottom: 6, right: 6, type: 'gray', - text: Utils.CustomStamp_str( - timestamp: seasonItem.pubdate, date: 'YY-MM-DD'), + text: Utils.timeFormat(seasonItem.duration), ) ], ); From 3f16a36bacc6d140ed6355ba95e9ad1e3f1c4de4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Apr 2024 23:34:59 +0800 Subject: [PATCH 012/438] =?UTF-8?q?fix:=20=E6=A5=BC=E4=B8=AD=E6=A5=BC?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E8=AF=B7=E6=B1=82=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_reply/view.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index e8754a31..344ca1b1 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -140,8 +140,8 @@ class _VideoReplyReplyPanelState extends State { future: _futureBuilderFuture, builder: (BuildContext context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - final Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { // 请求成功 return Obx( () => SliverList( @@ -199,7 +199,7 @@ class _VideoReplyReplyPanelState extends State { } else { // 请求错误 return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求错误', fn: () => setState(() {}), ); } From 7fd23044313690d15b7b07586d251605c024191a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Apr 2024 23:52:21 +0800 Subject: [PATCH 013/438] =?UTF-8?q?fix:=20pip=E8=BF=9B=E5=85=A5=E6=97=B6ap?= =?UTF-8?q?pbarStream=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index ffa50476..a061a0e2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -107,7 +107,7 @@ class _VideoDetailPageState extends State // 流 appbarStreamListen() { - appbarStream = StreamController(); + appbarStream = StreamController.broadcast(); _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; From 4b3dd3ca599793d74e76abd0e71f943b2fd0631a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 11 Apr 2024 23:26:06 +0800 Subject: [PATCH 014/438] =?UTF-8?q?opt:=20navigationBar=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/view.dart | 130 +++++++++++++-------------- lib/pages/setting/style_setting.dart | 2 +- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c551e690..e4e980af 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -127,81 +127,81 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { }, children: _mainController.pages, ), - bottomNavigationBar: StreamBuilder( - stream: _mainController.hideTabBar - ? _mainController.bottomBarStream.stream - : StreamController.broadcast().stream, - initialData: true, - builder: (context, AsyncSnapshot snapshot) { - return AnimatedSlide( - curve: Curves.easeInOutCubicEmphasized, - duration: const Duration(milliseconds: 500), - offset: Offset(0, snapshot.data ? 0 : 1), - child: Obx( - () => enableMYBar - ? NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: _mainController.selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: Obx( - () => Badge( - label: - _mainController.dynamicBadgeType.value == + bottomNavigationBar: _mainController.navigationBars.length > 1 + ? StreamBuilder( + stream: _mainController.hideTabBar + ? _mainController.bottomBarStream.stream + : StreamController.broadcast().stream, + initialData: true, + builder: (context, AsyncSnapshot snapshot) { + return AnimatedSlide( + curve: Curves.easeInOutCubicEmphasized, + duration: const Duration(milliseconds: 500), + offset: Offset(0, snapshot.data ? 0 : 1), + child: enableMYBar + ? NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: _mainController.selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: Obx( + () => Badge( + label: _mainController + .dynamicBadgeType.value == DynamicBadgeMode.number ? Text(e['count'].toString()) : null, - padding: - const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: - _mainController.dynamicBadgeType.value != + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: _mainController + .dynamicBadgeType.value != DynamicBadgeMode.hidden && e['count'] > 0, - child: e['icon'], - ), - ), - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ) - : BottomNavigationBar( - currentIndex: _mainController.selectedIndex, - onTap: (value) => setIndex(value), - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - items: [ - ..._mainController.navigationBars.map((e) { - return BottomNavigationBarItem( - icon: Obx( - () => Badge( - label: - _mainController.dynamicBadgeType.value == + child: e['icon'], + ), + ), + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ) + : BottomNavigationBar( + currentIndex: _mainController.selectedIndex, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: Obx( + () => Badge( + label: _mainController + .dynamicBadgeType.value == DynamicBadgeMode.number ? Text(e['count'].toString()) : null, - padding: - const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: - _mainController.dynamicBadgeType.value != + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: _mainController + .dynamicBadgeType.value != DynamicBadgeMode.hidden && e['count'] > 0, - child: e['icon'], - ), - ), - activeIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ), - ), - ); - }, - ), + child: e['icon'], + ), + ), + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), + ); + }, + ) + : null, ), ); } diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index d2403cff..364eabf0 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -287,7 +287,7 @@ class _StyleSettingState extends State { ListTile( dense: false, onTap: () => Get.toNamed('/navbarSetting'), - title: Text('navbar设置', style: titleStyle), + title: Text('底部导航栏设置', style: titleStyle), ), if (Platform.isAndroid) ListTile( From a74647eb61a36e1f0795f92593185c57c4e73bf6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 11 Apr 2024 23:29:51 +0800 Subject: [PATCH 015/438] =?UTF-8?q?opt:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E6=94=B6=E8=B5=B7=E9=A1=B6=E6=A0=8F&=E5=BA=95=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 2 +- lib/pages/main/controller.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index fb85be0b..ca70e1c4 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -35,7 +35,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { userLogin.value = userInfo != null; userFace.value = userInfo != null ? userInfo.face : ''; hideSearchBar = - setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); + setting.get(SettingBoxKey.hideSearchBar, defaultValue: false); if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { searchDefault(); } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index f929a1aa..c2e5c322 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -33,7 +33,7 @@ class MainController extends GetxController { if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) { Utils.checkUpdata(); } - hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); + hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: false); var userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; From 10435bb7b1fecff8552d5b5f523db10a2637da06 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 12 Apr 2024 00:01:32 +0800 Subject: [PATCH 016/438] =?UTF-8?q?mod:=20=E6=9C=80=E8=BF=91=E6=8A=95?= =?UTF-8?q?=E5=B8=81=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98=E5=B7=A6=E5=AF=B9?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_coin/widgets/item.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/member_coin/widgets/item.dart b/lib/pages/member_coin/widgets/item.dart index ea6e7ee1..de28585c 100644 --- a/lib/pages/member_coin/widgets/item.dart +++ b/lib/pages/member_coin/widgets/item.dart @@ -59,6 +59,7 @@ class MemberCoinsItem extends StatelessWidget { padding: const EdgeInsets.fromLTRB(5, 6, 0, 0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( coinItem.title!, From 1076c02a58173d4cdc25fbc85f7b03a9a2171557 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 12 Apr 2024 23:12:12 +0800 Subject: [PATCH 017/438] =?UTF-8?q?fix:=20android=2012=E6=9D=83=E9=99=90?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/download.dart | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/utils/download.dart b/lib/utils/download.dart index e27335d0..2aff8999 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -1,5 +1,7 @@ +import 'dart:io'; import 'dart:typed_data'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -11,7 +13,8 @@ class DownloadUtils { static Future requestStoragePer() async { await Permission.storage.request(); PermissionStatus status = await Permission.storage.status; - if (status == PermissionStatus.denied) { + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -40,7 +43,8 @@ class DownloadUtils { static Future requestPhotoPer() async { await Permission.photos.request(); PermissionStatus status = await Permission.photos.status; - if (status == PermissionStatus.denied) { + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -68,9 +72,20 @@ class DownloadUtils { static Future downloadImg(String imgUrl, {String imgType = 'cover'}) async { try { - if (!await requestPhotoPer()) { + if (!Platform.isAndroid || !await requestPhotoPer()) { return false; } + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt <= 32) { + if (!await requestStoragePer()) { + return false; + } + } else { + if (!await requestPhotoPer()) { + return false; + } + } + SmartDialog.showLoading(msg: '保存中'); var response = await Dio() .get(imgUrl, options: Options(responseType: ResponseType.bytes)); From 297ad6a46d600bdfc664f54d44a3ccb9ab6ba2bd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 01:41:24 +0800 Subject: [PATCH 018/438] =?UTF-8?q?opt:=20navBar=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 85 +++++++++++++++++++------------------- lib/pages/main/view.dart | 6 +-- lib/utils/global_data.dart | 6 +++ 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 44bb1dcd..c3f5814a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. import 'package:pilipala/utils/recommend_filter.dart'; @@ -63,14 +64,8 @@ void main() async { }, ); - // 小白条、导航栏沉浸 - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - statusBarColor: Colors.transparent, - )); Data.init(); + GlobalData(); PiliSchame.init(); DisableBatteryOpt(); }); @@ -133,45 +128,51 @@ class MyApp extends StatelessWidget { brightness: Brightness.dark, ); } + + final SnackBarThemeData snackBarThemeData = SnackBarThemeData( + actionTextColor: darkColorScheme.primary, + backgroundColor: darkColorScheme.secondaryContainer, + closeIconColor: darkColorScheme.secondary, + contentTextStyle: TextStyle(color: darkColorScheme.secondary), + elevation: 20, + ); + + ThemeData themeData = ThemeData( + // fontFamily: 'HarmonyOS', + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: snackBarThemeData, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ); + + // 小白条、导航栏沉浸 + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + systemNavigationBarColor: GlobalData().enableMYBar + ? themeData.colorScheme.surfaceVariant + : themeData.canvasColor, + systemNavigationBarDividerColor: GlobalData().enableMYBar + ? themeData.colorScheme.surfaceVariant + : themeData.canvasColor, + systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark + ? Brightness.light + : Brightness.dark, + statusBarColor: Colors.transparent, + )); + // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; return GetMaterialApp( title: 'PiLiPaLa', - theme: ThemeData( - // fontFamily: 'HarmonyOS', - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - useMaterial3: true, - snackBarTheme: SnackBarThemeData( - actionTextColor: lightColorScheme.primary, - backgroundColor: lightColorScheme.secondaryContainer, - closeIconColor: lightColorScheme.secondary, - contentTextStyle: TextStyle(color: lightColorScheme.secondary), - elevation: 20, - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), - ), - darkTheme: ThemeData( - // fontFamily: 'HarmonyOS', - colorScheme: currentThemeValue == ThemeType.light - ? lightColorScheme - : darkColorScheme, - useMaterial3: true, - snackBarTheme: SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ), - ), + theme: themeData, + darkTheme: themeData, localizationsDelegates: const [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index e4e980af..731134e9 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -10,6 +10,7 @@ import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import './controller.dart'; @@ -29,7 +30,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; - late bool enableMYBar; @override void initState() { @@ -37,7 +37,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _lastSelectTime = DateTime.now().millisecondsSinceEpoch; _mainController.pageController = PageController(initialPage: _mainController.selectedIndex); - enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); } void setIndex(int value) async { @@ -138,7 +137,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: enableMYBar + child: GlobalData().enableMYBar ? NavigationBar( onDestinationSelected: (value) => setIndex(value), selectedIndex: _mainController.selectedIndex, @@ -169,6 +168,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { ) : BottomNavigationBar( currentIndex: _mainController.selectedIndex, + type: BottomNavigationBarType.fixed, onTap: (value) => setIndex(value), iconSize: 16, selectedFontSize: 12, diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index ef3daf21..29791210 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -1,10 +1,16 @@ +import 'package:hive/hive.dart'; +import 'package:pilipala/utils/storage.dart'; import '../models/common/index.dart'; +Box setting = GStrorage.setting; + class GlobalData { int imgQuality = 10; FullScreenGestureMode fullScreenGestureMode = FullScreenGestureMode.values.last; bool enablePlayerControlAnimation = true; + final bool enableMYBar = + setting.get(SettingBoxKey.enableMYBar, defaultValue: true); // 私有构造函数 GlobalData._(); From ef6070aa8b1a6b52d840ce9e8cceb40d4ee4fd34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 10:35:06 +0800 Subject: [PATCH 019/438] mod: systemNavBarColor --- lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c3f5814a..3996a5fd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -156,10 +156,10 @@ class MyApp extends StatelessWidget { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( systemNavigationBarColor: GlobalData().enableMYBar - ? themeData.colorScheme.surfaceVariant + ? const Color(0x00010000) : themeData.canvasColor, systemNavigationBarDividerColor: GlobalData().enableMYBar - ? themeData.colorScheme.surfaceVariant + ? const Color(0x00010000) : themeData.canvasColor, systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark ? Brightness.light From 504d9e20653980e174876e6a002b9b6186da75c9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 10:55:14 +0800 Subject: [PATCH 020/438] opt: stream listener --- lib/pages/bangumi/view.dart | 19 ++------------ lib/pages/dynamics/detail/view.dart | 2 +- lib/pages/dynamics/view.dart | 14 ++-------- lib/pages/fav_detail/view.dart | 2 +- lib/pages/home/view.dart | 2 +- lib/pages/hot/view.dart | 19 ++------------ lib/pages/live/view.dart | 19 ++------------ lib/pages/main/view.dart | 2 +- lib/pages/media/view.dart | 19 ++------------ lib/pages/member/view.dart | 2 +- lib/pages/rank/zone/view.dart | 19 ++------------ lib/pages/rcmd/view.dart | 18 ++----------- lib/pages/subscription_detail/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- lib/utils/main_stream.dart | 34 +++++++++++++++++++++++++ 15 files changed, 55 insertions(+), 120 deletions(-) create mode 100644 lib/utils/main_stream.dart diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index f59f94a2..8759af65 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -2,13 +2,11 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:nil/nil.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; import 'widgets/bangumu_card_v.dart'; @@ -34,10 +32,6 @@ class _BangumiPageState extends State void initState() { super.initState(); scrollController = _bangumidController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; _futureBuilderFuture = _bangumidController.queryBangumiListFeed(); _futureBuilderFutureFollow = _bangumidController.queryBangumiFollow(); scrollController.addListener( @@ -49,16 +43,7 @@ class _BangumiPageState extends State _bangumidController.onLoad(); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 840cd33f..9da085f4 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -194,7 +194,7 @@ class _DynamicDetailPageState extends State centerTitle: false, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index fe594a43..82a555b1 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,15 +3,14 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; @@ -44,8 +43,6 @@ class _DynamicsPageState extends State _futureBuilderFuture = _dynamicsController.queryFollowDynamic(); _futureBuilderFutureUp = _dynamicsController.queryFollowUp(); scrollController = _dynamicsController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; scrollController.addListener( () async { if (scrollController.position.pixels >= @@ -55,14 +52,7 @@ class _DynamicsPageState extends State _dynamicsController.queryFollowDynamic(type: 'onLoad'); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - } + handleScrollEvent(scrollController); }, ); diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index d94f5149..74faa829 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -67,7 +67,7 @@ class _FavDetailPageState extends State { pinned: true, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index b0cef90b..cc228f6b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -171,7 +171,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: stream, + stream: stream!.distinct(), initialData: true, builder: (BuildContext context, AsyncSnapshot snapshot) { final RxBool isUserLoggedIn = ctr!.userLogin; diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 7a0a57ea..e2e20e73 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; @@ -9,9 +8,8 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; -import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/hot/controller.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; class HotPage extends StatefulWidget { const HotPage({Key? key}) : super(key: key); @@ -34,10 +32,6 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { super.initState(); _futureBuilderFuture = _hotController.queryHotFeed('init'); scrollController = _hotController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -47,16 +41,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { _hotController.onLoad(); } } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index f3f91c9e..c61d20b3 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -2,15 +2,13 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; import 'widgets/live_item.dart'; @@ -36,10 +34,6 @@ class _LivePageState extends State super.initState(); _futureBuilderFuture = _liveController.queryLiveList('init'); scrollController = _liveController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -49,16 +43,7 @@ class _LivePageState extends State _liveController.onLoad(); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c551e690..5353ac52 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -129,7 +129,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { ), bottomNavigationBar: StreamBuilder( stream: _mainController.hideTabBar - ? _mainController.bottomBarStream.stream + ? _mainController.bottomBarStream.stream.distinct() : StreamController.broadcast().stream, initialData: true, builder: (context, AsyncSnapshot snapshot) { diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 460c5648..0bb12039 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -1,13 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; -import 'package:media_kit/media_kit.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/user/fav_folder.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/media/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/utils.dart'; class MediaPage extends StatefulWidget { @@ -31,25 +29,12 @@ class _MediaPageState extends State mediaController = Get.put(MediaController()); _futureBuilderFuture = mediaController.queryFavFolder(); ScrollController scrollController = mediaController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - mediaController.userLogin.listen((status) { setState(() { _futureBuilderFuture = mediaController.queryFavFolder(); }); }); - scrollController.addListener( - () { - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - } - }, - ); + handleScrollEvent(scrollController); } @override diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index c8a9f406..015750db 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -65,7 +65,7 @@ class _MemberPageState extends State children: [ AppBar( title: StreamBuilder( - stream: appbarStream.stream, + stream: appbarStream.stream.distinct(), initialData: false, builder: (BuildContext context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index fbf8a524..04631a8c 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; @@ -9,9 +8,8 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/rank/zone/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; class ZonePage extends StatefulWidget { const ZonePage({Key? key, required this.rid}) : super(key: key); @@ -38,10 +36,6 @@ class _ZonePageState extends State _zoneController = Get.put(ZoneController(), tag: widget.rid.toString()); _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); scrollController = _zoneController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -51,16 +45,7 @@ class _ZonePageState extends State _zoneController.onLoad(); } } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index d732f370..acc1e654 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; @@ -10,8 +9,7 @@ import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; @@ -35,10 +33,6 @@ class _RcmdPageState extends State super.initState(); _futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); ScrollController scrollController = _rcmdController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -49,15 +43,7 @@ class _RcmdPageState extends State _rcmdController.onLoad(); }); } - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 93e0abbb..2c219e58 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -67,7 +67,7 @@ class _SubDetailPageState extends State { pinned: true, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 822d0a45..7bb4d909 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -618,7 +618,7 @@ class _VideoDetailPageState extends State /// 重新进入会刷新 // 播放完成/暂停播放 StreamBuilder( - stream: appbarStream.stream, + stream: appbarStream.stream.distinct(), initialData: 0, builder: ((context, snapshot) { return ScrollAppBar( diff --git a/lib/utils/main_stream.dart b/lib/utils/main_stream.dart new file mode 100644 index 00000000..20c160de --- /dev/null +++ b/lib/utils/main_stream.dart @@ -0,0 +1,34 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/rendering.dart'; +import 'package:get/get.dart'; + +import '../pages/home/index.dart'; +import '../pages/main/index.dart'; + +void handleScrollEvent( + ScrollController scrollController, + // StreamController mainStream, + // StreamController? searchBarStream, +) { + StreamController mainStream = + Get.find().bottomBarStream; + StreamController searchBarStream = + Get.find().searchBarStream; + EasyThrottle.throttle( + 'stream-throttler', + const Duration(milliseconds: 300), + () { + final ScrollDirection direction = + scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + }, + ); +} From 83d121fd9cd0d2978df8ce7ed9b5327c63f61c0b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 15:04:33 +0800 Subject: [PATCH 021/438] opt: sheetHeight --- lib/pages/video/detail/controller.dart | 2 + lib/pages/video/detail/introduction/view.dart | 6 +-- lib/pages/video/detail/reply_reply/view.dart | 44 +++++++++---------- lib/pages/video/detail/view.dart | 4 +- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 2f750a24..4d40e535 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -106,6 +106,7 @@ class VideoDetailController extends GetxController BottomControlType.fit, BottomControlType.fullscreen, ].obs; + RxDouble sheetHeight = 0.0.obs; @override void onInit() { @@ -172,6 +173,7 @@ class VideoDetailController extends GetxController firstFloor: firstFloor, replyType: ReplyType.video, source: 'videoDetail', + sheetHeight: sheetHeight.value, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 70fa578d..a7eae6d2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -372,7 +372,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), /// 点赞收藏转发 - actionGrid(context, videoIntroController), + Material(child: actionGrid(context, videoIntroController)), // 合集 videoPart 简洁 if (widget.videoDetail!.ugcSeason != null) ...[ Obx( @@ -381,7 +381,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { cid: videoIntroController.lastPlayCid.value != 0 ? videoIntroController.lastPlayCid.value : widget.videoDetail!.pages!.first.cid, - sheetHeight: sheetHeight, + sheetHeight: videoDetailCtr.sheetHeight.value, changeFuc: (bvid, cid, aid) => videoIntroController.changeSeasonOrbangu( bvid, @@ -399,7 +399,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => PagesPanel( pages: widget.videoDetail!.pages!, cid: videoIntroController.lastPlayCid.value, - sheetHeight: sheetHeight, + sheetHeight: videoDetailCtr.sheetHeight.value, changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( videoIntroController.bvid, cid, diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 344ca1b1..3fe84c71 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -19,6 +19,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.firstFloor, this.source, this.replyType, + this.sheetHeight, super.key, }); final int? oid; @@ -27,6 +28,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final ReplyItemModel? firstFloor; final String? source; final ReplyType? replyType; + final double? sheetHeight; @override State createState() => _VideoReplyReplyPanelState(); @@ -36,7 +38,6 @@ class _VideoReplyReplyPanelState extends State { late VideoReplyReplyController _videoReplyReplyController; late AnimationController replyAnimationCtl; final Box localCache = GStrorage.localCache; - late double sheetHeight; Future? _futureBuilderFuture; late ScrollController scrollController; @@ -62,7 +63,6 @@ class _VideoReplyReplyPanelState extends State { }, ); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); } @@ -77,33 +77,31 @@ class _VideoReplyReplyPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: widget.source == 'videoDetail' ? sheetHeight : null, + height: widget.source == 'videoDetail' ? widget.sheetHeight : null, color: Theme.of(context).colorScheme.background, child: Column( children: [ if (widget.source == 'videoDetail') - Container( - height: 45, - padding: const EdgeInsets.only(left: 12, right: 2), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('评论详情'), - IconButton( - icon: const Icon(Icons.close, size: 20), - onPressed: () { - _videoReplyReplyController.currentPage = 0; - widget.closePanel?.call; - Navigator.pop(context); - }, - ), - ], + AppBar( + toolbarHeight: 45, + automaticallyImplyLeading: false, + centerTitle: false, + title: Text( + '评论详情', + style: Theme.of(context).textTheme.titleSmall, ), + actions: [ + IconButton( + icon: const Icon(Icons.close, size: 20), + onPressed: () { + _videoReplyReplyController.currentPage = 0; + widget.closePanel?.call; + Navigator.pop(context); + }, + ), + const SizedBox(width: 14), + ], ), - Divider( - height: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), Expanded( child: RefreshIndicator( onRefresh: () async { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 10b60c10..05ce2f5e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -67,6 +67,7 @@ class _VideoDetailPageState extends State super.initState(); heroTag = Get.arguments['heroTag']; vdCtr = Get.put(VideoDetailController(), tag: heroTag); + vdCtr.sheetHeight.value = localCache.get('sheetHeight'); videoIntroController = Get.put( VideoIntroController(bvid: Get.parameters['bvid']!), tag: heroTag); @@ -112,6 +113,8 @@ class _VideoDetailPageState extends State _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; + vdCtr.sheetHeight.value = + Get.size.height - videoHeight - statusBarHeight + offset; appbarStream.add(offset); }, ); @@ -271,7 +274,6 @@ class _VideoDetailPageState extends State @override Widget build(BuildContext context) { - // final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; final sizeContext = MediaQuery.sizeOf(context); final _context = MediaQuery.of(context); late double defaultVideoHeight = sizeContext.width * 9 / 16; From ff5101d3d0cf71e9f2dd856f8886d8bd8ab86f11 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 15:46:12 +0800 Subject: [PATCH 022/438] =?UTF-8?q?opt:=20=E5=8F=AF=E6=8B=96=E5=8A=A8?= =?UTF-8?q?=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 05ce2f5e..7a1de4e4 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -47,7 +47,7 @@ class _VideoDetailPageState extends State late BangumiIntroController bangumiIntroController; late String heroTag; - PlayerStatus playerStatus = PlayerStatus.playing; + Rx playerStatus = PlayerStatus.playing.obs; double doubleOffset = 0; final Box localCache = GStrorage.localCache; @@ -122,7 +122,7 @@ class _VideoDetailPageState extends State // 播放器状态监听 void playerListener(PlayerStatus? status) async { - playerStatus = status!; + playerStatus.value = status!; if (status == PlayerStatus.completed) { // 结束播放退出全屏 if (autoExitFullcreen) { @@ -368,6 +368,18 @@ class _VideoDetailPageState extends State child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Obx(() => AnimatedOpacity( + opacity: playerStatus.value != PlayerStatus.playing + ? 1 + : 0, + duration: const Duration(milliseconds: 100), + child: const Icon( + Icons.drag_handle_rounded, + size: 20, + color: Colors.grey, + ), + )), + const SizedBox(width: 8), SizedBox( height: 32, child: TextButton( @@ -406,7 +418,7 @@ class _VideoDetailPageState extends State ), ), ), - const SizedBox(width: 14), + const SizedBox(width: 18), ], ), )), @@ -559,7 +571,7 @@ class _VideoDetailPageState extends State Orientation.landscape || plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height - : playerStatus != PlayerStatus.playing + : playerStatus.value != PlayerStatus.playing ? kToolbarHeight : pinnedHeaderHeight; }, @@ -626,7 +638,7 @@ class _VideoDetailPageState extends State return ScrollAppBar( snapshot.data!.toDouble(), () => continuePlay(), - playerStatus, + playerStatus.value, null, ); }), From b1c801c8c82f79006fb7e4157c9f686572c79f3b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 16:31:28 +0800 Subject: [PATCH 023/438] fix: mediaPage stream error --- lib/pages/media/view.dart | 2 -- lib/utils/main_stream.dart | 26 ++++++++++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 0bb12039..6541680a 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -28,13 +28,11 @@ class _MediaPageState extends State super.initState(); mediaController = Get.put(MediaController()); _futureBuilderFuture = mediaController.queryFavFolder(); - ScrollController scrollController = mediaController.scrollController; mediaController.userLogin.listen((status) { setState(() { _futureBuilderFuture = mediaController.queryFavFolder(); }); }); - handleScrollEvent(scrollController); } @override diff --git a/lib/utils/main_stream.dart b/lib/utils/main_stream.dart index 20c160de..e63248f8 100644 --- a/lib/utils/main_stream.dart +++ b/lib/utils/main_stream.dart @@ -7,11 +7,7 @@ import 'package:get/get.dart'; import '../pages/home/index.dart'; import '../pages/main/index.dart'; -void handleScrollEvent( - ScrollController scrollController, - // StreamController mainStream, - // StreamController? searchBarStream, -) { +void handleScrollEvent(ScrollController scrollController) { StreamController mainStream = Get.find().bottomBarStream; StreamController searchBarStream = @@ -20,15 +16,17 @@ void handleScrollEvent( 'stream-throttler', const Duration(milliseconds: 300), () { - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + try { + final ScrollDirection direction = + scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + } catch (_) {} }, ); } From 894f4fa68da085eeb30353d69ef69d980ba47c94 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 22:57:18 +0800 Subject: [PATCH 024/438] =?UTF-8?q?opt:=20index=E4=B8=BA0=E6=97=B6?= =?UTF-8?q?=E5=90=88=E9=9B=86=E8=B7=B3=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/widgets/bangumi_panel.dart | 14 +++++++++----- .../detail/introduction/widgets/page_panel.dart | 17 +++++++++-------- .../introduction/widgets/season_panel.dart | 2 -- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 988f8d4f..58078d80 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -92,11 +92,15 @@ class _BangumiPanelState extends State { // 在回调函数中获取更新后的状态 final double offset = min((currentIndex * 150) - 75, listViewScrollCtr.position.maxScrollExtent); - listViewScrollCtr.animateTo( - offset, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); + if (currentIndex.value == 0) { + listViewScrollCtr.jumpTo(0); + } else { + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } }); } diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index fc999ba8..83db2d52 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -5,7 +5,6 @@ import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../../../common/pages_bottom_sheet.dart'; import '../../../../../models/common/video_episode_type.dart'; @@ -35,7 +34,6 @@ class _PagesPanelState extends State { final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; final ScrollController listViewScrollCtr = ScrollController(); - final ItemScrollController itemScrollController = ItemScrollController(); late PersistentBottomSheetController? _bottomSheetController; @override @@ -60,7 +58,6 @@ class _PagesPanelState extends State { } void changeFucCall(item, i) async { - print('pages changeFucCall'); widget.changeFuc?.call(item.cid); currentIndex.value = i; _bottomSheetController?.close(); @@ -72,11 +69,15 @@ class _PagesPanelState extends State { // 在回调函数中获取更新后的状态 final double offset = min((currentIndex * 150) - 75, listViewScrollCtr.position.maxScrollExtent); - listViewScrollCtr.animateTo( - offset, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); + if (currentIndex.value == 0) { + listViewScrollCtr.jumpTo(0); + } else { + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } }); } diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index 745c081d..2afefcb4 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -4,7 +4,6 @@ import 'package:pilipala/common/pages_bottom_sheet.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/id_utils.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../../../models/common/video_episode_type.dart'; import '../controller.dart'; @@ -33,7 +32,6 @@ class _SeasonPanelState extends State { late RxInt currentIndex = (-1).obs; final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; - final ItemScrollController itemScrollController = ItemScrollController(); late PersistentBottomSheetController? _bottomSheetController; @override From f1334b550554801074cac46bd8d22318f3164c9d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 23:34:46 +0800 Subject: [PATCH 025/438] =?UTF-8?q?feat:=20=E7=95=AA=E5=89=A7=E9=80=89?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 5 +++++ lib/pages/bangumi/introduction/view.dart | 1 + lib/pages/bangumi/widgets/bangumi_panel.dart | 6 +++++- lib/pages/video/detail/view.dart | 19 ++++++++++++++----- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 63eadacf..2098302d 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -56,6 +56,7 @@ class BangumiIntroController extends GetxController { RxMap followStatus = {}.obs; int _tempThemeValue = -1; var userInfo; + PersistentBottomSheetController? bottomSheetController; @override void onInit() { @@ -320,4 +321,8 @@ class BangumiIntroController extends GetxController { ).buildShowContent(Get.context!), ); } + + hiddenEpisodeBottomSheet() { + bottomSheetController?.close(); + } } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 13db7432..e47db480 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -325,6 +325,7 @@ class _BangumiInfoState extends State { changeFuc: (bvid, cid, aid) => bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), bangumiDetail: bangumiIntroController.bangumiDetail.value, + bangumiIntroController: bangumiIntroController, ) ], ], diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 58078d80..b01f3be7 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -10,6 +10,7 @@ import 'package:pilipala/utils/storage.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../common/pages_bottom_sheet.dart'; import '../../../models/common/video_episode_type.dart'; +import '../introduction/controller.dart'; class BangumiPanel extends StatefulWidget { const BangumiPanel({ @@ -19,6 +20,7 @@ class BangumiPanel extends StatefulWidget { this.sheetHeight, this.changeFuc, this.bangumiDetail, + this.bangumiIntroController, }); final List pages; @@ -26,6 +28,7 @@ class BangumiPanel extends StatefulWidget { final double? sheetHeight; final Function? changeFuc; final BangumiInfoModel? bangumiDetail; + final BangumiIntroController? bangumiIntroController; @override State createState() => _BangumiPanelState(); @@ -136,7 +139,8 @@ class _BangumiPanelState extends State { padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { - _bottomSheetController = EpisodeBottomSheet( + widget.bangumiIntroController?.bottomSheetController = + _bottomSheetController = EpisodeBottomSheet( currentCid: cid, episodes: widget.pages, changeFucCall: changeFucCall, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7a1de4e4..687baaf0 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -180,11 +180,20 @@ class _VideoDetailPageState extends State plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { vdCtr.hiddenReplyReplyPanel(); - videoIntroController.hiddenEpisodeBottomSheet(); - if (videoIntroController.videoDetail.value.ugcSeason != null || - (videoIntroController.videoDetail.value.pages != null && - videoIntroController.videoDetail.value.pages!.length > 1)) { - vdCtr.bottomList.insert(3, BottomControlType.episode); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + if (videoIntroController.videoDetail.value.ugcSeason != null || + (videoIntroController.videoDetail.value.pages != null && + videoIntroController.videoDetail.value.pages!.length > 1)) { + vdCtr.bottomList.insert(3, BottomControlType.episode); + } + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + if (bangumiIntroController.bangumiDetail.value.episodes != null && + bangumiIntroController.bangumiDetail.value.episodes!.length > 1) { + vdCtr.bottomList.insert(3, BottomControlType.episode); + } } } else { if (vdCtr.bottomList.contains(BottomControlType.episode)) { From cc5e3705101f35390d2aad0f11af26824ec56cd1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 00:47:40 +0800 Subject: [PATCH 026/438] =?UTF-8?q?fix:=20=E8=B7=9F=E9=9A=8F=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=B8=BB=E9=A2=98=E6=A8=A1=E5=BC=8F=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 52 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 604441e8..3877685c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -130,27 +130,10 @@ class MyApp extends StatelessWidget { ); } - final SnackBarThemeData snackBarThemeData = SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ); - ThemeData themeData = ThemeData( - // fontFamily: 'HarmonyOS', colorScheme: currentThemeValue == ThemeType.dark ? darkColorScheme : lightColorScheme, - snackBarTheme: snackBarThemeData, - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), ); // 小白条、导航栏沉浸 @@ -171,9 +154,38 @@ class MyApp extends StatelessWidget { // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; return GetMaterialApp( - title: 'PiLiPaLa', - theme: themeData, - darkTheme: themeData, + title: 'PiliPala', + theme: ThemeData( + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: SnackBarThemeData( + actionTextColor: lightColorScheme.primary, + backgroundColor: lightColorScheme.secondaryContainer, + closeIconColor: lightColorScheme.secondary, + contentTextStyle: TextStyle(color: lightColorScheme.secondary), + elevation: 20, + ), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ), + darkTheme: ThemeData( + colorScheme: currentThemeValue == ThemeType.light + ? lightColorScheme + : darkColorScheme, + snackBarTheme: SnackBarThemeData( + actionTextColor: darkColorScheme.primary, + backgroundColor: darkColorScheme.secondaryContainer, + closeIconColor: darkColorScheme.secondary, + contentTextStyle: TextStyle(color: darkColorScheme.secondary), + elevation: 20, + ), + ), localizationsDelegates: const [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, From e7f46883ed94bbae03687432997845f1228946ca Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 17:00:29 +0800 Subject: [PATCH 027/438] =?UTF-8?q?fix:=20=E6=9C=AA=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=92=AD=E6=94=BE=E6=97=B6=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E5=BC=80=E5=85=B3=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 46 +++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 687baaf0..0152a2cb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -404,27 +404,41 @@ class _VideoDetailPageState extends State width: 38, height: 38, child: Obx( - () => IconButton( - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController?.isOpenDanmu.value ?? - false); - }, - icon: !(plPlayerController?.isOpenDanmu.value ?? - false) - ? SvgPicture.asset( + () => !vdCtr.isShowCover.value + ? IconButton( + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController + ?.isOpenDanmu.value ?? + false); + }, + icon: + !(plPlayerController?.isOpenDanmu.value ?? + false) + ? SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .outline, + ) + : SvgPicture.asset( + 'assets/images/video/danmu_open.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .primary, + ), + ) + : IconButton( + icon: SvgPicture.asset( 'assets/images/video/danmu_close.svg', // ignore: deprecated_member_use color: Theme.of(context).colorScheme.outline, - ) - : SvgPicture.asset( - 'assets/images/video/danmu_open.svg', - // ignore: deprecated_member_use - color: - Theme.of(context).colorScheme.primary, ), - ), + onPressed: () {}, + ), ), ), const SizedBox(width: 18), From d943e156d2337b5f1e81cec4bcfbb91f4c7513aa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 17:10:47 +0800 Subject: [PATCH 028/438] =?UTF-8?q?fix:=20=E5=85=B3=E9=97=AD=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=97=B6=E5=88=87=E6=8D=A2=E5=90=88=E9=9B=86=E5=A4=B1?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index d81bda00..6469b5c5 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -58,6 +58,7 @@ class VideoIntroController extends GetxController { String heroTag = ''; late ModelResult modelResult; PersistentBottomSheetController? bottomSheetController; + late bool enableRelatedVideo; @override void onInit() { @@ -74,6 +75,8 @@ class VideoIntroController extends GetxController { queryOnlineTotal(); startTimer(); // 在页面加载时启动定时器 } + enableRelatedVideo = + setting.get(SettingBoxKey.enableRelatedVideo, defaultValue: true); } // 获取视频简介&分p @@ -447,15 +450,18 @@ class VideoIntroController extends GetxController { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); - final ReleatedController releatedCtr = - Get.find(tag: heroTag); + if (enableRelatedVideo) { + final ReleatedController releatedCtr = + Get.find(tag: heroTag); + releatedCtr.bvid = bvid; + releatedCtr.queryRelatedVideo(); + } + videoDetailCtr.bvid = bvid; videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); - releatedCtr.bvid = bvid; - releatedCtr.queryRelatedVideo(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From adba7b33d31a04a6720f4d6c7b671ee97988ed29 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 15 Apr 2024 22:59:35 +0800 Subject: [PATCH 029/438] =?UTF-8?q?fix:=20=E9=A2=84=E8=AE=BE=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E7=94=BB=E8=B4=A8=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/utils.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index a7273f05..987f57c1 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -210,15 +210,18 @@ class Utils { int minDiff = 127; int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 + if (numbers.contains(target)) { + return target; + } // 向下查找 try { for (int number in numbers) { if (number < target) { int diff = target - number; // 计算目标值与当前整数的差值 - if (diff < minDiff) { minDiff = diff; closestNumber = number; + return closestNumber; } } } From 76f0d5fba2f8152cec2f16a68ba4601439872c38 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 00:11:36 +0800 Subject: [PATCH 030/438] fix: zoneController error --- lib/models/common/rank_type.dart | 18 ------------------ lib/pages/rank/controller.dart | 13 ++++++++----- lib/pages/rank/view.dart | 2 +- lib/pages/rank/zone/controller.dart | 15 +++++++++------ 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/lib/models/common/rank_type.dart b/lib/models/common/rank_type.dart index 2ce6d3b5..07be15c2 100644 --- a/lib/models/common/rank_type.dart +++ b/lib/models/common/rank_type.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:pilipala/pages/rank/zone/index.dart'; enum RandType { @@ -74,7 +73,6 @@ List tabsConfig = [ ), 'label': '全站', 'type': RandType.all, - 'ctr': Get.put, 'page': const ZonePage(rid: 0), }, { @@ -84,7 +82,6 @@ List tabsConfig = [ ), 'label': '国创相关', 'type': RandType.creation, - 'ctr': Get.put, 'page': const ZonePage(rid: 168), }, { @@ -94,7 +91,6 @@ List tabsConfig = [ ), 'label': '动画', 'type': RandType.animation, - 'ctr': Get.put, 'page': const ZonePage(rid: 1), }, { @@ -104,7 +100,6 @@ List tabsConfig = [ ), 'label': '音乐', 'type': RandType.music, - 'ctr': Get.put, 'page': const ZonePage(rid: 3), }, { @@ -114,7 +109,6 @@ List tabsConfig = [ ), 'label': '舞蹈', 'type': RandType.dance, - 'ctr': Get.put, 'page': const ZonePage(rid: 129), }, { @@ -124,7 +118,6 @@ List tabsConfig = [ ), 'label': '游戏', 'type': RandType.game, - 'ctr': Get.put, 'page': const ZonePage(rid: 4), }, { @@ -134,7 +127,6 @@ List tabsConfig = [ ), 'label': '知识', 'type': RandType.knowledge, - 'ctr': Get.put, 'page': const ZonePage(rid: 36), }, { @@ -144,7 +136,6 @@ List tabsConfig = [ ), 'label': '科技', 'type': RandType.technology, - 'ctr': Get.put, 'page': const ZonePage(rid: 188), }, { @@ -154,7 +145,6 @@ List tabsConfig = [ ), 'label': '运动', 'type': RandType.sport, - 'ctr': Get.put, 'page': const ZonePage(rid: 234), }, { @@ -164,7 +154,6 @@ List tabsConfig = [ ), 'label': '汽车', 'type': RandType.car, - 'ctr': Get.put, 'page': const ZonePage(rid: 223), }, { @@ -174,7 +163,6 @@ List tabsConfig = [ ), 'label': '生活', 'type': RandType.life, - 'ctr': Get.put, 'page': const ZonePage(rid: 160), }, { @@ -184,7 +172,6 @@ List tabsConfig = [ ), 'label': '美食', 'type': RandType.food, - 'ctr': Get.put, 'page': const ZonePage(rid: 211), }, { @@ -194,7 +181,6 @@ List tabsConfig = [ ), 'label': '动物圈', 'type': RandType.animal, - 'ctr': Get.put, 'page': const ZonePage(rid: 217), }, { @@ -204,7 +190,6 @@ List tabsConfig = [ ), 'label': '鬼畜', 'type': RandType.madness, - 'ctr': Get.put, 'page': const ZonePage(rid: 119), }, { @@ -214,7 +199,6 @@ List tabsConfig = [ ), 'label': '时尚', 'type': RandType.fashion, - 'ctr': Get.put, 'page': const ZonePage(rid: 155), }, { @@ -224,7 +208,6 @@ List tabsConfig = [ ), 'label': '娱乐', 'type': RandType.entertainment, - 'ctr': Get.put, 'page': const ZonePage(rid: 5), }, { @@ -234,7 +217,6 @@ List tabsConfig = [ ), 'label': '影视', 'type': RandType.film, - 'ctr': Get.put, 'page': const ZonePage(rid: 181), } ]; diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 6fe3d424..fa906fc9 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/rank_type.dart'; +import 'package:pilipala/pages/rank/zone/index.dart'; import 'package:pilipala/utils/storage.dart'; class RankController extends GetxController with GetTickerProviderStateMixin { @@ -29,20 +30,22 @@ class RankController extends GetxController with GetTickerProviderStateMixin { void onRefresh() { int index = tabController.index; - var ctr = tabsCtrList[index]; - ctr().onRefresh(); + final ZoneController ctr = tabsCtrList[index]; + ctr.onRefresh(); } void animateToTop() { int index = tabController.index; - var ctr = tabsCtrList[index]; - ctr().animateToTop(); + final ZoneController ctr = tabsCtrList[index]; + ctr.animateToTop(); } void setTabConfig() async { tabs.value = tabsConfig; initialIndex.value = 0; - tabsCtrList = tabs.map((e) => e['ctr']).toList(); + tabsCtrList = tabs + .map((e) => Get.put(ZoneController(), tag: e['rid'].toString())) + .toList(); tabsPageList = tabs.map((e) => e['page']).toList(); tabController = TabController( diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 7b5b4906..4efa2b4e 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -102,7 +102,7 @@ class _RankPageState extends State onTap: (value) { feedBack(); if (_rankController.initialIndex.value == value) { - _rankController.tabsCtrList[value]().animateToTop(); + _rankController.tabsCtrList[value].animateToTop(); } _rankController.initialIndex.value = value; }, diff --git a/lib/pages/rank/zone/controller.dart b/lib/pages/rank/zone/controller.dart index f9f4dc6e..71f27b93 100644 --- a/lib/pages/rank/zone/controller.dart +++ b/lib/pages/rank/zone/controller.dart @@ -42,12 +42,15 @@ class ZoneController extends GetxController { // 返回顶部并刷新 void animateToTop() async { - if (scrollController.offset >= - MediaQuery.of(Get.context!).size.height * 5) { - scrollController.jumpTo(0); - } else { - await scrollController.animateTo(0, - duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); + if (scrollController.hasClients) { + if (scrollController.offset >= + MediaQuery.of(Get.context!).size.height * 5) { + scrollController.jumpTo(0); + } else { + await scrollController.animateTo(0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut); + } } } } From a8cfe9fa1afa946454c70134666e68a34ede3816 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 23:38:03 +0800 Subject: [PATCH 031/438] =?UTF-8?q?fix:=20seekTo=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 74 +++++++++++++++------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index f936526b..1ede7045 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -473,17 +473,17 @@ class PlPlayerController { } // 字幕 - if (dataSource.subFiles != '' && dataSource.subFiles != null) { - await pp.setProperty( - 'sub-files', - UniversalPlatform.isWindows - ? dataSource.subFiles!.replaceAll(';', '\\;') - : dataSource.subFiles!.replaceAll(':', '\\:'), - ); - await pp.setProperty("subs-with-matching-audio", "no"); - await pp.setProperty("sub-forced-only", "yes"); - await pp.setProperty("blend-subtitles", "video"); - } + // if (dataSource.subFiles != '' && dataSource.subFiles != null) { + // await pp.setProperty( + // 'sub-files', + // UniversalPlatform.isWindows + // ? dataSource.subFiles!.replaceAll(';', '\\;') + // : dataSource.subFiles!.replaceAll(':', '\\:'), + // ); + // await pp.setProperty("subs-with-matching-audio", "no"); + // await pp.setProperty("sub-forced-only", "yes"); + // await pp.setProperty("blend-subtitles", "video"); + // } _videoController = _videoController ?? VideoController( @@ -603,7 +603,9 @@ class PlPlayerController { makeHeartBeat(event.inSeconds); }), videoPlayerController!.stream.duration.listen((event) { - duration.value = event; + if (event > Duration.zero) { + duration.value = event; + } }), videoPlayerController!.stream.buffer.listen((event) { _buffered.value = event; @@ -646,32 +648,38 @@ class PlPlayerController { /// 跳转至指定位置 Future seekTo(Duration position, {type = 'seek'}) async { - if (position < Duration.zero) { - position = Duration.zero; - } - _position.value = position; - updatePositionSecond(); - _heartDuration = position.inSeconds; - if (duration.value.inSeconds != 0) { - if (type != 'slider') { - /// 拖动进度条调节时,不等待第一帧,防止抖动 - await _videoPlayerController?.stream.buffer.first; + try { + if (position < Duration.zero) { + position = Duration.zero; } - await _videoPlayerController?.seek(position); - } else { - _timerForSeek?.cancel(); - _timerForSeek ??= - Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { - if (duration.value.inSeconds != 0) { - await _videoPlayerController!.stream.buffer.first; - await _videoPlayerController?.seek(position); - t.cancel(); - _timerForSeek = null; + _position.value = position; + updatePositionSecond(); + _heartDuration = position.inSeconds; + if (duration.value.inSeconds != 0) { + if (type != 'slider') { + await _videoPlayerController?.stream.buffer.first; } - }); + await _videoPlayerController?.seek(position); + } else { + _timerForSeek?.cancel(); + _timerForSeek ??= _startSeekTimer(position); + } + } catch (err) { + print('Error while seeking: $err'); } } + Timer? _startSeekTimer(Duration position) { + return Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { + if (duration.value.inSeconds != 0) { + await _videoPlayerController!.stream.buffer.first; + await _videoPlayerController?.seek(position); + t.cancel(); + _timerForSeek = null; + } + }); + } + /// 设置倍速 Future setPlaybackSpeed(double speed) async { /// TODO _duration.value丢失 From 038801a3b9c114d781d0d25fa542051554b46027 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 23:41:45 +0800 Subject: [PATCH 032/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 1ede7045..be72eccb 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -522,7 +522,22 @@ class PlPlayerController { Duration seekTo = Duration.zero, Duration? duration, }) async { - // 设置倍速 + getVideoFit(); + // if (_looping) { + // await setLooping(_looping); + // } + + /// 跳转播放 + if (seekTo != Duration.zero) { + await this.seekTo(seekTo); + } + + /// 自动播放 + if (_autoPlay) { + await play(duration: duration); + } + + /// 设置倍速 if (videoType.value == 'live') { await setPlaybackSpeed(1.0); } else { @@ -532,20 +547,6 @@ class PlPlayerController { await setPlaybackSpeed(1.0); } } - getVideoFit(); - // if (_looping) { - // await setLooping(_looping); - // } - - // 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } - - // 自动播放 - if (_autoPlay) { - await play(duration: duration); - } } List subscriptions = []; @@ -716,11 +717,10 @@ class PlPlayerController { await seekTo(Duration.zero); } await _videoPlayerController?.play(); - + playerStatus.status.value = PlayerStatus.playing; await getCurrentVolume(); await getCurrentBrightness(); - playerStatus.status.value = PlayerStatus.playing; // screenManager.setOverlays(false); /// 临时fix _duration.value丢失 From 706c13462755ed230c7e55be62bb4b253299302c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 17 Apr 2024 23:53:00 +0800 Subject: [PATCH 033/438] Update beta_ci.yml --- .github/workflows/beta_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index 40f3f042..14b51780 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -206,4 +206,4 @@ jobs: method: sendFile path: Pilipala-Beta/* parse_mode: Markdown - context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}](${{ github.event.head_commit.url }})" + context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}]" From 7fc2765471f74fd0f1fdc7ac89fab506a7d6a9a7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Apr 2024 00:00:19 +0800 Subject: [PATCH 034/438] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 16 ++++++++++++++++ .../video/detail/widgets/header_control.dart | 14 -------------- lib/utils/subtitle.dart | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 11716351..54b52e8e 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -5,6 +5,10 @@ enum SubtitleType { aizh, // 英语(自动生成) aien, + // 中文(简体) + zhHans, + // 英文(美国) + enUS, } extension SubtitleTypeExtension on SubtitleType { @@ -16,6 +20,10 @@ extension SubtitleTypeExtension on SubtitleType { return '中文(自动翻译)'; case SubtitleType.aien: return '英语(自动生成)'; + case SubtitleType.zhHans: + return '中文(简体)'; + case SubtitleType.enUS: + return '英文(美国)'; } } } @@ -29,6 +37,10 @@ extension SubtitleIdExtension on SubtitleType { return 'ai-zh'; case SubtitleType.aien: return 'ai-en'; + case SubtitleType.zhHans: + return 'zh-Hans'; + case SubtitleType.enUS: + return 'en-US'; } } } @@ -42,6 +54,10 @@ extension SubtitleCodeExtension on SubtitleType { return 2; case SubtitleType.aien: return 3; + case SubtitleType.zhHans: + return 4; + case SubtitleType.enUS: + return 5; } } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e6a324cb..e00311c5 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1306,20 +1306,6 @@ class _HeaderControlState extends State { ], /// 字幕 - // SizedBox( - // width: 34, - // height: 34, - // child: IconButton( - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () => showSubtitleDialog(), - // icon: const Icon( - // Icons.closed_caption_off, - // size: 22, - // ), - // ), - // ), ComBtn( icon: const Icon( Icons.closed_caption_off, diff --git a/lib/utils/subtitle.dart b/lib/utils/subtitle.dart index 452be542..1b4088f3 100644 --- a/lib/utils/subtitle.dart +++ b/lib/utils/subtitle.dart @@ -5,8 +5,8 @@ class SubTitleUtils { for (int i = 0; i < jsonData.length; i++) { final item = jsonData[i]; - double from = item['from'] as double; - double to = item['to'] as double; + double from = double.parse(item['from'].toString()); + double to = double.parse(item['to'].toString()); int sid = (item['sid'] ?? 0) as int; String content = item['content'] as String; From 3e105804d77bcbe9378907383d4d587d204cfe04 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Apr 2024 23:33:55 +0800 Subject: [PATCH 035/438] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E9=A1=B5=E9=9D=A2=E9=94=AE=E7=9B=98=E6=94=B6=E8=B5=B7?= =?UTF-8?q?=E5=90=8E=E6=97=A0=E6=B3=95=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 367 ++++++++++++++-------------- 1 file changed, 177 insertions(+), 190 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 9da085f4..a620b5ed 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -16,6 +16,7 @@ import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import '../../../models/video/reply/item.dart'; import '../widgets/dynamic_panel.dart'; class DynamicDetailPage extends StatefulWidget { @@ -210,208 +211,194 @@ class _DynamicDetailPageState extends State onRefresh: () async { await _dynamicDetailController.queryReplyList(); }, - child: Stack( - children: [ - CustomScrollView( - controller: scrollController, - slivers: [ - if (action != 'comment') - SliverToBoxAdapter( - child: DynamicPanel( - item: _dynamicDetailController.item, - source: 'detail', + child: CustomScrollView( + controller: scrollController, + slivers: [ + if (action != 'comment') + SliverToBoxAdapter( + child: DynamicPanel( + item: _dynamicDetailController.item, + source: 'detail', + ), + ), + SliverPersistentHeader( + delegate: _MySliverPersistentHeaderDelegate( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + width: 0.6, + color: Theme.of(context).dividerColor.withOpacity(0.05), + ), ), ), - SliverPersistentHeader( - delegate: _MySliverPersistentHeaderDelegate( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border( - top: BorderSide( - width: 0.6, - color: Theme.of(context) - .dividerColor - .withOpacity(0.05), + height: 45, + padding: const EdgeInsets.only(left: 12, right: 6), + child: Row( + children: [ + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Text( + '${_dynamicDetailController.acount.value}', + key: ValueKey( + _dynamicDetailController.acount.value), ), ), ), - height: 45, - padding: const EdgeInsets.only(left: 12, right: 6), - child: Row( - children: [ - Obx( - () => AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - '${_dynamicDetailController.acount.value}', - key: ValueKey( - _dynamicDetailController.acount.value), - ), - ), - ), - const Text('条回复'), - const Spacer(), - SizedBox( - height: 35, - child: TextButton.icon( - onPressed: () => - _dynamicDetailController.queryBySort(), - icon: const Icon(Icons.sort, size: 16), - label: Obx(() => Text( - _dynamicDetailController - .sortTypeLabel.value, - style: const TextStyle(fontSize: 13), - )), - ), - ) - ], - ), - ), + const Text('条回复'), + const Spacer(), + SizedBox( + height: 35, + child: TextButton.icon( + onPressed: () => + _dynamicDetailController.queryBySort(), + icon: const Icon(Icons.sort, size: 16), + label: Obx(() => Text( + _dynamicDetailController.sortTypeLabel.value, + style: const TextStyle(fontSize: 13), + )), + ), + ) + ], ), - pinned: true, - ), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (snapshot.data['status']) { - // 请求成功 - return Obx( - () => _dynamicDetailController.replyList.isEmpty && - _dynamicDetailController.isLoadingMore - ? SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ) - : SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == - _dynamicDetailController - .replyList.length) { - return Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - height: MediaQuery.of(context) - .padding - .bottom + - 100, - child: Center( - child: Obx( - () => Text( - _dynamicDetailController - .noMore.value, - style: TextStyle( - fontSize: 12, - color: Theme.of(context) - .colorScheme - .outline, - ), - ), - ), - ), - ); - } else { - return ReplyItem( - replyItem: _dynamicDetailController - .replyList[index], - showReplyRow: true, - replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), - replyType: - ReplyType.values[replyType], - addReply: (replyItem) { - _dynamicDetailController - .replyList[index].replies! - .add(replyItem); - }, - ); - } - }, - childCount: _dynamicDetailController - .replyList.length + - 1, - ), - ), - ); - } else { - // 请求错误 - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ); - } - }, - ) - ], - ), - Positioned( - bottom: MediaQuery.of(context).padding.bottom + 14, - right: 14, - child: SlideTransition( - position: Tween( - begin: const Offset(0, 2), - end: const Offset(0, 0), - ).animate(CurvedAnimation( - parent: fabAnimationCtr, - curve: Curves.easeInOut, - )), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.values[replyType], - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - { - _dynamicDetailController.replyList - .add(value['data']), - _dynamicDetailController.acount.value++ - } - }, - ); - }, - tooltip: '评论动态', - child: const Icon(Icons.reply), ), ), + pinned: true, ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (snapshot.data['status']) { + RxList replyList = + _dynamicDetailController.replyList; + // 请求成功 + return Obx( + () => replyList.isEmpty && + _dynamicDetailController.isLoadingMore + ? SliverList( + delegate: + SliverChildBuilderDelegate((context, index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ) + : SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == replyList.length) { + return Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context) + .padding + .bottom), + height: MediaQuery.of(context) + .padding + .bottom + + 100, + child: Center( + child: Obx( + () => Text( + _dynamicDetailController + .noMore.value, + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .colorScheme + .outline, + ), + ), + ), + ), + ); + } else { + return ReplyItem( + replyItem: replyList[index], + showReplyRow: true, + replyLevel: '1', + replyReply: (replyItem) => + replyReply(replyItem), + replyType: ReplyType.values[replyType], + addReply: (replyItem) { + replyList[index] + .replies! + .add(replyItem); + }, + ); + } + }, + childCount: replyList.length + 1, + ), + ), + ); + } else { + // 请求错误 + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ); + } + }, + ) ], ), ), + floatingActionButton: SlideTransition( + position: Tween( + begin: const Offset(0, 2), + end: const Offset(0, 0), + ).animate( + CurvedAnimation( + parent: fabAnimationCtr, + curve: Curves.easeInOut, + ), + ), + child: FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.values[replyType], + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _dynamicDetailController.replyList.add(value['data']), + _dynamicDetailController.acount.value++ + } + }, + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), + ), ); } } From 350e34902336c3541c2544e410fa498e51f976dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 19 Apr 2024 00:01:09 +0800 Subject: [PATCH 036/438] =?UTF-8?q?feat:=20ai=E6=80=BB=E7=BB=93=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/ai_detail.dart | 109 ++++++++---------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index fb280d91..882a9a8b 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -49,7 +49,7 @@ class AiDetail extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - Text( + SelectableText( modelResult!.summary!, style: const TextStyle( fontSize: 15, @@ -60,13 +60,15 @@ class AiDetail extends StatelessWidget { const SizedBox(height: 20), ListView.builder( shrinkWrap: true, - itemCount: modelResult!.outline!.length, physics: const NeverScrollableScrollPhysics(), + itemCount: modelResult!.outline!.length, itemBuilder: (context, index) { + final outline = modelResult!.outline![index]; return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - modelResult!.outline![index].title!, + SelectableText( + outline.title!, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -77,76 +79,59 @@ class AiDetail extends StatelessWidget { ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: modelResult! - .outline![index].partOutline!.length, + itemCount: outline.partOutline!.length, itemBuilder: (context, i) { + final part = outline.partOutline![i]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Wrap( - children: [ - RichText( - text: TextSpan( - style: TextStyle( - fontSize: 13, - color: Theme.of(context) - .colorScheme - .onBackground, - height: 1.5, + GestureDetector( + onTap: () { + try { + final controller = + Get.find( + tag: Get.arguments['heroTag'], + ); + controller.plPlayerController.seekTo( + Duration( + seconds: Utils.duration( + Utils.tampToSeektime( + part.timestamp!), + ).toInt(), ), - children: [ - TextSpan( - text: Utils.tampToSeektime( - modelResult! - .outline![index] - .partOutline![i] - .timestamp!), - style: TextStyle( - color: Theme.of(context) - .colorScheme - .primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 跳转到指定位置 - try { - Get.find( - tag: Get.arguments[ - 'heroTag']) - .plPlayerController - .seekTo( - Duration( - seconds: - Utils.duration( - Utils.tampToSeektime(modelResult! - .outline![ - index] - .partOutline![ - i] - .timestamp!) - .toString(), - ), - ), - ); - } catch (_) {} - }, - ), - const TextSpan(text: ' '), - TextSpan( - text: modelResult! - .outline![index] - .partOutline![i] - .content!), - ], + ); + } catch (_) {} + }, + child: SelectableText.rich( + TextSpan( + style: TextStyle( + fontSize: 13, + color: Theme.of(context) + .colorScheme + .onBackground, + height: 1.5, ), + children: [ + TextSpan( + text: Utils.tampToSeektime( + part.timestamp!), + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary, + ), + ), + const TextSpan(text: ' '), + TextSpan(text: part.content!), + ], ), - ], + ), ), + const SizedBox(height: 20), ], ); }, ), - const SizedBox(height: 20), ], ); }, From 5f0bb44bf156708cafef644440cb4c6dd6b202c3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 19 Apr 2024 23:43:23 +0800 Subject: [PATCH 037/438] =?UTF-8?q?mod:=20=E8=8E=B7=E5=8F=96=E6=94=B6?= =?UTF-8?q?=E8=97=8F=E5=A4=B9pageSize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index a5f94525..6ff0afc8 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -13,7 +13,7 @@ class FavController extends GetxController { Box userInfoCache = GStrorage.userInfo; UserInfoData? userInfo; int currentPage = 1; - int pageSize = 10; + int pageSize = 60; RxBool hasMore = true.obs; Future queryFavFolder({type = 'init'}) async { From fcd7781789a1f53fc7ab7f80af85c60675103a68 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 00:55:18 +0800 Subject: [PATCH 038/438] =?UTF-8?q?feat:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 +++ lib/http/user.dart | 17 ++++++++++++ lib/pages/fav/controller.dart | 13 +++++++++- lib/pages/fav/view.dart | 5 ++-- lib/pages/fav/widgets/item.dart | 18 +++++++------ lib/pages/fav_detail/controller.dart | 39 ++++++++++++++++++++++++++++ lib/pages/fav_detail/view.dart | 18 +++++++++---- 7 files changed, 96 insertions(+), 17 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index b6975c4b..ad314acf 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -511,4 +511,7 @@ class Api { /// 取消订阅 static const String cancelSub = '/x/v3/fav/season/unfav'; + + /// 删除收藏夹 + static const String delFavFolder = '/x/v3/fav/folder/del'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index fea0a22e..dfdf187e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -395,4 +395,21 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 删除文件夹 + static Future delFavFolder({required int mediaIds}) async { + var res = await Request().post( + Api.delFavFolder, + queryParameters: { + 'media_ids': mediaIds, + 'platform': 'web', + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 6ff0afc8..2307d303 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -10,6 +10,7 @@ import 'package:pilipala/utils/storage.dart'; class FavController extends GetxController { final ScrollController scrollController = ScrollController(); Rx favFolderData = FavFolderData().obs; + RxList favFolderList = [].obs; Box userInfoCache = GStrorage.userInfo; UserInfoData? userInfo; int currentPage = 1; @@ -32,9 +33,10 @@ class FavController extends GetxController { if (res['status']) { if (type == 'init') { favFolderData.value = res['data']; + favFolderList.value = res['data'].list; } else { if (res['data'].list.isNotEmpty) { - favFolderData.value.list!.addAll(res['data'].list); + favFolderList.addAll(res['data'].list); favFolderData.update((val) {}); } } @@ -49,4 +51,13 @@ class FavController extends GetxController { Future onLoad() async { queryFavFolder(type: 'onload'); } + + removeFavFolder({required int mediaIds}) async { + for (var i in favFolderList) { + if (i.id == mediaIds) { + favFolderList.remove(i); + break; + } + } + } } diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index b980914a..424a885d 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -62,11 +62,10 @@ class _FavPageState extends State { return Obx( () => ListView.builder( controller: scrollController, - itemCount: _favController.favFolderData.value.list!.length, + itemCount: _favController.favFolderList.length, itemBuilder: (context, index) { return FavItem( - favFolderItem: - _favController.favFolderData.value.list![index]); + favFolderItem: _favController.favFolderList[index]); }, ), ); diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 08730d7b..3c44ec9d 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -13,14 +13,16 @@ class FavItem extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(favFolderItem.fid); return InkWell( - onTap: () => Get.toNamed( - '/favDetail', - arguments: favFolderItem, - parameters: { - 'heroTag': heroTag, - 'mediaId': favFolderItem.id.toString(), - }, - ), + onTap: () async { + Get.toNamed( + '/favDetail', + arguments: favFolderItem, + parameters: { + 'heroTag': heroTag, + 'mediaId': favFolderItem.id.toString(), + }, + ); + }, child: Padding( padding: const EdgeInsets.fromLTRB(12, 7, 12, 7), child: LayoutBuilder( diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 55d5b884..7af398e8 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -1,9 +1,11 @@ +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/user/fav_detail.dart'; import 'package:pilipala/models/user/fav_folder.dart'; +import 'package:pilipala/pages/fav/index.dart'; class FavDetailController extends GetxController { FavFolderItemData? item; @@ -74,4 +76,41 @@ class FavDetailController extends GetxController { onLoad() { queryUserFavFolderDetail(type: 'onLoad'); } + + onDelFavFolder() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('确定删除这个收藏夹吗?'), + actions: [ + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + }, + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await UserHttp.delFavFolder(mediaIds: mediaId!); + SmartDialog.dismiss(); + SmartDialog.showToast(res['status'] ? '操作成功' : res['msg']); + if (res['status']) { + FavController favController = Get.find(); + await favController.removeFavFolder(mediaIds: mediaId!); + Get.back(); + } + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 74faa829..5f2d92af 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -100,11 +100,19 @@ class _FavDetailPageState extends State { Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'), icon: const Icon(Icons.search_outlined), ), - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.more_vert), - // ), - const SizedBox(width: 6), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined), + position: PopupMenuPosition.under, + onSelected: (String type) {}, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + onTap: () => _favDetailController.onDelFavFolder(), + value: 'pause', + child: const Text('删除收藏夹'), + ), + ], + ), + const SizedBox(width: 14), ], flexibleSpace: FlexibleSpaceBar( background: Container( From 03ce69b140d33e0399114c591adbc0bb705a0ffc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 22:40:54 +0800 Subject: [PATCH 039/438] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E8=BD=AC?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 6 + lib/http/dynamics.dart | 79 ++++++ lib/pages/dynamics/widgets/action_panel.dart | 241 ++++++++++++++++++- 3 files changed, 319 insertions(+), 7 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index b6975c4b..e2e973f2 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -511,4 +511,10 @@ class Api { /// 取消订阅 static const String cancelSub = '/x/v3/fav/season/unfav'; + + /// 动态转发 + static const String dynamicForwardUrl = '/x/dynamic/feed/create/submit_check'; + + /// 创建动态 + static const String dynamicCreate = '/x/dynamic/feed/create/dyn'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index d62de12f..f0740d36 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import '../models/dynamics/result.dart'; import '../models/dynamics/up.dart'; import 'index.dart'; @@ -117,4 +118,82 @@ class DynamicsHttp { }; } } + + static Future dynamicForward() async { + var res = await Request().post( + Api.dynamicForwardUrl, + queryParameters: { + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, + data: { + 'attach_card': null, + 'scene': 4, + 'content': { + 'conetents': [ + {'raw_text': "2", 'type': 1, 'biz_id': ""} + ] + } + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } + + static Future dynamicCreate({ + required String dynIdStr, + required int mid, + String? rawText, + }) async { + DateTime now = DateTime.now(); + int timestamp = now.millisecondsSinceEpoch ~/ 1000; + Random random = Random(); + int randomNumber = random.nextInt(9000) + 1000; + String uploadId = + mid.toString() + timestamp.toString() + randomNumber.toString(); + var res = await Request().post(Api.dynamicCreate, queryParameters: { + 'platform': 'web', + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, data: { + 'dyn_req': { + 'content': { + 'contents': [ + {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} + ] + }, + 'scene': 4, + 'attach_card': null, + 'upload_id': uploadId, + 'meta': { + 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} + } + }, + 'web_repost_src': {'dyn_id_str': dynIdStr} + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0ca09b8c..0e67b0a7 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -3,10 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:status_bar_control/status_bar_control.dart'; +import 'rich_node_panel.dart'; class ActionPanel extends StatefulWidget { const ActionPanel({ @@ -20,21 +23,37 @@ class ActionPanel extends StatefulWidget { State createState() => _ActionPanelState(); } -class _ActionPanelState extends State { +class _ActionPanelState extends State + with TickerProviderStateMixin { final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; bool isProcessing = false; + RxDouble height = (310.0).obs; + RxBool isExpand = false.obs; + late double statusHeight; + TextEditingController _inputController = TextEditingController(); + FocusNode myFocusNode = FocusNode(); + String _inputText = ''; + void Function()? handleState(Future Function() action) { - return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); - }; + return isProcessing + ? null + : () async { + isProcessing = true; + await action(); + isProcessing = false; + }; } + @override void initState() { super.initState(); stat = widget.item!.modules.moduleStat; + onInit(); + } + + onInit() async { + statusHeight = await StatusBarControl.getHeight; } // 动态点赞 @@ -67,6 +86,214 @@ class _ActionPanelState extends State { } } + // 转发动态预览 + Widget dynamicPreview() { + return Padding( + padding: const EdgeInsets.fromLTRB(12, 0, 14, 12), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${widget.item.modules.moduleAuthor.name}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 6), + Row( + children: [ + NetworkImgLayer( + src: widget.item.modules.moduleAuthor.face, + width: 34, + height: 34, + type: 'emote', + ), + const SizedBox(width: 10), + Expanded( + child: Text.rich( + style: const TextStyle(height: 0), + richNode(widget.item, context), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + // Text(data) + ], + ) + ], + )), + ), + ); + } + + // 动态转发 + void forwardHandler() async { + showModalBottomSheet( + context: context, + enableDrag: false, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return Obx( + () => AnimatedContainer( + duration: Durations.medium1, + onEnd: () async { + if (isExpand.value) { + await Future.delayed(const Duration(milliseconds: 120)); + myFocusNode.requestFocus(); + } + }, + height: height.value, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedContainer( + duration: Durations.medium1, + height: isExpand.value ? statusHeight : 0, + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 10, 12, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedSwitcher( + duration: Durations.medium1, + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: isExpand.value + ? IconButton( + onPressed: () => togglePanelState(false), + icon: const Icon(Icons.close)) + : const Text( + '转发动态', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + AnimatedSwitcher( + duration: Durations.medium1, + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: isExpand.value + ? FilledButton( + onPressed: () => dynamicForward('forward'), + child: const Text('转发'), + ) + : TextButton( + onPressed: () {}, + child: const Text('立即转发'), + ), + ), + ], + ), + ), + if (!isExpand.value) ...[ + GestureDetector( + onTap: () => togglePanelState(true), + behavior: HitTestBehavior.translucent, + child: Container( + width: double.infinity, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(16, 0, 10, 15), + child: Text( + '说点什么吧', + textAlign: TextAlign.start, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + ), + ] else ...[ + Padding( + padding: const EdgeInsets.fromLTRB(16, 10, 16, 0), + child: TextField( + maxLines: 5, + focusNode: myFocusNode, + controller: _inputController, + onChanged: (value) { + setState(() { + _inputText = value; + }); + }, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '说点什么吧', + ), + ), + ), + ], + dynamicPreview(), + if (!isExpand.value) ...[ + const Divider(thickness: 0.1, height: 1), + ListTile( + onTap: () => Get.back(), + minLeadingWidth: 0, + dense: true, + title: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20, + ) + ] + ], + ), + ), + ); + }, + ); + } + + togglePanelState(status) { + if (!status) { + Get.back(); + height.value = 310; + _inputText = ''; + _inputController.clear(); + } else { + height.value = Get.size.height; + } + isExpand.value = !(isExpand.value); + } + + dynamicForward(String type) async { + String dynamicId = widget.item.idStr!; + var res = await DynamicsHttp.dynamicCreate( + dynIdStr: dynamicId, + mid: _dynamicsController.userInfo.mid, + rawText: _inputText, + ); + if (res['status']) { + SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功'); + togglePanelState(false); + } + } + + @override + void dispose() { + myFocusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { var color = Theme.of(context).colorScheme.outline; @@ -77,7 +304,7 @@ class _ActionPanelState extends State { Expanded( flex: 1, child: TextButton.icon( - onPressed: () {}, + onPressed: forwardHandler, icon: const Icon( FontAwesomeIcons.shareFromSquare, size: 16, From bc29f691333bb9f84c897a8168a9d09669ad4266 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 23:32:28 +0800 Subject: [PATCH 040/438] opt: streamController close --- lib/pages/dynamics/detail/view.dart | 1 + lib/pages/fav_detail/view.dart | 1 + lib/pages/home/controller.dart | 6 ++++++ lib/pages/main/controller.dart | 6 ++++++ lib/pages/member/view.dart | 1 + lib/pages/rank/controller.dart | 6 ++++++ lib/pages/subscription_detail/view.dart | 1 + 7 files changed, 22 insertions(+) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index a620b5ed..c6ec682a 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -183,6 +183,7 @@ class _DynamicDetailPageState extends State scrollController.removeListener(() {}); fabAnimationCtr.dispose(); scrollController.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 74faa829..993ff63b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -53,6 +53,7 @@ class _FavDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index ca70e1c4..f197fdfa 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -114,4 +114,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { defaultSearch.value = res.data['data']['name']; } } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index c2e5c322..a77d9304 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -101,4 +101,10 @@ class MainController extends GetxController { selectedIndex = defaultIndex != -1 ? defaultIndex : 0; pages = navigationBars.map((e) => e['page']).toList(); } + + @override + void onClose() { + bottomBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 015750db..b6648647 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -54,6 +54,7 @@ class _MemberPageState extends State @override void dispose() { _extendNestCtr.removeListener(() {}); + appbarStream.close(); super.dispose(); } diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index fa906fc9..64395ec7 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -54,4 +54,10 @@ class RankController extends GetxController with GetTickerProviderStateMixin { vsync: this, ); } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 2c219e58..63352429 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -53,6 +53,7 @@ class _SubDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } From 22d8cf12a0db7d7002abbddbdffe18d7e5d28e5f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 15:52:16 +0800 Subject: [PATCH 041/438] opt: forward panel --- lib/pages/dynamics/widgets/action_panel.dart | 264 ++++++++++++------ lib/pages/dynamics/widgets/dynamic_panel.dart | 9 +- lib/pages/member_dynamics/view.dart | 4 +- 3 files changed, 181 insertions(+), 96 deletions(-) diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0e67b0a7..e94757ab 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -14,10 +14,10 @@ import 'rich_node_panel.dart'; class ActionPanel extends StatefulWidget { const ActionPanel({ super.key, - this.item, + required this.item, }); // ignore: prefer_typing_uninitialized_variables - final item; + final DynamicItemModel item; @override State createState() => _ActionPanelState(); @@ -28,7 +28,8 @@ class _ActionPanelState extends State final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; bool isProcessing = false; - RxDouble height = (310.0).obs; + double defaultHeight = 260; + RxDouble height = 0.0.obs; RxBool isExpand = false.obs; late double statusHeight; TextEditingController _inputController = TextEditingController(); @@ -48,7 +49,7 @@ class _ActionPanelState extends State @override void initState() { super.initState(); - stat = widget.item!.modules.moduleStat; + stat = widget.item.modules!.moduleStat!; onInit(); } @@ -62,7 +63,7 @@ class _ActionPanelState extends State var item = widget.item!; String dynamicId = item.idStr!; // 1 已点赞 2 不喜欢 0 未操作 - Like like = item.modules.moduleStat.like; + Like like = item.modules!.moduleStat!.like!; int count = like.count == '点赞' ? 0 : int.parse(like.count ?? '0'); bool status = like.status!; int up = status ? 2 : 1; @@ -70,15 +71,15 @@ class _ActionPanelState extends State if (res['status']) { SmartDialog.showToast(!status ? '点赞成功' : '取消赞'); if (up == 1) { - item.modules.moduleStat.like.count = (count + 1).toString(); - item.modules.moduleStat.like.status = true; + item.modules!.moduleStat!.like!.count = (count + 1).toString(); + item.modules!.moduleStat!.like!.status = true; } else { if (count == 1) { - item.modules.moduleStat.like.count = '点赞'; + item.modules!.moduleStat!.like!.count = '点赞'; } else { - item.modules.moduleStat.like.count = (count - 1).toString(); + item.modules!.moduleStat!.like!.count = (count - 1).toString(); } - item.modules.moduleStat.like.status = false; + item.modules!.moduleStat!.like!.status = false; } setState(() {}); } else { @@ -88,50 +89,134 @@ class _ActionPanelState extends State // 转发动态预览 Widget dynamicPreview() { - return Padding( - padding: const EdgeInsets.fromLTRB(12, 0, 14, 12), - child: Container( - width: double.infinity, - decoration: BoxDecoration( - color: - Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), - borderRadius: BorderRadius.circular(12), + ItemModulesModel? modules = widget.item.modules; + final String type = widget.item.type!; + String? cover = modules?.moduleAuthor?.face; + switch (type) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 转发的动态 + case 'DYNAMIC_TYPE_FORWARD': + String forwardType = widget.item.orig!.type!; + switch (forwardType) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = modules?.moduleDynamic?.major?.pgc?.cover; + + default: + cover = ''; + } + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧查看 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = ''; + + default: + cover = ''; + } + return Container( + width: double.infinity, + height: 95, + margin: const EdgeInsets.fromLTRB(12, 0, 12, 14), + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), + borderRadius: BorderRadius.circular(6), + border: Border( + left: BorderSide( + width: 4, + color: Theme.of(context).colorScheme.primary.withOpacity(0.8)), ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${widget.item.modules!.moduleAuthor!.name}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 8), + Row( children: [ - Text( - '@${widget.item.modules.moduleAuthor.name}', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, + NetworkImgLayer( + src: cover ?? '', + width: 34, + height: 34, + type: 'emote', + ), + const SizedBox(width: 10), + Expanded( + child: Text.rich( + style: const TextStyle(height: 0), + richNode(widget.item, context), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), ), - const SizedBox(height: 6), - Row( - children: [ - NetworkImgLayer( - src: widget.item.modules.moduleAuthor.face, - width: 34, - height: 34, - type: 'emote', - ), - const SizedBox(width: 10), - Expanded( - child: Text.rich( - style: const TextStyle(height: 0), - richNode(widget.item, context), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - // Text(data) - ], - ) + // Text(data) ], - )), + ) + ], + ), ), ); } @@ -149,56 +234,54 @@ class _ActionPanelState extends State duration: Durations.medium1, onEnd: () async { if (isExpand.value) { - await Future.delayed(const Duration(milliseconds: 120)); + await Future.delayed(const Duration(milliseconds: 80)); myFocusNode.requestFocus(); } }, - height: height.value, + height: height.value + MediaQuery.of(context).padding.bottom, child: Column( - mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: Durations.medium1, height: isExpand.value ? statusHeight : 0, ), Padding( - padding: const EdgeInsets.fromLTRB(16, 10, 12, 0), + padding: EdgeInsets.fromLTRB( + isExpand.value ? 10 : 16, + 10, + isExpand.value ? 14 : 12, + 0, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AnimatedSwitcher( - duration: Durations.medium1, - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: isExpand.value - ? IconButton( - onPressed: () => togglePanelState(false), - icon: const Icon(Icons.close)) - : const Text( - '转发动态', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - AnimatedSwitcher( - duration: Durations.medium1, - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: isExpand.value - ? FilledButton( - onPressed: () => dynamicForward('forward'), - child: const Text('转发'), - ) - : TextButton( - onPressed: () {}, - child: const Text('立即转发'), - ), - ), + if (isExpand.value) ...[ + IconButton( + onPressed: () => togglePanelState(false), + icon: const Icon(Icons.close), + ), + Text( + '转发动态', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ) + ] else ...[ + const Text( + '转发动态', + style: TextStyle(fontWeight: FontWeight.bold), + ) + ], + isExpand.value + ? FilledButton( + onPressed: () => dynamicForward('forward'), + child: const Text('转发'), + ) + : TextButton( + onPressed: () {}, + child: const Text('立即转发'), + ) ], ), ), @@ -209,7 +292,7 @@ class _ActionPanelState extends State child: Container( width: double.infinity, alignment: Alignment.centerLeft, - padding: const EdgeInsets.fromLTRB(16, 0, 10, 15), + padding: const EdgeInsets.fromLTRB(16, 0, 10, 14), child: Text( '说点什么吧', textAlign: TextAlign.start, @@ -251,9 +334,6 @@ class _ActionPanelState extends State textAlign: TextAlign.center, ), ), - SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, - ) ] ], ), @@ -266,7 +346,7 @@ class _ActionPanelState extends State togglePanelState(status) { if (!status) { Get.back(); - height.value = 310; + height.value = defaultHeight; _inputText = ''; _inputController.clear(); } else { @@ -298,6 +378,8 @@ class _ActionPanelState extends State Widget build(BuildContext context) { var color = Theme.of(context).colorScheme.outline; var primary = Theme.of(context).colorScheme.primary; + height.value = defaultHeight; + print('height.value: ${height.value}'); return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index c85cad45..d273a1a6 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,15 +1,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; +import '../../../models/dynamics/result.dart'; import 'action_panel.dart'; import 'author_panel.dart'; import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - final dynamic item; + final DynamicItemModel item; final String? source; - DynamicPanel({this.item, this.source, Key? key}) : super(key: key); + DynamicPanel({required this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); @override @@ -41,8 +42,8 @@ class DynamicPanel extends StatelessWidget { padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), child: AuthorPanel(item: item), ), - if (item!.modules!.moduleDynamic!.desc != null || - item!.modules!.moduleDynamic!.major != null) + if (item.modules!.moduleDynamic!.desc != null || + item.modules!.moduleDynamic!.major != null) Content(item: item, source: source), forWard(item, context, _dynamicsController, source), const SizedBox(height: 2), diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index 68aa72d7..2e093bcc 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/utils/utils.dart'; import '../../common/widgets/http_error.dart'; +import '../../models/dynamics/result.dart'; import '../dynamics/widgets/dynamic_panel.dart'; class MemberDynamicsPage extends StatefulWidget { @@ -66,7 +67,8 @@ class _MemberDynamicsPageState extends State { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data != null) { Map data = snapshot.data as Map; - List list = _memberDynamicController.dynamicsList; + RxList list = + _memberDynamicController.dynamicsList; if (data['status']) { return Obx( () => list.isNotEmpty From 9dc7256a005247cb24b112d4850bc70b19ec9734 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 16:47:38 +0800 Subject: [PATCH 042/438] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E7=95=AA?= =?UTF-8?q?=E5=89=A7=E8=AF=84=E8=AE=BA=E6=97=B6=E8=BD=AC=E5=8F=91=E5=88=B0?= =?UTF-8?q?=E5=8A=A8=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/dynamics.dart | 22 +++++++-- lib/pages/dynamics/widgets/action_panel.dart | 1 + lib/pages/video/detail/reply_new/view.dart | 50 ++++++++++++++++++-- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index f0740d36..63dea4ff 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -152,16 +152,28 @@ class DynamicsHttp { } static Future dynamicCreate({ - required String dynIdStr, required int mid, + required int scene, + int? oid, + String? dynIdStr, String? rawText, }) async { DateTime now = DateTime.now(); int timestamp = now.millisecondsSinceEpoch ~/ 1000; Random random = Random(); int randomNumber = random.nextInt(9000) + 1000; - String uploadId = - mid.toString() + timestamp.toString() + randomNumber.toString(); + String uploadId = '${mid}_${timestamp}_$randomNumber'; + + Map webRepostSrc = { + 'dyn_id_str': dynIdStr ?? '', + }; + + /// 投稿转发 + if (scene == 5) { + webRepostSrc = { + 'revs_id': {'dyn_type': 8, 'rid': oid} + }; + } var res = await Request().post(Api.dynamicCreate, queryParameters: { 'platform': 'web', 'csrf': await Request.getCsrf(), @@ -174,14 +186,14 @@ class DynamicsHttp { {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} ] }, - 'scene': 4, + 'scene': scene, 'attach_card': null, 'upload_id': uploadId, 'meta': { 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} } }, - 'web_repost_src': {'dyn_id_str': dynIdStr} + 'web_repost_src': webRepostSrc }); if (res.data['code'] == 0) { return { diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index e94757ab..51ef3952 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -361,6 +361,7 @@ class _ActionPanelState extends State dynIdStr: dynamicId, mid: _dynamicsController.userInfo.mid, rawText: _inputText, + scene: 4, ); if (res['status']) { SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功'); diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a94b6071..029e015a 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/emote.dart'; @@ -40,6 +41,8 @@ class _VideoReplyNewDialogState extends State double keyboardHeight = 0.0; // 键盘高度 final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 String toolbarType = 'input'; + RxBool isForward = false.obs; + RxBool showForward = false.obs; @override void initState() { @@ -52,6 +55,10 @@ class _VideoReplyNewDialogState extends State _autoFocus(); // 监听聚焦状态 _focuslistener(); + final String routePath = Get.currentRoute; + if (routePath.startsWith('/video')) { + showForward.value = true; + } } _autoFocus() async { @@ -88,6 +95,16 @@ class _VideoReplyNewDialogState extends State Get.back(result: { 'data': ReplyItemModel.fromJson(result['data']['reply'], ''), }); + + /// 投稿、番剧页面 + if (isForward.value) { + await DynamicsHttp.dynamicCreate( + mid: 0, + rawText: message, + oid: widget.oid!, + scene: 5, + ); + } } else { SmartDialog.showToast(result['msg']); } @@ -145,7 +162,6 @@ class _VideoReplyNewDialogState extends State double _keyboardHeight = EdgeInsets.fromViewPadding( View.of(context).viewInsets, View.of(context).devicePixelRatio) .bottom; - print('_keyboardHeight: $_keyboardHeight'); return Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( @@ -225,9 +241,37 @@ class _VideoReplyNewDialogState extends State toolbarType: toolbarType, selected: toolbarType == 'emote', ), + const SizedBox(width: 6), + Obx( + () => showForward.value + ? TextButton.icon( + onPressed: () { + isForward.value = !isForward.value; + }, + icon: Icon( + isForward.value + ? Icons.check_box + : Icons.check_box_outline_blank, + size: 22), + label: const Text('转发到动态'), + style: ButtonStyle( + foregroundColor: MaterialStateProperty.all( + isForward.value + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), + ) + : const SizedBox(), + ), const Spacer(), - TextButton( - onPressed: () => submitReplyAdd(), child: const Text('发送')) + SizedBox( + height: 36, + child: FilledButton( + onPressed: () => submitReplyAdd(), + child: const Text('发送'), + ), + ), ], ), ), From c42ce2f1f96c797237b4fca38690d5fae2053810 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 17:38:07 +0800 Subject: [PATCH 043/438] =?UTF-8?q?mod:=20=E7=95=AA=E5=89=A7=E4=BC=A0?= =?UTF-8?q?=E9=80=92aid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/controller.dart | 1 + lib/pages/bangumi/widgets/bangumi_panel.dart | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 2098302d..b26028cf 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -236,6 +236,7 @@ class BangumiIntroController extends GetxController { videoDetailCtr.bvid = bvid; videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; + videoDetailCtr.oid.value = aid; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index b01f3be7..3e965f34 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -85,7 +85,9 @@ class _BangumiPanelState extends State { item.cid, item.aid, ); - _bottomSheetController?.close(); + if (_bottomSheetController != null) { + _bottomSheetController?.close(); + } currentIndex = i; scrollToIndex(); } From b83ac38c6099a6cadf9a38f7542ccc5c24706cad Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 19:25:42 +0800 Subject: [PATCH 044/438] =?UTF-8?q?mod:=20scheme=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index ffe90e35..9009df6f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -162,9 +162,14 @@ class PiliSchame { final String host = value.host!; final String? path = value.path; Map? query = value.query; - RegExp regExp = RegExp(r'^(www\.)?m?\.(bilibili\.com)$'); + RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); if (regExp.hasMatch(host)) { - print('bilibili.com'); + print('bilibili.com host: $host'); + print('bilibili.com path: $path'); + final String lastPathSegment = path!.split('/').last; + if (lastPathSegment.contains('BV')) { + _videoPush(null, lastPathSegment); + } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); Get.toNamed( From 935073ce756ef21a8f07d2ec6d338742fff54a8b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 22 Apr 2024 23:51:31 +0800 Subject: [PATCH 045/438] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 335 +++++++++++++++---------------- 1 file changed, 166 insertions(+), 169 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0152a2cb..f216fbcf 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -23,6 +23,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; +import 'package:status_bar_control/status_bar_control.dart'; import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../services/shutdown_timer_service.dart'; @@ -61,10 +62,12 @@ class _VideoDetailPageState extends State late bool autoPiP; late Floating floating; bool isShowing = true; + late double statusHeight; @override void initState() { super.initState(); + getStatusHeight(); heroTag = Get.arguments['heroTag']; vdCtr = Get.put(VideoDetailController(), tag: heroTag); vdCtr.sheetHeight.value = localCache.get('sheetHeight'); @@ -203,6 +206,10 @@ class _VideoDetailPageState extends State }); } + getStatusHeight() async { + statusHeight = await StatusBarControl.getHeight; + } + @override void dispose() { shutdownTimerService.handleWaitingFinished(); @@ -281,6 +288,143 @@ class _VideoDetailPageState extends State } } + /// 手动播放 + Widget handlePlayPanel() { + return Stack( + children: [ + GestureDetector( + onTap: handlePlay, + child: Image.network( + vdCtr.videoItem['pic'], + width: Get.width, + height: videoHeight, + fit: BoxFit.cover, // 适应方式根据需要调整 + ), + ), + buildCustomAppBar(), + Positioned( + right: 12, + bottom: 10, + child: GestureDetector( + onTap: handlePlay, + child: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + ), + ), + ), + ], + ); + } + + /// tabbar + Widget tabbarBuild() { + return Container( + width: double.infinity, + height: 45, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + ), + ), + child: Material( + child: Row( + children: [ + Expanded( + child: Obx( + () => TabBar( + padding: EdgeInsets.zero, + controller: vdCtr.tabCtr, + labelStyle: const TextStyle(fontSize: 13), + labelPadding: const EdgeInsets.symmetric(horizontal: 10.0), + dividerColor: Colors.transparent, + tabs: + vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + ), + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Obx(() => AnimatedOpacity( + opacity: playerStatus.value != PlayerStatus.playing + ? 1 + : 0, + duration: const Duration(milliseconds: 100), + child: const Icon( + Icons.drag_handle_rounded, + size: 20, + color: Colors.grey, + ), + )), + const SizedBox(width: 8), + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: + const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + SizedBox( + width: 38, + height: 38, + child: Obx( + () => !vdCtr.isShowCover.value + ? IconButton( + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? + false); + }, + icon: !(plPlayerController?.isOpenDanmu.value ?? + false) + ? SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .outline, + ) + : SvgPicture.asset( + 'assets/images/video/danmu_open.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .primary, + ), + ) + : IconButton( + icon: SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context).colorScheme.outline, + ), + onPressed: () {}, + ), + ), + ), + const SizedBox(width: 18), + ], + ), + ), + ), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); @@ -338,162 +482,13 @@ class _VideoDetailPageState extends State }, ); - /// tabbar - Widget tabbarBuild = Container( - width: double.infinity, - height: 45, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), - ), - ), - child: Material( - child: Row( - children: [ - Flexible( - flex: 1, - child: Obx( - () => TabBar( - padding: EdgeInsets.zero, - controller: vdCtr.tabCtr, - labelStyle: const TextStyle(fontSize: 13), - labelPadding: - const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度 - dividerColor: Colors.transparent, - tabs: vdCtr.tabs - .map( - (String name) => Tab(text: name), - ) - .toList(), - ), - ), - ), - Flexible( - flex: 1, - child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Obx(() => AnimatedOpacity( - opacity: playerStatus.value != PlayerStatus.playing - ? 1 - : 0, - duration: const Duration(milliseconds: 100), - child: const Icon( - Icons.drag_handle_rounded, - size: 20, - color: Colors.grey, - ), - )), - const SizedBox(width: 8), - SizedBox( - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => vdCtr.showShootDanmakuSheet(), - child: - const Text('发弹幕', style: TextStyle(fontSize: 12)), - ), - ), - SizedBox( - width: 38, - height: 38, - child: Obx( - () => !vdCtr.isShowCover.value - ? IconButton( - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController - ?.isOpenDanmu.value ?? - false); - }, - icon: - !(plPlayerController?.isOpenDanmu.value ?? - false) - ? SvgPicture.asset( - 'assets/images/video/danmu_close.svg', - // ignore: deprecated_member_use - color: Theme.of(context) - .colorScheme - .outline, - ) - : SvgPicture.asset( - 'assets/images/video/danmu_open.svg', - // ignore: deprecated_member_use - color: Theme.of(context) - .colorScheme - .primary, - ), - ) - : IconButton( - icon: SvgPicture.asset( - 'assets/images/video/danmu_close.svg', - // ignore: deprecated_member_use - color: - Theme.of(context).colorScheme.outline, - ), - onPressed: () {}, - ), - ), - ), - const SizedBox(width: 18), - ], - ), - )), - ], - ), - ), - ); - - /// 手动播放 - Widget handlePlayPanel() { - return Stack( - children: [ - GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight.value, - ), - ), - Positioned( - top: 0, - left: 0, - right: 0, - child: buildCustomAppBar(), - ), - Positioned( - right: 12, - bottom: 10, - child: IconButton( - tooltip: '播放', - onPressed: () => handlePlay(), - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), - ), - ], - ); - } - Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: false, //plPlayerController?.isFullScreen.value != true, - right: false, //plPlayerController?.isFullScreen.value != true, + left: false, + right: false, child: Stack( children: [ Scaffold( @@ -511,12 +506,22 @@ class _VideoDetailPageState extends State controller: _extendNestCtr, headerSliverBuilder: (BuildContext context2, bool innerBoxIsScrolled) { + final Orientation orientation = + MediaQuery.of(context).orientation; + final bool isFullScreen = + plPlayerController?.isFullScreen.value == true; + final double expandedHeight = + orientation == Orientation.landscape || isFullScreen + ? (MediaQuery.sizeOf(context).height - + (orientation == Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top)) + : videoHeight.value; return [ Obx( () { - if (MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true) { + if (orientation == Orientation.landscape || + isFullScreen) { enterFullScreen(); } else { exitFullScreen(); @@ -528,15 +533,7 @@ class _VideoDetailPageState extends State elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, - expandedHeight: MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true - ? (MediaQuery.sizeOf(context).height - - (MediaQuery.of(context).orientation == - Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top)) - : videoHeight.value, + expandedHeight: expandedHeight, backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( @@ -556,13 +553,13 @@ class _VideoDetailPageState extends State child: LayoutBuilder( builder: (BuildContext context, BoxConstraints boxConstraints) { - // final double maxWidth = - // boxConstraints.maxWidth; - // final double maxHeight = - // boxConstraints.maxHeight; return Stack( children: [ - if (isShowing) videoPlayerPanel, + if (isShowing) + Padding( + padding: EdgeInsets.only(top: 0), + child: videoPlayerPanel, + ), /// 关闭自动播放时 手动播放 Obx( @@ -604,7 +601,7 @@ class _VideoDetailPageState extends State color: Theme.of(context).colorScheme.background, child: Column( children: [ - tabbarBuild, + tabbarBuild(), Expanded( child: TabBarView( controller: vdCtr.tabCtr, From 926c99001ebd47b860073029419f3d93338ab346 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 22 Apr 2024 23:58:36 +0800 Subject: [PATCH 046/438] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E7=AE=80?= =?UTF-8?q?=E4=BB=8B=E5=A2=9E=E5=8A=A0aid=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../introduction/widgets/intro_detail.dart | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 1e9bb842..3ec92023 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -23,16 +23,34 @@ class IntroDetail extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.bvid!, - style: TextStyle( - fontSize: 13, color: Theme.of(context).colorScheme.primary), - ), + Row( + children: [ + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.aid!.toString(), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ) + ], ), const SizedBox(height: 4), Text.rich( From 31125435b11501e4236456e31541ab48aa996118 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Apr 2024 23:40:40 +0800 Subject: [PATCH 047/438] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=A0=8F=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/overlay_pop.dart | 1 + lib/common/widgets/video_card_v.dart | 283 +++++++++++---------------- lib/pages/home/view.dart | 87 ++++---- lib/pages/rcmd/view.dart | 20 -- 4 files changed, 160 insertions(+), 231 deletions(-) diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index fe9b9377..4f0a3899 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -62,6 +62,7 @@ class OverlayPop extends StatelessWidget { Expanded( child: Text( videoItem.title! as String, + style: Theme.of(context).textTheme.titleSmall, ), ), const SizedBox(width: 4), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0d96f7b7..0598f973 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import '../../models/model_rec_video_item.dart'; +import 'overlay_pop.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -19,15 +20,15 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - final Function()? longPress; - final Function()? longPressEnd; + // final Function()? longPress; + // final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, - this.longPress, - this.longPressEnd, + // this.longPress, + // this.longPressEnd, }) : super(key: key); bool isStringNumeric(String str) { @@ -131,59 +132,55 @@ class VideoCardV extends StatelessWidget { elevation: 0, clipBehavior: Clip.hardEdge, margin: EdgeInsets.zero, - child: GestureDetector( + child: InkWell( + onTap: () async => onPushDetail(heroTag), onLongPress: () { - if (longPress != null) { - longPress!(); - } + SmartDialog.show( + builder: (context) => OverlayPop( + videoItem: videoItem, + closeFn: () => SmartDialog.dismiss(), + ), + ); }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - child: Column( - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic, - width: maxWidth, - height: maxHeight, - ), + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic, + width: maxWidth, + height: maxHeight, ), - if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - ) - ], - ], - ); - }), - ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) - ], - ), + ), + if (videoItem.duration > 0) + if (crossAxisCount == 1) ...[ + PBadge( + bottom: 10, + right: 10, + text: Utils.timeFormat(videoItem.duration), + ) + ] else ...[ + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + ) + ], + ], + ); + }), + ), + VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + ], ), ), ); @@ -196,122 +193,73 @@ class VideoContent extends StatelessWidget { const VideoContent( {Key? key, required this.videoItem, required this.crossAxisCount}) : super(key: key); + + Widget _buildBadge(String text, String type, [double fs = 12]) { + return PBadge( + text: text, + stack: 'normal', + size: 'small', + type: type, + fs: fs, + ); + } + @override Widget build(BuildContext context) { - return Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Padding( - padding: crossAxisCount == 1 - ? const EdgeInsets.fromLTRB(9, 9, 9, 4) - : const EdgeInsets.fromLTRB(5, 8, 5, 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Expanded( - child: Text( - videoItem.title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - if (videoItem.goto == 'av' && crossAxisCount == 1) ...[ - const SizedBox(width: 10), - VideoPopupMenu( - size: 32, - iconSize: 18, - videoItem: videoItem, - ), - ], - ], - ), - if (crossAxisCount > 1) ...[ - const SizedBox(height: 2), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - ], - if (crossAxisCount == 1) const SizedBox(height: 4), - Row( - children: [ - if (videoItem.goto == 'bangumi') ...[ - PBadge( - text: videoItem.bangumiBadge, - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.rcmdReason != null && - videoItem.rcmdReason.content != '') ...[ - PBadge( - text: videoItem.rcmdReason.content, - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - if (videoItem.goto == 'picture') ...[ - const PBadge( - text: '动态', - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.isFollowed == 1) ...[ - const PBadge( - text: '已关注', - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Text( - videoItem.owner.name, - maxLines: 1, - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - if (crossAxisCount == 1) ...[ - Text( - ' • ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - const Spacer(), - ], - if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), - ] else ...[ - const SizedBox(height: 24) - ] - ], - ), + return Padding( + padding: crossAxisCount == 1 + ? const EdgeInsets.fromLTRB(9, 9, 9, 4) + : const EdgeInsets.fromLTRB(5, 8, 5, 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoItem.title, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (crossAxisCount > 1) ...[ + const SizedBox(height: 2), + VideoStat(videoItem: videoItem, crossAxisCount: crossAxisCount), ], - ), + if (crossAxisCount == 1) const SizedBox(height: 4), + Row( + children: [ + if (videoItem.goto == 'bangumi') + _buildBadge(videoItem.bangumiBadge, 'line', 9), + if (videoItem.rcmdReason?.content != null && + videoItem.rcmdReason.content != '') + _buildBadge(videoItem.rcmdReason.content, 'color'), + if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9), + if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'), + Expanded( + flex: crossAxisCount == 1 ? 0 : 1, + child: Text( + videoItem.owner.name, + maxLines: 1, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + if (crossAxisCount == 1) ...[ + const SizedBox(width: 10), + VideoStat( + videoItem: videoItem, + crossAxisCount: crossAxisCount, + ), + const Spacer(), + ], + if (videoItem.goto == 'av') + VideoPopupMenu( + size: 24, + iconSize: 14, + videoItem: videoItem, + ), + ], + ), + ], ), ); } @@ -383,7 +331,6 @@ class VideoPopupMenu extends StatelessWidget { size: iconSize, ), position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), onSelected: (String type) {}, itemBuilder: (BuildContext context) => >[ PopupMenuItem( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index cc228f6b..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -214,6 +214,34 @@ class UserInfoWidget extends StatelessWidget { final VoidCallback? callback; final HomeController? ctr; + Widget buildLoggedInWidget(context) { + return Stack( + children: [ + NetworkImgLayer( + type: 'avatar', + width: 34, + height: 34, + src: userFace, + ), + Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => callback?.call(), + splashColor: Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.3), + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + ), + ), + ) + ], + ); + } + @override Widget build(BuildContext context) { return Row( @@ -231,31 +259,7 @@ class UserInfoWidget extends StatelessWidget { const SizedBox(width: 8), Obx( () => userLogin.value - ? Stack( - children: [ - NetworkImgLayer( - type: 'avatar', - width: 34, - height: 34, - src: userFace, - ), - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => callback?.call(), - splashColor: Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.3), - borderRadius: const BorderRadius.all( - Radius.circular(50), - ), - ), - ), - ) - ], - ) + ? buildLoggedInWidget(context) : DefaultUser(callback: () => callback!()), ), ], @@ -402,30 +406,27 @@ class SearchBar extends StatelessWidget { color: colorScheme.onSecondaryContainer.withOpacity(0.05), child: InkWell( splashColor: colorScheme.primaryContainer.withOpacity(0.3), - onTap: () => Get.toNamed( - '/search', - parameters: {'hintText': ctr!.defaultSearch.value}, - ), - child: Row( - children: [ - const SizedBox(width: 14), - Icon( - Icons.search_outlined, - color: colorScheme.onSecondaryContainer, - ), - const SizedBox(width: 10), - Obx( - () => Expanded( - child: Text( + onTap: () => Get.toNamed('/search', + parameters: {'hintText': ctr!.defaultSearch.value}), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: Row( + children: [ + Icon( + Icons.search_outlined, + color: colorScheme.onSecondaryContainer, + ), + const SizedBox(width: 10), + Obx( + () => Text( ctr!.defaultSearch.value, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: colorScheme.outline), ), ), - ), - const SizedBox(width: 15), - ], + ], + ), ), ), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index acc1e654..67567870 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/utils/main_stream.dart'; @@ -118,16 +116,6 @@ class _RcmdPageState extends State ); } - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _rcmdController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _rcmdController.popupDialog?.remove), - ), - ); - } - Widget contentGrid(ctr, videoList) { // double maxWidth = Get.size.width; // int baseWidth = 500; @@ -158,14 +146,6 @@ class _RcmdPageState extends State ? VideoCardV( videoItem: videoList[index], crossAxisCount: crossAxisCount, - longPress: () { - _rcmdController.popupDialog = - _createPopupDialog(videoList[index]); - Overlay.of(context).insert(_rcmdController.popupDialog!); - }, - longPressEnd: () { - _rcmdController.popupDialog?.remove(); - }, ) : const VideoCardVSkeleton(); }, From ab45d2e6a68a5000c82e038a3f1308ab88b4be0d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 24 Apr 2024 23:56:07 +0800 Subject: [PATCH 048/438] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 310 ++++++++++++++------------- 1 file changed, 157 insertions(+), 153 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0598f973..9916aa7a 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; import '../../models/model_rec_video_item.dart'; import 'overlay_pop.dart'; import 'stat/danmu.dart'; @@ -20,15 +21,11 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - // final Function()? longPress; - // final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, - // this.longPress, - // this.longPressEnd, }) : super(key: key); bool isStringNumeric(String str) { @@ -128,60 +125,56 @@ class VideoCardV extends StatelessWidget { @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(videoItem.id); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - onLongPress: () { - SmartDialog.show( - builder: (context) => OverlayPop( - videoItem: videoItem, - closeFn: () => SmartDialog.dismiss(), - ), - ); - }, - child: Column( - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic, - width: maxWidth, - height: maxHeight, - ), + return InkWell( + onTap: () async => onPushDetail(heroTag), + onLongPress: () { + SmartDialog.show( + builder: (context) => OverlayPop( + videoItem: videoItem, + closeFn: () => SmartDialog.dismiss(), + ), + ); + }, + borderRadius: BorderRadius.circular(16), + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic, + width: maxWidth, + height: maxHeight, ), - if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - ) - ], - ], - ); - }), - ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) - ], - ), + ), + if (videoItem.duration > 0) + if (crossAxisCount == 1) ...[ + PBadge( + bottom: 10, + right: 10, + text: Utils.timeFormat(videoItem.duration), + ) + ] else ...[ + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + ) + ], + ], + ); + }), + ), + VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + ], ), ); } @@ -252,11 +245,28 @@ class VideoContent extends StatelessWidget { const Spacer(), ], if (videoItem.goto == 'av') - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), + SizedBox( + width: 24, + height: 24, + child: IconButton( + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(videoItem: videoItem); + }, + ); + }, + icon: Icon( + Icons.more_vert_outlined, + color: Theme.of(context).colorScheme.outline, + size: 14, + ), + ), + ) ], ), ], @@ -279,15 +289,9 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView( - theme: 'gray', - view: videoItem.stat.view, - ), + StatView(theme: 'gray', view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', - danmu: videoItem.stat.danmu, - ), + StatDanMu(theme: 'gray', danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( @@ -306,98 +310,98 @@ class VideoStat extends StatelessWidget { } } -class VideoPopupMenu extends StatelessWidget { - final double? size; - final double? iconSize; +class MorePanel extends StatelessWidget { final dynamic videoItem; + const MorePanel({super.key, required this.videoItem}); - const VideoPopupMenu({ - Key? key, - required this.size, - required this.iconSize, - required this.videoItem, - }) : super(key: key); + Future menuActionHandler(String type) async { + switch (type) { + case 'block': + blockUser(); + break; + case 'watchLater': + var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + Get.back(); + break; + default: + } + } + + void blockUser() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } @override Widget build(BuildContext context) { - return SizedBox( - width: size, - height: size, - child: PopupMenuButton( - padding: EdgeInsets.zero, - icon: Icon( - Icons.more_vert_outlined, - color: Theme.of(context).colorScheme.outline, - size: iconSize, - ), - position: PopupMenuPosition.under, - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - onTap: () async { - var res = - await UserHttp.toViewLater(bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), ), ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], + ListTile( + onTap: () async => await menuActionHandler('block'), + minLeadingWidth: 0, + leading: const Icon(Icons.block, size: 19), + title: Text( + '拉黑up主 「${videoItem.owner.name}」', + style: Theme.of(context).textTheme.titleSmall, ), ), + ListTile( + onTap: () async => await menuActionHandler('watchLater'), + minLeadingWidth: 0, + leading: const Icon(Icons.watch_later_outlined, size: 19), + title: + Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), + ), ], ), ); From 1f426881f2c4ae43f5f58695ff342d175460f2a8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Apr 2024 22:42:42 +0800 Subject: [PATCH 049/438] =?UTF-8?q?mod:=20=E4=BD=BF=E7=94=A8feed=5Fversion?= =?UTF-8?q?=20V3=E6=8E=A8=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index d43656b2..f8e19060 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -35,7 +35,7 @@ class VideoHttp { Api.recommendListWeb, data: { 'version': 1, - 'feed_version': 'V8', + 'feed_version': 'V3', 'homepage_ver': 1, 'ps': ps, 'fresh_idx': freshIdx, From c6f5bc561c5f4f5c275b569338132be9df41f6f8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Apr 2024 23:18:21 +0800 Subject: [PATCH 050/438] =?UTF-8?q?feat:=20up=E6=8A=95=E7=A8=BF=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=85=85=E7=94=B5=E4=B8=93=E5=B1=9E=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 9 +++++++++ lib/pages/member_archive/view.dart | 1 + 2 files changed, 10 insertions(+) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 99059a9e..25e701ac 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -23,6 +23,7 @@ class VideoCardH extends StatelessWidget { this.showView = true, this.showDanmaku = true, this.showPubdate = false, + this.showCharge = false, }); // ignore: prefer_typing_uninitialized_variables final videoItem; @@ -33,6 +34,7 @@ class VideoCardH extends StatelessWidget { final bool showView; final bool showDanmaku; final bool showPubdate; + final bool showCharge; @override Widget build(BuildContext context) { @@ -121,6 +123,13 @@ class VideoCardH extends StatelessWidget { // videoItem.rcmdReason.content != '') // pBadge(videoItem.rcmdReason.content, context, // 6.0, 6.0, null, null), + if (showCharge && videoItem?.isChargingSrc) + const PBadge( + text: '充电专属', + right: 6.0, + top: 6.0, + type: 'primary', + ), ], ); }, diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 3103683a..f38ca0cb 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -79,6 +79,7 @@ class _MemberArchivePageState extends State { videoItem: list[index], showOwner: false, showPubdate: true, + showCharge: true, ); }, childCount: list.length, From 4e66a3f9e7ab379a5501f40ea5fabd983e0a8480 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Apr 2024 23:22:26 +0800 Subject: [PATCH 051/438] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +- lib/pages/video/detail/controller.dart | 5 +- lib/pages/video/detail/reply/view.dart | 9 +- .../detail/reply/widgets/reply_item.dart | 133 +++++++++--------- .../video/detail/reply_reply/controller.dart | 10 +- lib/pages/video/detail/reply_reply/view.dart | 14 +- 6 files changed, 99 insertions(+), 78 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index c6ec682a..757ba0ce 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4d40e535..4613c7ca 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -161,11 +161,11 @@ class VideoDetailController extends GetxController getSubtitle(); } - showReplyReplyPanel() { + showReplyReplyPanel(oid, fRpid, firstFloor, currentReply) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( - oid: oid.value, + oid: oid, rpid: fRpid, closePanel: () => { fRpid = 0, @@ -174,6 +174,7 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, + currentReply: currentReply, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 2a167fe9..3b4891b8 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -110,14 +110,15 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { videoDetailCtr.oid.value = replyItem.oid; videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; - videoDetailCtr.showReplyReplyPanel(); + videoDetailCtr.showReplyReplyPanel( + replyItem.oid, replyItem.rpid!, replyItem, currentReply); } } @@ -228,8 +229,8 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 50fe20d4..8a8e4af7 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,7 +12,6 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -45,7 +44,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem); + replyReply!(replyItem, null); } }, onLongPress: () { @@ -59,28 +58,23 @@ class ReplyItem extends StatelessWidget { }, ); }, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(12, 14, 8, 5), - child: content(context), - ), - Divider( - indent: 55, - endIndent: 15, - height: 0.3, - color: Theme.of(context) - .colorScheme - .onInverseSurface - .withOpacity(0.5), - ) - ], + child: Container( + padding: const EdgeInsets.fromLTRB(12, 14, 8, 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: + Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.5), + ))), + child: content(context), ), ), ); } Widget lfAvtar(BuildContext context, String heroTag) { + ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( children: [ Hero( @@ -100,11 +94,11 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: Theme.of(context).colorScheme.background, + color: colorScheme.background, ), child: Icon( Icons.offline_bolt, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, size: 16, ), ), @@ -117,7 +111,7 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: Theme.of(context).colorScheme.background, + color: colorScheme.background, ), child: Image.asset( 'assets/images/big-vip.png', @@ -131,6 +125,8 @@ class ReplyItem extends StatelessWidget { Widget content(BuildContext context) { final String heroTag = Utils.makeHeroTag(replyItem!.mid); + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -160,16 +156,17 @@ class ReplyItem extends StatelessWidget { style: TextStyle( color: replyItem!.member!.vip!['vipStatus'] > 0 ? const Color.fromARGB(255, 251, 100, 163) - : Theme.of(context).colorScheme.outline, + : colorScheme.outline, fontSize: 13, ), ), - const SizedBox(width: 6), - Image.asset( - 'assets/images/lv/lv${replyItem!.member!.level}.png', - height: 11, + Padding( + padding: const EdgeInsets.only(left: 6, right: 6), + child: Image.asset( + 'assets/images/lv/lv${replyItem!.member!.level}.png', + height: 11, + ), ), - const SizedBox(width: 6), if (replyItem!.isUp!) const PBadge( text: 'UP', @@ -184,9 +181,8 @@ class ReplyItem extends StatelessWidget { Text( Utils.dateFormat(replyItem!.ctime), style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.outline, + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, ), ), if (replyItem!.replyControl != null && @@ -194,11 +190,8 @@ class ReplyItem extends StatelessWidget { Text( ' • ${replyItem!.replyControl!.location!}', style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline), ), ], ) @@ -256,6 +249,8 @@ class ReplyItem extends StatelessWidget { // 感谢、回复、复制 Widget bottonAction(BuildContext context, replyControl) { + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Row( children: [ const SizedBox(width: 32), @@ -287,15 +282,13 @@ class ReplyItem extends StatelessWidget { }, child: Row(children: [ Icon(Icons.reply, - size: 18, - color: - Theme.of(context).colorScheme.outline.withOpacity(0.8)), + size: 18, color: colorScheme.outline.withOpacity(0.8)), const SizedBox(width: 3), Text( '回复', style: TextStyle( - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, ), ), ]), @@ -306,8 +299,8 @@ class ReplyItem extends StatelessWidget { Text( 'up主觉得很赞', style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize), + color: colorScheme.primary, + fontSize: textTheme.labelMedium!.fontSize), ), const SizedBox(width: 2), ], @@ -316,8 +309,8 @@ class ReplyItem extends StatelessWidget { Text( '热评', style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize), + color: colorScheme.primary, + fontSize: textTheme.labelMedium!.fontSize), ), const Spacer(), ZanButton(replyItem: replyItem, replyType: replyType), @@ -347,10 +340,13 @@ class ReplyItemRow extends StatelessWidget { Widget build(BuildContext context) { final bool isShow = replyControl!.isShow!; final int extraRow = replyControl != null && isShow ? 1 : 0; + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; + return Container( margin: const EdgeInsets.only(left: 42, right: 4, top: 0), child: Material( - color: Theme.of(context).colorScheme.onInverseSurface, + color: colorScheme.onInverseSurface, borderRadius: BorderRadius.circular(6), clipBehavior: Clip.hardEdge, animationDuration: Duration.zero, @@ -361,7 +357,9 @@ class ReplyItemRow extends StatelessWidget { for (int i = 0; i < replies!.length; i++) ...[ InkWell( // 一楼点击评论展开评论详情 - onTap: () => replyReply!(replyItem), + // onTap: () { + // replyReply?.call(replyItem, replies![i]); + // }, onLongPress: () { feedBack(); showModalBottomSheet( @@ -379,7 +377,7 @@ class ReplyItemRow extends StatelessWidget { 8, i == 0 && (extraRow == 1 || replies!.length > 1) ? 8 : 5, 8, - i == 0 && (extraRow == 1 || replies!.length > 1) ? 5 : 6, + 6, ), child: Text.rich( overflow: TextOverflow.ellipsis, @@ -393,7 +391,7 @@ class ReplyItemRow extends StatelessWidget { .textTheme .titleSmall! .fontSize, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -429,15 +427,14 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem), + onTap: () => replyReply!(replyItem, null), child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), child: Text.rich( TextSpan( style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, + fontSize: textTheme.labelMedium!.fontSize, ), children: [ if (replyControl!.upReply!) @@ -445,7 +442,7 @@ class ReplyItemRow extends StatelessWidget { TextSpan( text: replyControl!.entryText!, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], @@ -464,6 +461,7 @@ InlineSpan buildContent( BuildContext context, replyItem, replyReply, fReplyItem) { final String routePath = Get.currentRoute; bool isVideoPage = routePath.startsWith('/video'); + ColorScheme colorScheme = Theme.of(context).colorScheme; // replyItem 当前回复内容 // replyReply 查看二楼回复(回复详情)回调 @@ -533,8 +531,10 @@ InlineSpan buildContent( TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), + ..onTap = () => replyReply?.call( + replyItem.root == 0 ? replyItem : fReplyItem, + replyItem, + ), ), ); } @@ -564,7 +564,7 @@ InlineSpan buildContent( TextSpan( text: matchStr, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -584,7 +584,7 @@ InlineSpan buildContent( text: ' $matchStr ', style: isVideoPage ? TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ) : null, recognizer: TapGestureRecognizer() @@ -624,14 +624,14 @@ InlineSpan buildContent( child: Image.network( content.jumpUrl[matchStr]['prefix_icon'], height: 19, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], TextSpan( text: content.jumpUrl[matchStr]['title'], style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () async { @@ -721,7 +721,7 @@ InlineSpan buildContent( TextSpan( text: matchStr, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -747,7 +747,7 @@ InlineSpan buildContent( text: ' $matchStr ', style: isVideoPage ? TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ) : null, recognizer: TapGestureRecognizer() @@ -786,14 +786,14 @@ InlineSpan buildContent( child: Image.network( content.jumpUrl[patternStr]['prefix_icon'], height: 19, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], TextSpan( text: content.jumpUrl[patternStr]['title'], style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -997,7 +997,8 @@ class MorePanel extends StatelessWidget { @override Widget build(BuildContext context) { - Color errorColor = Theme.of(context).colorScheme.error; + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), child: Column( @@ -1013,7 +1014,7 @@ class MorePanel extends StatelessWidget { width: 32, height: 3, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.outline, + color: colorScheme.outline, borderRadius: const BorderRadius.all(Radius.circular(3))), ), ), @@ -1023,13 +1024,13 @@ class MorePanel extends StatelessWidget { onTap: () async => await menuActionHandler('copyAll'), minLeadingWidth: 0, leading: const Icon(Icons.copy_all_outlined, size: 19), - title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall), + title: Text('复制全部', style: textTheme.titleSmall), ), ListTile( onTap: () async => await menuActionHandler('copyFreedom'), minLeadingWidth: 0, leading: const Icon(Icons.copy_outlined, size: 19), - title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall), + title: Text('自由复制', style: textTheme.titleSmall), ), // ListTile( // onTap: () async => await menuActionHandler('block'), diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index e94aaea5..4c553e29 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init'}) async { + Future queryReplyList({type = 'init', currentReply}) async { if (type == 'init') { currentPage = 0; } @@ -63,6 +63,14 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } + if (replyList.isNotEmpty && currentReply != null) { + int indexToRemove = + replyList.indexWhere((item) => currentReply.rpid == item.rpid); + // 如果找到了指定ID的项,则移除 + if (indexToRemove != -1) { + replyList.removeAt(indexToRemove); + } + } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 3fe84c71..6441c600 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,6 +20,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, + this.currentReply, super.key, }); final int? oid; @@ -29,6 +30,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; + final dynamic currentReply; @override State createState() => _VideoReplyReplyPanelState(); @@ -63,7 +65,9 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); } void replyReply(replyItem) {} @@ -107,7 +111,9 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList(); + return await _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -140,6 +146,10 @@ class _VideoReplyReplyPanelState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { + if (widget.currentReply != null) { + _videoReplyReplyController.replyList + .insert(0, widget.currentReply); + } // 请求成功 return Obx( () => SliverList( From 8273ff2e02f3900425465485c31d99ef3e5b5b15 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Apr 2024 23:46:54 +0800 Subject: [PATCH 052/438] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/reply.dart | 11 +--- lib/pages/dynamics/detail/controller.dart | 2 + lib/pages/dynamics/detail/view.dart | 63 +++++++++++--------- lib/pages/video/detail/reply/controller.dart | 2 + lib/pages/video/detail/reply/view.dart | 59 ++++++++++-------- lib/pages/video/detail/reply_new/view.dart | 22 ++++--- 6 files changed, 88 insertions(+), 71 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index f080ed51..880f9072 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -22,19 +22,14 @@ class ReplyHttp { return { 'status': true, 'data': ReplyData.fromJson(res.data['data']), + 'code': 200, }; } else { - Map errMap = { - -400: '请求错误', - -404: '无此项', - 12002: '当前页面评论功能已关闭', - 12009: '评论主体的type不合法', - 12061: 'UP主已关闭评论区', - }; return { 'status': false, 'date': [], - 'msg': errMap[res.data['code']] ?? res.data['message'], + 'code': res.data['code'], + 'msg': res.data['message'], }; } } diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index 8e117383..f34de061 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -25,6 +25,7 @@ class DynamicDetailController extends GetxController { RxString sortTypeTitle = ReplySortType.time.titles.obs; RxString sortTypeLabel = ReplySortType.time.labels.obs; Box setting = GStrorage.setting; + RxInt replyReqCode = 200.obs; @override void onInit() { @@ -84,6 +85,7 @@ class DynamicDetailController extends GetxController { replyList.addAll(replies); } } + replyReqCode.value = res['code']; isLoadingMore = false; return res; } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 757ba0ce..11636b5b 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -369,35 +369,40 @@ class _DynamicDetailPageState extends State curve: Curves.easeInOut, ), ), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.values[replyType], - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - { - _dynamicDetailController.replyList.add(value['data']), - _dynamicDetailController.acount.value++ - } - }, - ); - }, - tooltip: '评论动态', - child: const Icon(Icons.reply), + child: Obx( + () => _dynamicDetailController.replyReqCode.value == 12061 + ? const SizedBox() + : FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.values[replyType], + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _dynamicDetailController.replyList + .add(value['data']), + _dynamicDetailController.acount.value++ + } + }, + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), ), ), ); diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 06ce26ff..b718c840 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -37,6 +37,7 @@ class VideoReplyController extends GetxController { RxString sortTypeLabel = ReplySortType.time.labels.obs; Box setting = GStrorage.setting; + RxInt replyReqCode = 200.obs; @override void onInit() { @@ -106,6 +107,7 @@ class VideoReplyController extends GetxController { replyList.addAll(replies); } } + replyReqCode.value = res['code']; isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 3b4891b8..420f349c 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -277,32 +277,39 @@ class _VideoReplyPanelState extends State parent: fabAnimationCtr, curve: Curves.easeInOut, )), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _videoReplyController.aid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.video, - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - {_videoReplyController.replyList.add(value['data'])} - }, - ); - }, - tooltip: '发表评论', - child: const Icon(Icons.reply), + child: Obx( + () => _videoReplyController.replyReqCode.value == 12061 + ? const SizedBox() + : FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _videoReplyController.aid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.video, + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _videoReplyController.replyList + .add(value['data']) + } + }, + ); + }, + tooltip: '发表评论', + child: const Icon(Icons.reply), + ), ), ), ), diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 029e015a..3da15f64 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -43,6 +43,7 @@ class _VideoReplyNewDialogState extends State String toolbarType = 'input'; RxBool isForward = false.obs; RxBool showForward = false.obs; + RxString message = ''.obs; @override void initState() { @@ -80,15 +81,15 @@ class _VideoReplyNewDialogState extends State Future submitReplyAdd() async { feedBack(); - String message = _replyContentController.text; + // String message = _replyContentController.text; var result = await VideoHttp.replyAdd( type: widget.replyType ?? ReplyType.video, oid: widget.oid!, root: widget.root!, parent: widget.parent!, message: widget.replyItem != null && widget.replyItem!.root != 0 - ? ' 回复 @${widget.replyItem!.member!.uname!} : $message' - : message, + ? ' 回复 @${widget.replyItem!.member!.uname!} : ${message.value}' + : message.value, ); if (result['status']) { SmartDialog.showToast(result['data']['success_toast']); @@ -100,7 +101,7 @@ class _VideoReplyNewDialogState extends State if (isForward.value) { await DynamicsHttp.dynamicCreate( mid: 0, - rawText: message, + rawText: message.value, oid: widget.oid!, scene: 5, ); @@ -188,7 +189,7 @@ class _VideoReplyNewDialogState extends State autovalidateMode: AutovalidateMode.onUserInteraction, child: TextField( controller: _replyContentController, - minLines: 1, + minLines: 3, maxLines: null, autofocus: false, focusNode: replyContentFocusNode, @@ -199,6 +200,9 @@ class _VideoReplyNewDialogState extends State fontSize: 14, )), style: Theme.of(context).textTheme.bodyLarge, + onChanged: (text) { + message.value = text; + }, ), ), ), @@ -267,9 +271,11 @@ class _VideoReplyNewDialogState extends State const Spacer(), SizedBox( height: 36, - child: FilledButton( - onPressed: () => submitReplyAdd(), - child: const Text('发送'), + child: Obx( + () => FilledButton( + onPressed: message.isNotEmpty ? submitReplyAdd : null, + child: const Text('发送'), + ), ), ), ], From b752214af3c09f885e312bf23ed99567e07c4a68 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 01:04:11 +0800 Subject: [PATCH 053/438] =?UTF-8?q?fix:=20durl=E6=A0=BC=E5=BC=8F=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/quality.dart | 8 ++ lib/pages/video/detail/controller.dart | 84 ++++++++++++------- .../video/detail/widgets/header_control.dart | 43 ++++++---- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/lib/models/video/play/quality.dart b/lib/models/video/play/quality.dart index 6cae84cc..7bedf62a 100644 --- a/lib/models/video/play/quality.dart +++ b/lib/models/video/play/quality.dart @@ -39,6 +39,14 @@ extension VideoQualityCode on VideoQuality { } return null; } + + static int? toCode(VideoQuality quality) { + final index = VideoQuality.values.indexOf(quality); + if (index != -1 && index < _codeList.length) { + return _codeList[index]; + } + return null; + } } extension VideoQualityDesc on VideoQuality { diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4d40e535..3cc43005 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -51,7 +51,7 @@ class VideoDetailController extends GetxController /// 播放器配置 画质 音质 解码格式 late VideoQuality currentVideoQa; AudioQuality? currentAudioQa; - late VideoDecodeFormats currentDecodeFormats; + VideoDecodeFormats? currentDecodeFormats; // 是否开始自动播放 存在多p的情况下,第二p需要为true RxBool autoPlay = true.obs; // 视频资源是否有效 @@ -107,6 +107,7 @@ class VideoDetailController extends GetxController BottomControlType.fullscreen, ].obs; RxDouble sheetHeight = 0.0.obs; + RxString archiveSourceType = 'dash'.obs; @override void onInit() { @@ -189,37 +190,43 @@ class VideoDetailController extends GetxController plPlayerController.isBuffering.value = false; plPlayerController.buffered.value = Duration.zero; - /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl - List videoList = - data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList(); - try { - firstVideo = videoList - .firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code)); - } catch (_) { - if (currentVideoQa == VideoQuality.dolbyVision) { - firstVideo = videoList.first; - currentDecodeFormats = - VideoDecodeFormatsCode.fromString(videoList.first.codecs!)!; - } else { - // 当前格式不可用 - currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( - SettingBoxKey.defaultDecode, - defaultValue: VideoDecodeFormats.values.last.code))!; - firstVideo = videoList - .firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code)); + if (archiveSourceType.value == 'dash') { + /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl + List videoList = + data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList(); + try { + firstVideo = videoList.firstWhere( + (i) => i.codecs!.startsWith(currentDecodeFormats?.code)); + } catch (_) { + if (currentVideoQa == VideoQuality.dolbyVision) { + firstVideo = videoList.first; + currentDecodeFormats = + VideoDecodeFormatsCode.fromString(videoList.first.codecs!)!; + } else { + // 当前格式不可用 + currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( + SettingBoxKey.defaultDecode, + defaultValue: VideoDecodeFormats.values.last.code))!; + firstVideo = videoList.firstWhere( + (i) => i.codecs!.startsWith(currentDecodeFormats?.code)); + } + } + videoUrl = firstVideo.baseUrl!; + + /// 根据currentAudioQa 重新设置audioUrl + if (currentAudioQa != null) { + final AudioItem firstAudio = data.dash!.audio!.firstWhere( + (AudioItem i) => i.id == currentAudioQa!.code, + orElse: () => data.dash!.audio!.first, + ); + audioUrl = firstAudio.baseUrl ?? ''; } } - videoUrl = firstVideo.baseUrl!; - /// 根据currentAudioQa 重新设置audioUrl - if (currentAudioQa != null) { - final AudioItem firstAudio = data.dash!.audio!.firstWhere( - (AudioItem i) => i.id == currentAudioQa!.code, - orElse: () => data.dash!.audio!.first, - ); - audioUrl = firstAudio.baseUrl ?? ''; + if (archiveSourceType.value == 'durl') { + cacheVideoQa = VideoQualityCode.toCode(currentVideoQa); + queryVideoUrl(); } - playerInit(); } @@ -272,7 +279,8 @@ class VideoDetailController extends GetxController // 视频链接 Future queryVideoUrl() async { - var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); + var result = + await VideoHttp.videoUrl(cid: cid.value, bvid: bvid, qn: cacheVideoQa); if (result['status']) { data = result['data']; if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) { @@ -290,8 +298,22 @@ class VideoDetailController extends GetxController } return result; } + if (data.durl != null) { + archiveSourceType.value = 'durl'; + videoUrl = data.durl!.first.url!; + audioUrl = ''; + defaultST = Duration.zero; + firstVideo = VideoItem(); + currentVideoQa = VideoQualityCode.fromCode(data.quality!)!; + if (autoPlay.value) { + await playerInit(); + isShowCover.value = false; + } + return result; + } final List allVideosList = data.dash!.video!; try { + archiveSourceType.value = 'dash'; // 当前可播放的最高质量视频 int currentHighVideoQa = allVideosList.first.quality!.code; // 预设的画质为null,则当前可用的最高质量 @@ -321,7 +343,7 @@ class VideoDetailController extends GetxController // 当前视频没有对应格式返回第一个 bool flag = false; for (var i in supportDecodeFormats) { - if (i.startsWith(currentDecodeFormats.code)) { + if (i.startsWith(currentDecodeFormats?.code)) { flag = true; } } @@ -335,7 +357,7 @@ class VideoDetailController extends GetxController /// 取出符合当前解码格式的videoItem try { firstVideo = videosList.firstWhere( - (e) => e.codecs!.startsWith(currentDecodeFormats.code)); + (e) => e.codecs!.startsWith(currentDecodeFormats?.code)); } catch (_) { firstVideo = videosList.first; } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e00311c5..6acd20d7 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -180,15 +180,16 @@ class _HeaderControlState extends State { '当前音质 ${widget.videoDetailCtr!.currentAudioQa!.description}', style: subTitleStyle), ), - ListTile( - onTap: () => {Get.back(), showSetDecodeFormats()}, - dense: true, - leading: const Icon(Icons.av_timer_outlined, size: 20), - title: const Text('解码格式', style: titleStyle), - subtitle: Text( - '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', - style: subTitleStyle), - ), + if (widget.videoDetailCtr!.currentDecodeFormats != null) + ListTile( + onTap: () => {Get.back(), showSetDecodeFormats()}, + dense: true, + leading: const Icon(Icons.av_timer_outlined, size: 20), + title: const Text('解码格式', style: titleStyle), + subtitle: Text( + '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats!.description}', + style: subTitleStyle), + ), ListTile( onTap: () => {Get.back(), showSetRepeat()}, dense: true, @@ -541,16 +542,24 @@ class _HeaderControlState extends State { /// 可用的质量分类 int userfulQaSam = 0; - final List video = videoInfo.dash!.video!; - final Set idSet = {}; - for (final VideoItem item in video) { - final int id = item.id!; - if (!idSet.contains(id)) { - idSet.add(id); - userfulQaSam++; + if (videoInfo.dash != null) { + // dash格式视频一次请求会返回所有可播放的清晰度video + final List video = videoInfo.dash!.video!; + final Set idSet = {}; + for (final VideoItem item in video) { + final int id = item.id!; + if (!idSet.contains(id)) { + idSet.add(id); + userfulQaSam++; + } } } + if (videoInfo.durl != null) { + // durl格式视频一次请求返回对应清晰度video + userfulQaSam = videoFormat.length - 1; + } + showModalBottomSheet( context: context, elevation: 0, @@ -707,7 +716,7 @@ class _HeaderControlState extends State { void showSetDecodeFormats() { // 当前选中的解码格式 final VideoDecodeFormats currentDecodeFormats = - widget.videoDetailCtr!.currentDecodeFormats; + widget.videoDetailCtr!.currentDecodeFormats!; final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo; // 当前视频可用的解码格式 final List videoFormat = videoInfo.supportFormats!; From 0312c8d01d6d8623de6f23c3fc9af8b58f2a81e3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 12:35:43 +0800 Subject: [PATCH 054/438] =?UTF-8?q?feat:=20=E6=98=BE=E7=A4=BA=E8=81=94?= =?UTF-8?q?=E5=90=88=E6=8A=95=E7=A8=BFup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 15 +-- lib/models/video_detail_res.dart | 45 +++++++ lib/pages/video/detail/introduction/view.dart | 111 +++++++++++++----- .../introduction/widgets/staff_up_item.dart | 74 ++++++++++++ 4 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 lib/pages/video/detail/introduction/widgets/staff_up_item.dart diff --git a/lib/http/video.dart b/lib/http/video.dart index d43656b2..bdfaedf7 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -192,22 +192,15 @@ class VideoHttp { // 视频信息 标题、简介 static Future videoIntro({required String bvid}) async { var res = await Request().get(Api.videoIntro, data: {'bvid': bvid}); - VideoDetailResponse result = VideoDetailResponse.fromJson(res.data); - if (result.code == 0) { + if (res.data['code'] == 0) { + VideoDetailResponse result = VideoDetailResponse.fromJson(res.data); return {'status': true, 'data': result.data!}; } else { - Map errMap = { - -400: '请求错误', - -403: '权限不足', - -404: '视频资源失效', - 62002: '稿件不可见', - 62004: '稿件审核中', - }; return { 'status': false, 'data': null, - 'code': result.code, - 'msg': errMap[result.code] ?? '请求异常', + 'code': res.data['code'], + 'msg': res.data['message'], }; } } diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index 38e0b877..a82b6fbb 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -67,6 +67,7 @@ class VideoDetailData { String? likeIcon; bool? needJumpBv; String? epId; + List? staff; VideoDetailData({ this.bvid, @@ -103,6 +104,7 @@ class VideoDetailData { this.likeIcon, this.needJumpBv, this.epId, + this.staff, }); VideoDetailData.fromJson(Map json) { @@ -155,6 +157,9 @@ class VideoDetailData { if (json['redirect_url'] != null) { epId = resolveEpId(json['redirect_url']); } + staff = json["staff"] != null + ? List.from(json["staff"]!.map((e) => Staff.fromJson(e))) + : null; } String resolveEpId(url) { @@ -652,3 +657,43 @@ class EpisodeItem { bvid = json['bvid']; } } + +class Staff { + Staff({ + this.mid, + this.title, + this.name, + this.face, + this.vip, + }); + + int? mid; + String? title; + String? name; + String? face; + int? status; + Vip? vip; + + Staff.fromJson(Map json) { + mid = json['mid']; + title = json['title']; + name = json['name']; + face = json['face']; + vip = Vip.fromJson(json['vip']); + } +} + +class Vip { + Vip({ + this.type, + this.status, + }); + + int? type; + int? status; + + Vip.fromJson(Map json) { + type = json['type']; + status = json['status']; + } +} diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a7eae6d2..597b6def 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -22,6 +22,7 @@ import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; import 'widgets/page_panel.dart'; import 'widgets/season_panel.dart'; +import 'widgets/staff_up_item.dart'; class VideoIntroPanel extends StatefulWidget { final String bvid; @@ -409,32 +410,35 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ) ], - GestureDetector( - onTap: onPushMember, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4), - child: Row( - children: [ - NetworkImgLayer( - type: 'avatar', - src: widget.videoDetail!.owner!.face, - width: 34, - height: 34, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 10), - Text(owner.name, style: const TextStyle(fontSize: 13)), - const SizedBox(width: 6), - Text( - follower, - style: TextStyle( - fontSize: t.textTheme.labelSmall!.fontSize, - color: outline, + if (widget.videoDetail!.staff == null) + GestureDetector( + onTap: onPushMember, + child: Container( + padding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 4), + child: Row( + children: [ + NetworkImgLayer( + type: 'avatar', + src: widget.videoDetail!.owner!.face, + width: 34, + height: 34, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, ), - ), - const Spacer(), - Obx(() => AnimatedOpacity( + const SizedBox(width: 10), + Text(owner.name, style: const TextStyle(fontSize: 13)), + const SizedBox(width: 6), + Text( + follower, + style: TextStyle( + fontSize: t.textTheme.labelSmall!.fontSize, + color: outline, + ), + ), + const Spacer(), + Obx( + () => AnimatedOpacity( opacity: videoIntroController.followStatus.isEmpty ? 0 : 1, duration: const Duration(milliseconds: 50), @@ -474,11 +478,58 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ), - )), - ], + ), + ), + ], + ), ), ), - ), + if (widget.videoDetail!.staff != null) ...[ + const SizedBox(height: 15), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + children: [ + TextSpan( + text: '创作团队', + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(fontWeight: FontWeight.bold), + ), + const WidgetSpan(child: SizedBox(width: 6)), + TextSpan( + text: '${widget.videoDetail!.staff!.length}人', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ) + ], + ), + ), + SizedBox( + height: 120, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + for (int i = 0; + i < widget.videoDetail!.staff!.length; + i++) ...[ + StaffUpItem(item: widget.videoDetail!.staff![i]) + ], + ], + ), + ), + ], + ), + ] ], )), ); @@ -545,4 +596,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); }); } + + // Widget StaffPanel(BuildContext context, videoIntroController) { + // return + // } } diff --git a/lib/pages/video/detail/introduction/widgets/staff_up_item.dart b/lib/pages/video/detail/introduction/widgets/staff_up_item.dart new file mode 100644 index 00000000..7b18d95d --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/staff_up_item.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/utils/utils.dart'; + +class StaffUpItem extends StatelessWidget { + final Staff item; + + const StaffUpItem({ + super.key, + required this.item, + }); + + @override + Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(item.mid); + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 15), + GestureDetector( + onTap: () => Get.toNamed( + '/member?mid=${item.mid}', + arguments: {'face': item.face, 'heroTag': heroTag}, + ), + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + src: item.face, + type: 'avatar', + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 4), + child: SizedBox( + width: 85, + child: Text( + item.name!, + overflow: TextOverflow.ellipsis, + softWrap: false, + textAlign: TextAlign.center, + style: TextStyle( + color: item.vip!.status == 1 + ? const Color.fromARGB(255, 251, 100, 163) + : null, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 4), + child: SizedBox( + width: 85, + child: Text( + item.title!, + overflow: TextOverflow.ellipsis, + softWrap: false, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: 12, + ), + ), + ), + ), + ], + ); + } +} From 29bdbbfe18d86fe3c5ee1755bb25902247525b89 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 15:32:05 +0800 Subject: [PATCH 055/438] mod: playCount calc --- lib/plugin/pl_player/controller.dart | 12 +++++------- lib/services/audio_handler.dart | 2 +- lib/services/audio_session.dart | 4 ++-- lib/services/shutdown_timer_service.dart | 18 +++++++++++------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index be72eccb..ca147fd1 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -101,7 +101,7 @@ class PlPlayerController { bool _isFirstTime = true; Timer? _timer; - late Timer? _timerForSeek; + Timer? _timerForSeek; Timer? _timerForVolume; Timer? _timerForShowingVolume; Timer? _timerForGettingVolume; @@ -335,8 +335,10 @@ class PlPlayerController { }) { // 如果实例尚未创建,则创建一个新实例 _instance ??= PlPlayerController._(); - _instance!._playerCount.value += 1; - _videoType.value = videoType; + if (videoType != 'none') { + _instance!._playerCount.value += 1; + _videoType.value = videoType; + } return _instance!; } @@ -1120,9 +1122,6 @@ class PlPlayerController { } Future dispose({String type = 'single'}) async { - print('dispose'); - print('dispose: ${playerCount.value}'); - // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { _playerCount.value -= 1; @@ -1132,7 +1131,6 @@ class PlPlayerController { } _playerCount.value = 0; try { - print('dispose dispose ---------'); _timer?.cancel(); _timerForVolume?.cancel(); _timerForGettingVolume?.cancel(); diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index bf98298b..b0ca8cd7 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(); + PlPlayerController player = PlPlayerController.getInstance(videoType: 'none'); VideoPlayerServiceHandler() { revalidateSetting(); diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index ea83a30a..53b497ae 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -18,7 +18,7 @@ class AudioSessionHandler { session.configure(const AudioSessionConfiguration.music()); session.interruptionEventStream.listen((event) { - final player = PlPlayerController.getInstance(); + final player = PlPlayerController.getInstance(videoType: 'none'); if (event.begin) { if (!player.playerStatus.playing) return; switch (event.type) { @@ -51,7 +51,7 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - final player = PlPlayerController.getInstance(); + final player = PlPlayerController.getInstance(videoType: 'none'); if (player.playerStatus.playing) { player.pause(); } diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart index aa9c5ceb..156b63c8 100644 --- a/lib/services/shutdown_timer_service.dart +++ b/lib/services/shutdown_timer_service.dart @@ -29,8 +29,8 @@ class ShutdownTimerService { return; } SmartDialog.showToast("设置 $scheduledExitInMinutes 分钟后定时关闭"); - _shutdownTimer = Timer(Duration(minutes: scheduledExitInMinutes), - () => _shutdownDecider()); + _shutdownTimer = Timer( + Duration(minutes: scheduledExitInMinutes), () => _shutdownDecider()); } void _showTimeUpButPauseDialog() { @@ -59,7 +59,7 @@ class ShutdownTimerService { // Start the 10-second timer to auto close the dialog _autoCloseDialogTimer?.cancel(); _autoCloseDialogTimer = Timer(const Duration(seconds: 10), () { - SmartDialog.dismiss();// Close the dialog + SmartDialog.dismiss(); // Close the dialog _executeShutdown(); }); return AlertDialog( @@ -88,7 +88,8 @@ class ShutdownTimerService { _showShutdownDialog(); return; } - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = + PlPlayerController.getInstance(videoType: 'none'); if (!exitApp && !waitForPlayingCompleted) { if (!plPlayerController.playerStatus.playing) { //仅提示用户 @@ -108,19 +109,22 @@ class ShutdownTimerService { //该方法依赖耦合实现,不够优雅 isWaiting = true; } - void handleWaitingFinished(){ - if(isWaiting){ + + void handleWaitingFinished() { + if (isWaiting) { _showShutdownDialog(); isWaiting = false; } } + void _executeShutdown() { if (exitApp) { //退出app exit(0); } else { //暂停播放 - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = + PlPlayerController.getInstance(videoType: 'none'); if (plPlayerController.playerStatus.playing) { plPlayerController.pause(); waitForPlayingCompleted = true; From 0e52e0288c0d9772df5615bf0121788a201cc7b7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 16:08:10 +0800 Subject: [PATCH 056/438] =?UTF-8?q?opt:=20=E4=BC=98=E5=8C=96=E6=8A=95?= =?UTF-8?q?=E5=B8=81Dialog=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 66 ++++++++----------- lib/pages/setting/widgets/select_dialog.dart | 2 +- .../video/detail/introduction/controller.dart | 64 +++++++----------- 3 files changed, 52 insertions(+), 80 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index b26028cf..575a77de 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -131,51 +131,37 @@ class BangumiIntroController extends GetxController { builder: (context) { return AlertDialog( title: const Text('选择投币个数'), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return Column( mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: 1, - title: const Text('1枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - RadioListTile( - value: 2, - title: const Text('2枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - ], + children: [1, 2] + .map( + (e) => RadioListTile( + value: e, + title: Text('$e枚'), + groupValue: _tempThemeValue, + onChanged: (value) async { + _tempThemeValue = value!; + setState(() {}); + var res = await VideoHttp.coinVideo( + bvid: bvid, multiply: _tempThemeValue); + if (res['status']) { + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + bangumiDetail.value.stat!['coins'] = + bangumiDetail.value.stat!['coins'] + + _tempThemeValue; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), ); }), - actions: [ - TextButton(onPressed: () => Get.back(), child: const Text('取消')), - TextButton( - onPressed: () async { - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功 👏'); - hasCoin.value = true; - bangumiDetail.value.stat!['coins'] = - bangumiDetail.value.stat!['coins'] + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, - child: const Text('确定'), - ) - ], ); }); } diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index 50229f9e..ab5f2ce4 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -29,7 +29,7 @@ class _SelectDialogState extends State> { return AlertDialog( title: Text(widget.title), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return SingleChildScrollView( child: Column( diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6469b5c5..8d602b83 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -219,50 +219,36 @@ class VideoIntroController extends GetxController { builder: (context) { return AlertDialog( title: const Text('选择投币个数'), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return Column( mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: 1, - title: const Text('1枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - RadioListTile( - value: 2, - title: const Text('2枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - ], + children: [1, 2] + .map( + (e) => RadioListTile( + value: e, + title: Text('$e枚'), + groupValue: _tempThemeValue, + onChanged: (value) async { + _tempThemeValue = value!; + setState(() {}); + var res = await VideoHttp.coinVideo( + bvid: bvid, multiply: _tempThemeValue); + if (res['status']) { + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + videoDetail.value.stat!.coin = + videoDetail.value.stat!.coin! + _tempThemeValue; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), ); }), - actions: [ - TextButton(onPressed: () => Get.back(), child: const Text('取消')), - TextButton( - onPressed: () async { - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功 👏'); - hasCoin.value = true; - videoDetail.value.stat!.coin = - videoDetail.value.stat!.coin! + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, - child: const Text('确定')) - ], ); }); } From f24291134c84c864b5047ed90f1d7a628c30722b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 17:44:19 +0800 Subject: [PATCH 057/438] =?UTF-8?q?feat:=20=E5=90=88=E9=9B=86=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=B1=95=E7=A4=BA=E3=80=81=E5=B0=81=E9=9D=A2=E5=88=87?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 44 ++++++++++++------- lib/models/model_hot_video_item.dart | 3 ++ lib/models/video_detail_res.dart | 6 +++ .../bangumi/introduction/controller.dart | 8 ++-- lib/pages/bangumi/introduction/view.dart | 4 +- lib/pages/bangumi/widgets/bangumi_panel.dart | 3 +- lib/pages/video/detail/controller.dart | 3 ++ .../video/detail/introduction/controller.dart | 12 +++-- lib/pages/video/detail/introduction/view.dart | 7 ++- .../introduction/widgets/page_panel.dart | 6 +-- .../introduction/widgets/season_panel.dart | 1 + lib/pages/video/detail/view.dart | 16 ++++--- 12 files changed, 78 insertions(+), 35 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index c64b58b6..54f14654 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../models/common/video_episode_type.dart'; @@ -44,25 +45,38 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return ListTile( + return InkWell( onTap: () { SmartDialog.showToast('切换至「$title」'); changeFucCall.call(episode, index); }, - dense: false, - leading: isCurrentIndex - ? Image.asset( - 'assets/images/live.gif', - color: primary, - height: 12, - ) - : null, - title: Text( - title, - style: TextStyle( - fontSize: 14, - color: isCurrentIndex ? primary : onSurface, - ), + child: Padding( + padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), + child: isFullScreen + ? Text( + title, + maxLines: 1, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex ? primary : onSurface, + ), + ) + : Row( + children: [ + NetworkImgLayer(width: 130, height: 75, src: episode.cover), + const SizedBox(width: 10), + Expanded( + child: Text( + title, + maxLines: 2, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex ? primary : onSurface, + ), + ), + ), + ], + ), ), ); } diff --git a/lib/models/model_hot_video_item.dart b/lib/models/model_hot_video_item.dart index db331a4c..ab7e6e04 100644 --- a/lib/models/model_hot_video_item.dart +++ b/lib/models/model_hot_video_item.dart @@ -23,6 +23,7 @@ class HotVideoItemModel { this.dimension, this.shortLinkV2, this.firstFrame, + this.cover, this.pubLocation, this.seasontype, this.isOgv, @@ -50,6 +51,7 @@ class HotVideoItemModel { Dimension? dimension; String? shortLinkV2; String? firstFrame; + String? cover; String? pubLocation; int? seasontype; bool? isOgv; @@ -77,6 +79,7 @@ class HotVideoItemModel { dimension = Dimension.fromMap(json['dimension']); shortLinkV2 = json["short_link_v2"]; firstFrame = json["first_frame"]; + cover = json["first_frame"]; pubLocation = json["pub_location"]; seasontype = json["seasontype"]; isOgv = json["isOgv"]; diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index a82b6fbb..b7b1b481 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -382,6 +382,7 @@ class Part { String? weblink; Dimension? dimension; String? firstFrame; + String? cover; Part({ this.cid, @@ -393,6 +394,7 @@ class Part { this.weblink, this.dimension, this.firstFrame, + this.cover, }); fromRawJson(String str) => Part.fromJson(json.decode(str)); @@ -411,6 +413,7 @@ class Part { ? null : Dimension.fromJson(json["dimension"]); firstFrame = json["first_frame"]; + cover = json["first_frame"]; } Map toJson() { @@ -634,6 +637,7 @@ class EpisodeItem { this.attribute, this.page, this.bvid, + this.cover, }); int? seasonId; int? sectionId; @@ -644,6 +648,7 @@ class EpisodeItem { int? attribute; Part? page; String? bvid; + String? cover; EpisodeItem.fromJson(Map json) { seasonId = json['season_id']; @@ -655,6 +660,7 @@ class EpisodeItem { attribute = json['attribute']; page = Part.fromJson(json['page']); bvid = json['bvid']; + cover = json['arc']['pic']; } } diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 575a77de..65cd5dd8 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -215,7 +215,7 @@ class BangumiIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid) async { + Future changeSeasonOrbangu(bvid, cid, aid, cover) async { // 重新获取视频资源 VideoDetailController videoDetailCtr = Get.find(tag: Get.arguments['heroTag']); @@ -223,6 +223,7 @@ class BangumiIntroController extends GetxController { videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.oid.value = aid; + videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { @@ -281,7 +282,8 @@ class BangumiIntroController extends GetxController { int cid = episodes[nextIndex].cid!; String bvid = episodes[nextIndex].bvid!; int aid = episodes[nextIndex].aid!; - changeSeasonOrbangu(bvid, cid, aid); + String cover = episodes[nextIndex].cover!; + changeSeasonOrbangu(bvid, cid, aid, cover); } // 播放器底栏 选集 回调 @@ -302,7 +304,7 @@ class BangumiIntroController extends GetxController { sheetHeight: Get.size.height, isFullScreen: true, changeFucCall: (item, index) { - changeSeasonOrbangu(item.bvid, item.cid, item.aid); + changeSeasonOrbangu(item.bvid, item.cid, item.aid, item.cover); SmartDialog.dismiss(); }, ).buildShowContent(Get.context!), diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index e47db480..6876da79 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -322,8 +322,8 @@ class _BangumiInfoState extends State { pages: widget.bangumiDetail!.episodes!, cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!, sheetHeight: sheetHeight, - changeFuc: (bvid, cid, aid) => - bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), + changeFuc: (bvid, cid, aid, cover) => bangumiIntroController + .changeSeasonOrbangu(bvid, cid, aid, cover), bangumiDetail: bangumiIntroController.bangumiDetail.value, bangumiIntroController: bangumiIntroController, ) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3e965f34..3df7ce25 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -84,11 +84,12 @@ class _BangumiPanelState extends State { item.bvid, item.cid, item.aid, + item.cover, ); if (_bottomSheetController != null) { _bottomSheetController?.close(); } - currentIndex = i; + currentIndex.value = i; scrollToIndex(); } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 3cc43005..5fecde8d 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -73,6 +73,7 @@ class VideoDetailController extends GetxController ReplyItemModel? firstFloor; final scaffoldKey = GlobalKey(); RxString bgCover = ''.obs; + RxString cover = ''.obs; PlPlayerController plPlayerController = PlPlayerController.getInstance(); late VideoItem firstVideo; @@ -120,10 +121,12 @@ class VideoDetailController extends GetxController var args = argMap['videoItem']; if (args.pic != null && args.pic != '') { videoItem['pic'] = args.pic; + cover.value = args.pic; } } if (keys.contains('pic')) { videoItem['pic'] = argMap['pic']; + cover.value = argMap['pic']; } } tabCtr = TabController(length: 2, vsync: this); diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 8d602b83..b9a3ef93 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -432,7 +432,7 @@ class VideoIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid) async { + Future changeSeasonOrbangu(bvid, cid, aid, cover) async { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); @@ -447,6 +447,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; + videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { @@ -494,6 +495,7 @@ class VideoIntroController extends GetxController { void nextPlay() { final List episodes = []; bool isPages = false; + late String cover; if (videoDetail.value.ugcSeason != null) { final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final List sections = ugcSeason.sections!; @@ -510,6 +512,7 @@ class VideoIntroController extends GetxController { final int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value); int nextIndex = currentIndex + 1; + cover = episodes[nextIndex].cover; final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; @@ -526,7 +529,7 @@ class VideoIntroController extends GetxController { final int cid = episodes[nextIndex].cid!; final String rBvid = isPages ? bvid : episodes[nextIndex].bvid; final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; - changeSeasonOrbangu(rBvid, cid, rAid); + changeSeasonOrbangu(rBvid, cid, rAid, cover); } // 设置关注分组 @@ -591,10 +594,11 @@ class VideoIntroController extends GetxController { isFullScreen: true, changeFucCall: (item, index) { if (dataType == VideoEpidoesType.videoEpisode) { - changeSeasonOrbangu(IdUtils.av2bv(item.aid), item.cid, item.aid); + changeSeasonOrbangu( + IdUtils.av2bv(item.aid), item.cid, item.aid, item.cover); } if (dataType == VideoEpidoesType.videoPart) { - changeSeasonOrbangu(bvid, item.cid, null); + changeSeasonOrbangu(bvid, item.cid, null, item.cover); } SmartDialog.dismiss(); }, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 597b6def..47404658 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -383,11 +383,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? videoIntroController.lastPlayCid.value : widget.videoDetail!.pages!.first.cid, sheetHeight: videoDetailCtr.sheetHeight.value, - changeFuc: (bvid, cid, aid) => + changeFuc: (bvid, cid, aid, cover) => videoIntroController.changeSeasonOrbangu( bvid, cid, aid, + cover, ), videoIntroCtr: videoIntroController, ), @@ -401,10 +402,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { pages: widget.videoDetail!.pages!, cid: videoIntroController.lastPlayCid.value, sheetHeight: videoDetailCtr.sheetHeight.value, - changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( + changeFuc: (cid, cover) => + videoIntroController.changeSeasonOrbangu( videoIntroController.bvid, cid, null, + cover, ), videoIntroCtr: videoIntroController, ), diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 83db2d52..266f5566 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -58,7 +58,7 @@ class _PagesPanelState extends State { } void changeFucCall(item, i) async { - widget.changeFuc?.call(item.cid); + widget.changeFuc?.call(item.cid, item.cover); currentIndex.value = i; _bottomSheetController?.close(); scrollToIndex(); @@ -129,7 +129,7 @@ class _PagesPanelState extends State { ), ), Container( - height: 35, + height: 55, margin: const EdgeInsets.only(bottom: 8), child: ListView.builder( scrollDirection: Axis.horizontal, @@ -163,7 +163,7 @@ class _PagesPanelState extends State { Expanded( child: Text( widget.pages[i].pagePart!, - maxLines: 1, + maxLines: 2, style: TextStyle( fontSize: 13, color: isCurrentIndex diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index 2afefcb4..fdfec6f9 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -67,6 +67,7 @@ class _SeasonPanelState extends State { IdUtils.av2bv(item.aid), item.cid, item.aid, + item.cover, ); currentIndex.value = i; _bottomSheetController?.close(); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0152a2cb..7f1d6039 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -458,11 +458,17 @@ class _VideoDetailPageState extends State onTap: () { handlePlay(); }, - child: NetworkImgLayer( - type: 'emote', - src: vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight.value, + child: Obx( + () => AnimatedOpacity( + duration: const Duration(milliseconds: 100), // 渐变动画的持续时间 + opacity: 1, // 设置不透明度 + child: NetworkImgLayer( + type: 'emote', + src: vdCtr.cover.value, + width: Get.width, + height: videoHeight.value, + ), + ), ), ), Positioned( From 257596cb9eb025ccc674c413c575e59fe4c0cf7e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 20:40:23 +0800 Subject: [PATCH 058/438] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=A1=AC=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 2 +- lib/pages/video/detail/controller.dart | 4 ++-- lib/plugin/pl_player/controller.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 07d736e3..4a8495e5 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -131,7 +131,7 @@ class _PlaySettingState extends State { title: '开启硬解', subTitle: '以较低功耗播放视频', setKey: SettingBoxKey.enableHA, - defaultVal: true, + defaultVal: false, ), const SetSwitchItem( title: '观看人数', diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4613c7ca..127e81a7 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -59,7 +59,7 @@ class VideoDetailController extends GetxController // 封面图的展示 RxBool isShowCover = true.obs; // 硬解 - RxBool enableHA = true.obs; + RxBool enableHA = false.obs; /// 本地存储 Box userInfoCache = GStrorage.userInfo; @@ -128,7 +128,7 @@ class VideoDetailController extends GetxController tabCtr = TabController(length: 2, vsync: this); autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); - enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true); + enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: false); enableRelatedVideo = setting.get(SettingBoxKey.enableRelatedVideo, defaultValue: true); if (userInfo == null || diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index be72eccb..a31ce0b8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -351,7 +351,7 @@ class PlPlayerController { // 初始化播放速度 double speed = 1.0, // 硬件加速 - bool enableHA = true, + bool enableHA = false, double? width, double? height, Duration? duration, From cbb7b90bf9ce8678fc2a9f585a5b4f8a3a21a016 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 22:11:56 +0800 Subject: [PATCH 059/438] mod --- lib/common/widgets/network_img_layer.dart | 1 - lib/pages/video/detail/widgets/header_control.dart | 2 +- lib/utils/utils.dart | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 173db853..bda8ee5c 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -36,7 +36,6 @@ class NetworkImgLayer extends StatelessWidget { final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; - // print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 6acd20d7..544d93e0 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -446,7 +446,7 @@ class _HeaderControlState extends State { children: [ RadioListTile( value: -1, - title: const Text('关闭弹幕'), + title: const Text('关闭字幕'), groupValue: tempThemeValue, onChanged: (value) { tempThemeValue = value!; diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 987f57c1..b40e9bfe 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -109,7 +109,6 @@ class Utils { toInt: false, formatType: formatType); } - print('distance: $distance'); if (distance <= 60) { return '刚刚'; } else if (distance <= 3600) { From aff39c291dc669158277301541cb98c9ed8ec0b0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 22:17:10 +0800 Subject: [PATCH 060/438] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86cover=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 46 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 54f14654..00233a07 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -45,25 +45,37 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return InkWell( - onTap: () { - SmartDialog.showToast('切换至「$title」'); - changeFucCall.call(episode, index); - }, - child: Padding( - padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), - child: isFullScreen - ? Text( - title, - maxLines: 1, + return isFullScreen || episode?.cover == null + ? ListTile( + onTap: () { + SmartDialog.showToast('切换至「$title」'); + changeFucCall.call(episode, index); + }, + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text(title, style: TextStyle( fontSize: 14, color: isCurrentIndex ? primary : onSurface, - ), - ) - : Row( + ))) + : InkWell( + onTap: () { + SmartDialog.showToast('切换至「$title」'); + changeFucCall.call(episode, index); + }, + child: Padding( + padding: + const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), + child: Row( children: [ - NetworkImgLayer(width: 130, height: 75, src: episode.cover), + NetworkImgLayer( + width: 130, height: 75, src: episode?.cover ?? ''), const SizedBox(width: 10), Expanded( child: Text( @@ -77,8 +89,8 @@ class EpisodeBottomSheet { ), ], ), - ), - ); + ), + ); } Widget buildTitle() { From c653a60f05501f983bb0d98c529fb240409bba6e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 00:06:08 +0800 Subject: [PATCH 061/438] =?UTF-8?q?feat:=20=E6=B4=BB=E5=8A=A8=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=8A=A8=E6=80=81=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/dynamics/result.dart | 3 + lib/pages/dynamics/widgets/forward_panel.dart | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 2f7c2d40..bc7105d1 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -414,6 +414,7 @@ class DynamicMajorModel { this.none, this.type, this.courses, + this.common, }); DynamicArchiveModel? archive; @@ -429,6 +430,7 @@ class DynamicMajorModel { // MAJOR_TYPE_OPUS 图文/文章 String? type; Map? courses; + Map? common; DynamicMajorModel.fromJson(Map json) { archive = json['archive'] != null @@ -452,6 +454,7 @@ class DynamicMajorModel { json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null; type = json['type']; courses = json['courses'] ?? {}; + common = json['common'] ?? {}; } } diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart index 5a8d9f17..76c7b24e 100644 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'additional_panel.dart'; @@ -182,6 +183,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { ) ], ); + // 活动 + case 'DYNAMIC_TYPE_COMMON_SQUARE': + return Padding( + padding: const EdgeInsets.only(top: 8), + child: InkWell( + onTap: () { + Get.toNamed('/webview', parameters: { + 'url': item.modules.moduleDynamic.major.common['jump_url'], + 'type': 'url', + 'pageTitle': item.modules.moduleDynamic.major.common['title'] + }); + }, + child: Container( + width: double.infinity, + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: Row( + children: [ + NetworkImgLayer( + width: 45, + height: 45, + src: item.modules.moduleDynamic.major.common['cover'], + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.modules.moduleDynamic.major.common['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + item.modules.moduleDynamic.major.common['desc'], + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ) + ], + ), + // TextButton(onPressed: () {}, child: Text('123')) + ), + ), + ); default: return const SizedBox( width: double.infinity, From 750e9d2b58635d70c0627d64adaf1ea5dd7621db Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 07:39:55 +0800 Subject: [PATCH 062/438] =?UTF-8?q?revert:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +++--- lib/pages/video/detail/controller.dart | 3 +-- lib/pages/video/detail/reply/view.dart | 8 ++++---- .../video/detail/reply/widgets/reply_item.dart | 6 ++---- lib/pages/video/detail/reply_reply/controller.dart | 10 +--------- lib/pages/video/detail/reply_reply/view.dart | 14 ++------------ 6 files changed, 13 insertions(+), 34 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 11636b5b..bd07921b 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: (replyItem) => + replyReply(replyItem), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c6c4bef6..8efc8ea1 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -165,7 +165,7 @@ class VideoDetailController extends GetxController getSubtitle(); } - showReplyReplyPanel(oid, fRpid, firstFloor, currentReply) { + showReplyReplyPanel(oid, fRpid, firstFloor) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( @@ -178,7 +178,6 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, - currentReply: currentReply, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 420f349c..4c533647 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -110,7 +110,7 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { @@ -118,7 +118,7 @@ class _VideoReplyPanelState extends State videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; videoDetailCtr.showReplyReplyPanel( - replyItem.oid, replyItem.rpid!, replyItem, currentReply); + replyItem.oid, replyItem.rpid!, replyItem); } } @@ -229,8 +229,8 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: (replyItem) => + replyReply(replyItem), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 8a8e4af7..ecc2dd02 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -531,10 +531,8 @@ InlineSpan buildContent( TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = () => replyReply?.call( - replyItem.root == 0 ? replyItem : fReplyItem, - replyItem, - ), + ..onTap = () => + replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), ), ); } diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 4c553e29..e94aaea5 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init', currentReply}) async { + Future queryReplyList({type = 'init'}) async { if (type == 'init') { currentPage = 0; } @@ -63,14 +63,6 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } - if (replyList.isNotEmpty && currentReply != null) { - int indexToRemove = - replyList.indexWhere((item) => currentReply.rpid == item.rpid); - // 如果找到了指定ID的项,则移除 - if (indexToRemove != -1) { - replyList.removeAt(indexToRemove); - } - } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 6441c600..3fe84c71 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,7 +20,6 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, - this.currentReply, super.key, }); final int? oid; @@ -30,7 +29,6 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; - final dynamic currentReply; @override State createState() => _VideoReplyReplyPanelState(); @@ -65,9 +63,7 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList( - currentReply: widget.currentReply, - ); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); } void replyReply(replyItem) {} @@ -111,9 +107,7 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList( - currentReply: widget.currentReply, - ); + return await _videoReplyReplyController.queryReplyList(); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -146,10 +140,6 @@ class _VideoReplyReplyPanelState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { - if (widget.currentReply != null) { - _videoReplyReplyController.replyList - .insert(0, widget.currentReply); - } // 请求成功 return Obx( () => SliverList( From 8a6b4f987f4aeabe91c0d2d98adc898be044521f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 22:03:19 +0800 Subject: [PATCH 063/438] revert: NavigationBar Color --- lib/main.dart | 59 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 3877685c..43bcf930 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -65,6 +65,20 @@ void main() async { }, ); + // 小白条、导航栏沉浸 + if (Platform.isAndroid) { + List versionParts = Platform.version.split('.'); + int androidVersion = int.parse(versionParts[0]); + if (androidVersion >= 29) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.transparent, + )); + } + Data.init(); GlobalData(); PiliSchame.init(); @@ -130,26 +144,33 @@ class MyApp extends StatelessWidget { ); } - ThemeData themeData = ThemeData( - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - ); + // ThemeData themeData = ThemeData( + // colorScheme: currentThemeValue == ThemeType.dark + // ? darkColorScheme + // : lightColorScheme, + // ); - // 小白条、导航栏沉浸 - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - systemNavigationBarColor: GlobalData().enableMYBar - ? const Color(0x00010000) - : themeData.canvasColor, - systemNavigationBarDividerColor: GlobalData().enableMYBar - ? const Color(0x00010000) - : themeData.canvasColor, - systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark - ? Brightness.light - : Brightness.dark, - statusBarColor: Colors.transparent, - )); + // // 小白条、导航栏沉浸 + // if (Platform.isAndroid) { + // List versionParts = Platform.version.split('.'); + // int androidVersion = int.parse(versionParts[0]); + // if (androidVersion >= 29) { + // SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + // } + // SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + // systemNavigationBarColor: GlobalData().enableMYBar + // ? const Color(0x00010000) + // : themeData.canvasColor, + // systemNavigationBarDividerColor: GlobalData().enableMYBar + // ? const Color(0x00010000) + // : themeData.canvasColor, + // systemNavigationBarIconBrightness: + // currentThemeValue == ThemeType.dark + // ? Brightness.light + // : Brightness.dark, + // statusBarColor: Colors.transparent, + // )); + // } // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; From bc4fb0e0ae40cb9a77ebe18893f8a4cbd34071c5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 22:27:14 +0800 Subject: [PATCH 064/438] fix: videoDetail cover null --- lib/pages/video/detail/controller.dart | 27 ++++++++++--------- .../video/detail/introduction/controller.dart | 1 + lib/utils/app_scheme.dart | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c6c4bef6..bb099255 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -115,20 +115,15 @@ class VideoDetailController extends GetxController super.onInit(); final Map argMap = Get.arguments; userInfo = userInfoCache.get('userInfoCache'); - var keys = argMap.keys.toList(); - if (keys.isNotEmpty) { - if (keys.contains('videoItem')) { - var args = argMap['videoItem']; - if (args.pic != null && args.pic != '') { - videoItem['pic'] = args.pic; - cover.value = args.pic; - } - } - if (keys.contains('pic')) { - videoItem['pic'] = argMap['pic']; - cover.value = argMap['pic']; - } + if (argMap.containsKey('videoItem')) { + var args = argMap['videoItem']; + updateCover(args.pic); } + + if (argMap.containsKey('pic')) { + updateCover(argMap['pic']); + } + tabCtr = TabController(length: 2, vsync: this); autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); @@ -551,4 +546,10 @@ class VideoDetailController extends GetxController }, ); } + + void updateCover(String? pic) { + if (pic != null && pic != '') { + cover.value = videoItem['pic'] = pic; + } + } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index b9a3ef93..2ced79f0 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -90,6 +90,7 @@ class VideoIntroController extends GetxController { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}']; + videoDetailCtr.cover.value = result['data'].pic ?? ''; // 获取到粉丝数再返回 await queryUserStat(); } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 9009df6f..69fa717d 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -117,7 +117,7 @@ class PiliSchame { // ignore: always_specify_types (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: { - 'pic': null, + 'pic': '', 'heroTag': heroTag, }), ); From 811487bdd42f696b7ee56c4dd8c43fed2c787217 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 23:03:33 +0800 Subject: [PATCH 065/438] =?UTF-8?q?fix:=20=E9=87=8D=E5=9B=9E=E5=89=8D?= =?UTF-8?q?=E5=8F=B0=E5=BC=B9=E5=B9=95=E5=A0=86=E7=A7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7f1d6039..199e6b88 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -38,7 +38,7 @@ class VideoDetailPage extends StatefulWidget { } class _VideoDetailPageState extends State - with TickerProviderStateMixin, RouteAware { + with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver { late VideoDetailController vdCtr; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); @@ -61,6 +61,8 @@ class _VideoDetailPageState extends State late bool autoPiP; late Floating floating; bool isShowing = true; + // 生命周期监听 + late final AppLifecycleListener _lifecycleListener; @override void initState() { @@ -96,6 +98,8 @@ class _VideoDetailPageState extends State floating = vdCtr.floating!; autoEnterPip(); } + WidgetsBinding.instance.addObserver(this); + lifecycleListener(); } // 获取视频资源,初始化播放器 @@ -219,6 +223,8 @@ class _VideoDetailPageState extends State floating.dispose(); } appbarStream.close(); + WidgetsBinding.instance.removeObserver(this); + _lifecycleListener.dispose(); super.dispose(); } @@ -281,6 +287,29 @@ class _VideoDetailPageState extends State } } + // 生命周期监听 + void lifecycleListener() { + _lifecycleListener = AppLifecycleListener( + // onResume: () => _handleTransition('resume'), + // 后台 + // onInactive: () => _handleTransition('inactive'), + // 在Android和iOS端不生效 + // onHide: () => _handleTransition('hide'), + onShow: () => _handleTransition('show'), + onPause: () => _handleTransition('pause'), + onRestart: () => _handleTransition('restart'), + onDetach: () => _handleTransition('detach'), + ); + } + + void _handleTransition(String name) { + switch (name) { + case 'show' || 'restart': + plPlayerController?.danmakuController?.clear(); + break; + } + } + @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); From 0bb26aa46757eec6b3e5855e2e90e5f0fe4d167b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 29 Apr 2024 13:16:25 +0800 Subject: [PATCH 066/438] fix: videoDetail cover null --- lib/common/pages_bottom_sheet.dart | 2 +- lib/models/video_detail_res.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 00233a07..2a56eb41 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -45,7 +45,7 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return isFullScreen || episode?.cover == null + return isFullScreen || episode?.cover == null || episode?.cover == '' ? ListTile( onTap: () { SmartDialog.showToast('切换至「$title」'); diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index b7b1b481..ae272375 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -412,8 +412,8 @@ class Part { dimension = json["dimension"] == null ? null : Dimension.fromJson(json["dimension"]); - firstFrame = json["first_frame"]; - cover = json["first_frame"]; + firstFrame = json["first_frame"] ?? ''; + cover = json["first_frame"] ?? ''; } Map toJson() { From 3c8a92209fa5b1725df883b9b9cc62a369708e76 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 29 Apr 2024 13:41:25 +0800 Subject: [PATCH 067/438] =?UTF-8?q?revert:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ecc2dd02..468d2541 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -427,7 +427,7 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem, null), + onTap: () => replyReply!(replyItem), child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), From bab0985e86f147087d8f0793ad5e9da08f67d0df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 15:49:00 +0800 Subject: [PATCH 068/438] =?UTF-8?q?mod:=20=E5=85=B3=E6=B3=A8=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 74 ++++++++----------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a7eae6d2..8a92e9be 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -134,7 +134,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late double sheetHeight; late final dynamic owner; late final dynamic follower; - late final dynamic followStatus; late int mid; late String memberHeroTag; late bool enableAi; @@ -163,7 +162,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { owner = widget.videoDetail!.owner; follower = Utils.numFormat(videoIntroController.userStat['follower']); - followStatus = videoIntroController.followStatus; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); } @@ -434,47 +432,39 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), const Spacer(), - Obx(() => AnimatedOpacity( - opacity: - videoIntroController.followStatus.isEmpty ? 0 : 1, - duration: const Duration(milliseconds: 50), - child: SizedBox( - height: 32, - child: Obx( - () => videoIntroController.followStatus.isNotEmpty - ? TextButton( - onPressed: - videoIntroController.actionRelationMod, - style: TextButton.styleFrom( - padding: const EdgeInsets.only( - left: 8, right: 8), - foregroundColor: - followStatus['attribute'] != 0 - ? outline - : t.colorScheme.onPrimary, - backgroundColor: - followStatus['attribute'] != 0 - ? t.colorScheme.onInverseSurface - : t.colorScheme - .primary, // 设置按钮背景色 - ), - child: Text( - followStatus['attribute'] != 0 - ? '已关注' - : '关注', - style: TextStyle( - fontSize: t - .textTheme.labelMedium!.fontSize), - ), - ) - : ElevatedButton( - onPressed: - videoIntroController.actionRelationMod, - child: const Text('关注'), + Obx( + () { + final bool isFollowed = + videoIntroController.followStatus['attribute'] != 0; + return videoIntroController.followStatus.isEmpty + ? const SizedBox() + : SizedBox( + height: 32, + child: TextButton( + onPressed: + videoIntroController.actionRelationMod, + style: TextButton.styleFrom( + padding: const EdgeInsets.only( + left: 8, + right: 8, ), - ), - ), - )), + foregroundColor: isFollowed + ? outline + : t.colorScheme.onPrimary, + backgroundColor: isFollowed + ? t.colorScheme.onInverseSurface + : t.colorScheme.primary, // 设置按钮背景色 + ), + child: Text( + isFollowed ? '已关注' : '关注', + style: TextStyle( + fontSize: t.textTheme.labelMedium!.fontSize, + ), + ), + ), + ); + }, + ) ], ), ), From ba1a5486bfd259651b230f79e9d6963caf0d2f06 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 22:40:22 +0800 Subject: [PATCH 069/438] =?UTF-8?q?opt:=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=97=B6=E8=AE=B0=E5=BF=86=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 14 ++++++++------ pubspec.lock | 27 +++++++++++++++------------ pubspec.yaml | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 8ee6faa0..4b6c44a1 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -395,7 +395,7 @@ class PlPlayerController { } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( - dataSource, _looping, enableHA, width, height); + dataSource, _looping, enableHA, width, height, seekTo); // 获取视频时长 00:00 _duration.value = duration ?? _videoPlayerController!.state.duration; updateDurationSecond(); @@ -426,6 +426,7 @@ class PlPlayerController { bool enableHA, double? width, double? height, + Duration seekTo, ) async { // 每次配置时先移除监听 removeListeners(); @@ -507,8 +508,9 @@ class PlPlayerController { play: false, ); } - player.open( - Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), + await player.open( + Media(dataSource.videoSource!, + httpHeaders: dataSource.httpHeaders, start: seekTo), play: false, ); // 音轨 @@ -530,9 +532,9 @@ class PlPlayerController { // } /// 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } + // if (seekTo != Duration.zero) { + // await this.seekTo(seekTo); + // } /// 自动播放 if (_autoPlay) { diff --git a/pubspec.lock b/pubspec.lock index a64c85c0..46d1e646 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -873,10 +873,11 @@ packages: media_kit: dependency: "direct main" description: - name: media_kit - sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive @@ -913,10 +914,11 @@ packages: media_kit_libs_video: dependency: "direct main" description: - name: media_kit_libs_video - sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" - url: "https://pub.flutter-io.cn" - source: hosted + path: "libs/universal/media_kit_libs_video" + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.0.4" media_kit_libs_windows_video: dependency: transitive @@ -937,10 +939,11 @@ packages: media_kit_video: dependency: "direct main" description: - name: media_kit_video - sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit_video + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.2.4" meta: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 27b8b720..5ea6386e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -165,6 +165,20 @@ dev_dependencies: hive_generator: ^2.0.0 build_runner: ^2.4.8 +dependency_overrides: + media_kit: + git: + url: https://github.com/media-kit/media-kit + path: media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit + path: media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit + path: libs/universal/media_kit_libs_video + flutter_launcher_icons: android: true ios: true From 68c73fde5655dfdac36ad311ea08955462dfc3a8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 22:47:02 +0800 Subject: [PATCH 070/438] fix: videoDetail info copy --- lib/pages/video/detail/introduction/view.dart | 7 ++ .../introduction/widgets/intro_detail.dart | 82 ++++++++++--------- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 168e6ea7..9103c130 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,4 +1,5 @@ import 'package:expandable/expandable.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -265,6 +266,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => showIntroDetail(), + onLongPress: () async { + feedBack(); + await Clipboard.setData( + ClipboardData(text: widget.videoDetail!.title!)); + SmartDialog.showToast('标题已复制'); + }, child: ExpandablePanel( controller: _expandableCtr, collapsed: Text( diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 3ec92023..78c621c0 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/utils.dart'; class IntroDetail extends StatelessWidget { @@ -16,44 +17,47 @@ class IntroDetail extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: double.infinity, - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 4), - Row( - children: [ - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.bvid!, - style: TextStyle( - fontSize: 13, - color: Theme.of(context).colorScheme.primary), - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + Row( + children: [ + GestureDetector( + onTap: () { + feedBack(); + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), ), - const SizedBox(width: 10), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.aid!.toString(), - style: TextStyle( - fontSize: 13, - color: Theme.of(context).colorScheme.primary), - ), - ) - ], - ), - const SizedBox(height: 4), - Text.rich( + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + feedBack(); + Clipboard.setData( + ClipboardData(text: videoDetail!.aid!.toString())); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.aid!.toString(), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ) + ], + ), + const SizedBox(height: 4), + SelectableRegion( + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), + child: Text.rich( style: const TextStyle(height: 1.4), TextSpan( children: [ @@ -61,8 +65,8 @@ class IntroDetail extends StatelessWidget { ], ), ), - ], - ), + ), + ], ), ); } From 389747d6f47b4b7a4e76049d1db1ec5c35ae9910 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 23:30:28 +0800 Subject: [PATCH 071/438] =?UTF-8?q?v1.0.22=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.22.0430.md | 27 +++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.22.0430.md diff --git a/change_log/1.0.22.0430.md b/change_log/1.0.22.0430.md new file mode 100644 index 00000000..29f8aecf --- /dev/null +++ b/change_log/1.0.22.0430.md @@ -0,0 +1,27 @@ +## 1.0.22 + +### 功能 ++ 字幕 ++ 全屏时选集 ++ 动态转发 ++ 评论视频并转发 ++ 收藏夹删除 ++ 合集显示封面 ++ 底部导航栏编辑、排序功能 ++ 历史记录进度条展示 ++ 直播画质切换 ++ 排行榜功能 ++ 视频详情页推荐视频开关 ++ 显示联合投稿up + +### 修复 ++ 收藏夹个数错误 ++ 封面保存权限问题 ++ 合集最后1p未展示 ++ up主页关注按钮触发灰屏 + +### 优化 ++ 视频简介查看逻辑 + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 5ea6386e..7cfbbca8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.21+1021 +version: 1.0.22+1022 environment: sdk: ">=3.0.0 <4.0.0" From 7dbd832a8077d6abf3106a91bfc32355aa394eb3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 19:46:27 +0800 Subject: [PATCH 072/438] =?UTF-8?q?feat:=20=E9=95=BF=E6=8C=89=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=B0=81=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/Info.plist | 2 + lib/common/widgets/overlay_pop.dart | 87 ------- lib/common/widgets/video_card_h.dart | 219 +++++++++--------- lib/common/widgets/video_card_v.dart | 15 +- lib/models/bangumi/list.dart | 3 + lib/models/search/result.dart | 24 +- lib/pages/bangumi/widgets/bangumu_card_v.dart | 170 ++++++-------- .../fav_detail/widget/fav_video_card.dart | 7 + lib/pages/hot/view.dart | 21 -- lib/pages/later/view.dart | 2 +- lib/pages/live/view.dart | 20 -- lib/pages/live/widgets/live_item.dart | 111 ++++----- lib/pages/member_seasons/widgets/item.dart | 7 + lib/pages/rank/zone/view.dart | 21 -- .../search_panel/widgets/live_panel.dart | 16 +- .../widgets/media_bangumi_panel.dart | 2 +- .../search_panel/widgets/video_panel.dart | 6 +- .../widget/sub_video_card.dart | 7 + lib/pages/video/detail/related/view.dart | 27 --- lib/utils/download.dart | 90 ++++--- lib/utils/image_save.dart | 88 +++++++ 21 files changed, 430 insertions(+), 515 deletions(-) delete mode 100644 lib/common/widgets/overlay_pop.dart create mode 100644 lib/utils/image_save.dart diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 65906625..1e7d9fed 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -49,6 +49,8 @@ NSPhotoLibraryAddUsageDescription 请允许APP保存图片到相册 + NSPhotoLibraryUsageDescription + 请允许APP保存图片到相册 NSCameraUsageDescription App需要您的同意,才能访问相册 NSAppleMusicUsageDescription diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart deleted file mode 100644 index 4f0a3899..00000000 --- a/lib/common/widgets/overlay_pop.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../utils/download.dart'; -import '../constants.dart'; -import 'network_img_layer.dart'; - -class OverlayPop extends StatelessWidget { - const OverlayPop({super.key, this.videoItem, this.closeFn}); - - final dynamic videoItem; - final Function? closeFn; - - @override - Widget build(BuildContext context) { - final double imgWidth = MediaQuery.sizeOf(context).width - 8 * 2; - return Container( - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.circular(10.0), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - NetworkImgLayer( - width: imgWidth, - height: imgWidth / StyleString.aspectRatio, - src: videoItem.pic! as String, - quality: 100, - ), - Positioned( - right: 8, - top: 8, - child: Container( - width: 30, - height: 30, - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.3), - borderRadius: - const BorderRadius.all(Radius.circular(20))), - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => closeFn!(), - icon: const Icon( - Icons.close, - size: 18, - color: Colors.white, - ), - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.fromLTRB(12, 10, 8, 10), - child: Row( - children: [ - Expanded( - child: Text( - videoItem.title! as String, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - const SizedBox(width: 4), - IconButton( - tooltip: '保存封面图', - onPressed: () async { - await DownloadUtils.downloadImg( - videoItem.pic != null - ? videoItem.pic as String - : videoItem.cover as String, - ); - // closeFn!(); - }, - icon: const Icon(Icons.download, size: 20), - ) - ], - )), - ], - ), - ); - } -} diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 25e701ac..df0c29b7 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/image_save.dart'; import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; @@ -16,8 +17,7 @@ class VideoCardH extends StatelessWidget { const VideoCardH({ super.key, required this.videoItem, - this.longPress, - this.longPressEnd, + this.onPressedFn, this.source = 'normal', this.showOwner = true, this.showView = true, @@ -27,8 +27,8 @@ class VideoCardH extends StatelessWidget { }); // ignore: prefer_typing_uninitialized_variables final videoItem; - final Function()? longPress; - final Function()? longPressEnd; + final Function()? onPressedFn; + // normal 推荐, later 稍后再看, search 搜索 final String source; final bool showOwner; final bool showView; @@ -45,109 +45,103 @@ class VideoCardH extends StatelessWidget { type = videoItem.type; } catch (_) {} final String heroTag = Utils.makeHeroTag(aid); - return GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); + return InkWell( + onTap: () async { + try { + if (type == 'ketang') { + SmartDialog.showToast('课堂视频暂不支持播放'); + return; + } + final int cid = + videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); + Get.toNamed('/video?bvid=$bvid&cid=$cid', + arguments: {'videoItem': videoItem, 'heroTag': heroTag}); + } catch (err) { + SmartDialog.showToast(err.toString()); } }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - try { - if (type == 'ketang') { - SmartDialog.showToast('课堂视频暂不支持播放'); - return; - } - final int cid = - videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); - Get.toNamed('/video?bvid=$bvid&cid=$cid', - arguments: {'videoItem': videoItem, 'heroTag': heroTag}); - } catch (err) { - SmartDialog.showToast(err.toString()); - } - }, - child: Padding( - padding: const EdgeInsets.fromLTRB( - StyleString.safeSpace, 5, StyleString.safeSpace, 5), - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints boxConstraints) { - final double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(1.0)) / - 2; - return Container( - constraints: const BoxConstraints(minHeight: 88), - height: width / StyleString.aspectRatio, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic as String, - width: maxWidth, - height: maxHeight, - ), + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints boxConstraints) { + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; + return Container( + constraints: const BoxConstraints(minHeight: 88), + height: width / StyleString.aspectRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints boxConstraints) { + final double maxWidth = boxConstraints.maxWidth; + final double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic as String, + width: maxWidth, + height: maxHeight, ), - if (videoItem.duration != 0) - PBadge( - text: Utils.timeFormat(videoItem.duration!), - right: 6.0, - bottom: 6.0, - type: 'gray', - ), - if (type != 'video') - PBadge( - text: type, - left: 6.0, - bottom: 6.0, - type: 'primary', - ), - // if (videoItem.rcmdReason != null && - // videoItem.rcmdReason.content != '') - // pBadge(videoItem.rcmdReason.content, context, - // 6.0, 6.0, null, null), - if (showCharge && videoItem?.isChargingSrc) - const PBadge( - text: '充电专属', - right: 6.0, - top: 6.0, - type: 'primary', - ), - ], - ); - }, - ), + ), + if (videoItem.duration != 0) + PBadge( + text: Utils.timeFormat(videoItem.duration!), + right: 6.0, + bottom: 6.0, + type: 'gray', + ), + if (type != 'video') + PBadge( + text: type, + left: 6.0, + bottom: 6.0, + type: 'primary', + ), + // if (videoItem.rcmdReason != null && + // videoItem.rcmdReason.content != '') + // pBadge(videoItem.rcmdReason.content, context, + // 6.0, 6.0, null, null), + if (showCharge && videoItem?.isChargingSrc) + const PBadge( + text: '充电专属', + right: 6.0, + top: 6.0, + type: 'primary', + ), + ], + ); + }, ), - VideoContent( - videoItem: videoItem, - source: source, - showOwner: showOwner, - showView: showView, - showDanmaku: showDanmaku, - showPubdate: showPubdate, - ) - ], - ), - ); - }, - ), + ), + VideoContent( + videoItem: videoItem, + source: source, + showOwner: showOwner, + showView: showView, + showDanmaku: showDanmaku, + showPubdate: showPubdate, + onPressedFn: onPressedFn, + ) + ], + ), + ); + }, ), ), ); @@ -162,6 +156,7 @@ class VideoContent extends StatelessWidget { final bool showView; final bool showDanmaku; final bool showPubdate; + final Function()? onPressedFn; const VideoContent({ super.key, @@ -171,6 +166,7 @@ class VideoContent extends StatelessWidget { this.showView = true, this.showDanmaku = true, this.showPubdate = false, + this.onPressedFn, }); @override @@ -181,7 +177,7 @@ class VideoContent extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (videoItem.title is String) ...[ + if (source == 'normal' || source == 'later') ...[ Text( videoItem.title as String, textAlign: TextAlign.start, @@ -196,7 +192,7 @@ class VideoContent extends StatelessWidget { maxLines: 2, text: TextSpan( children: [ - for (final i in videoItem.title) ...[ + for (final i in videoItem.titleList) ...[ TextSpan( text: i['text'] as String, style: TextStyle( @@ -374,6 +370,19 @@ class VideoContent extends StatelessWidget { ], ), ), + if (source == 'later') ...[ + IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => onPressedFn?.call(), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ) + ], ], ), ], diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 9916aa7a..6a97d7e7 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/image_save.dart'; import '../../models/model_rec_video_item.dart'; -import 'overlay_pop.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -127,14 +127,11 @@ class VideoCardV extends StatelessWidget { String heroTag = Utils.makeHeroTag(videoItem.id); return InkWell( onTap: () async => onPushDetail(heroTag), - onLongPress: () { - SmartDialog.show( - builder: (context) => OverlayPop( - videoItem: videoItem, - closeFn: () => SmartDialog.dismiss(), - ), - ); - }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), borderRadius: BorderRadius.circular(16), child: Column( children: [ diff --git a/lib/models/bangumi/list.dart b/lib/models/bangumi/list.dart index c15014d0..fe71bb61 100644 --- a/lib/models/bangumi/list.dart +++ b/lib/models/bangumi/list.dart @@ -30,6 +30,7 @@ class BangumiListItemModel { BangumiListItemModel({ this.badge, this.badgeType, + this.pic, this.cover, // this.firstEp, this.indexShow, @@ -50,6 +51,7 @@ class BangumiListItemModel { String? badge; int? badgeType; + String? pic; String? cover; String? indexShow; int? isFinish; @@ -70,6 +72,7 @@ class BangumiListItemModel { BangumiListItemModel.fromJson(Map json) { badge = json['badge'] == '' ? null : json['badge']; badgeType = json['badge_type']; + pic = json['cover']; cover = json['cover']; indexShow = json['index_show']; isFinish = json['is_finish']; diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 418fb99d..81917b72 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -25,6 +25,7 @@ class SearchVideoItemModel { this.aid, this.bvid, this.title, + this.titleList, this.description, this.pic, // this.play, @@ -54,8 +55,8 @@ class SearchVideoItemModel { String? arcurl; int? aid; String? bvid; - List? title; - // List? titleList; + String? title; + List? titleList; String? description; String? pic; // String? play; @@ -82,8 +83,9 @@ class SearchVideoItemModel { aid = json['aid']; bvid = json['bvid']; mid = json['mid']; - // title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + // title = Em.regTitle(json['title']); + titleList = Em.regTitle(json['title']); description = json['description']; pic = json['pic'] != null && json['pic'].startsWith('//') ? 'https:${json['pic']}' @@ -232,6 +234,7 @@ class SearchLiveItemModel { this.userCover, this.type, this.title, + this.titleList, this.cover, this.pic, this.online, @@ -251,7 +254,8 @@ class SearchLiveItemModel { String? face; String? userCover; String? type; - List? title; + String? title; + List? titleList; String? cover; String? pic; int? online; @@ -272,7 +276,8 @@ class SearchLiveItemModel { face = json['uface']; userCover = json['user_cover']; type = json['type']; - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + titleList = Em.regTitle(json['title']); cover = json['cover']; pic = json['cover']; online = json['online']; @@ -302,6 +307,7 @@ class SearchMBangumiItemModel { this.type, this.mediaId, this.title, + this.titleList, this.orgTitle, this.mediaType, this.cv, @@ -328,7 +334,8 @@ class SearchMBangumiItemModel { String? type; int? mediaId; - List? title; + String? title; + List? titleList; String? orgTitle; int? mediaType; String? cv; @@ -355,7 +362,8 @@ class SearchMBangumiItemModel { SearchMBangumiItemModel.fromJson(Map json) { type = json['type']; mediaId = json['media_id']; - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + titleList = Em.regTitle(json['title']); orgTitle = json['org_title']; mediaType = json['media_type']; cv = json['cv']; diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index c1233ddf..3c8f6d2a 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -5,7 +5,9 @@ import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/bangumi/info.dart'; +import 'package:pilipala/models/bangumi/list.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -14,109 +16,87 @@ class BangumiCardV extends StatelessWidget { const BangumiCardV({ super.key, required this.bangumiItem, - this.longPress, - this.longPressEnd, }); - final bangumiItem; - final Function()? longPress; - final Function()? longPressEnd; + final BangumiListItemModel bangumiItem; @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(bangumiItem.mediaId); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - // onLongPress: () { - // if (longPress != null) { - // longPress!(); - // } - // }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - final int seasonId = bangumiItem.seasonId; - SmartDialog.showLoading(msg: '获取中...'); - final res = await SearchHttp.bangumiInfo(seasonId: seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - if (res['data'].episodes.isEmpty) { - SmartDialog.showToast('资源加载失败'); - return; - } - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, + return InkWell( + onTap: () async { + final int seasonId = bangumiItem.seasonId!; + SmartDialog.showLoading(msg: '获取中...'); + final res = await SearchHttp.bangumiInfo(seasonId: seasonId); + SmartDialog.dismiss().then((value) { + if (res['status']) { + if (res['data'].episodes.isEmpty) { + SmartDialog.showToast('资源加载失败'); + return; + } + EpisodeItem episode = res['data'].episodes.first; + String bvid = episode.bvid!; + int cid = episode.cid!; + String pic = episode.cover!; + String heroTag = Utils.makeHeroTag(cid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', + arguments: { + 'pic': pic, + 'heroTag': heroTag, + 'videoType': SearchType.media_bangumi, + 'bangumiItem': res['data'], + }, + ); + } + }); + }, + onLongPress: () => + imageSaveDialog(context, bangumiItem, SmartDialog.dismiss), + child: Column( + children: [ + ClipRRect( + borderRadius: const BorderRadius.all( + StyleString.imgRadius, + ), + child: AspectRatio( + aspectRatio: 0.65, + child: LayoutBuilder(builder: (context, boxConstraints) { + final double maxWidth = boxConstraints.maxWidth; + final double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: bangumiItem.cover, + width: maxWidth, + height: maxHeight, + ), + ), + if (bangumiItem.badge != null) + PBadge( + text: bangumiItem.badge, + top: 6, + right: 6, + bottom: null, + left: null), + if (bangumiItem.order != null) + PBadge( + text: bangumiItem.order, + top: null, + right: null, + bottom: 6, + left: 6, + type: 'gray', + ), + ], ); - } - }); - }, - child: Column( - children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: StyleString.imgRadius, - topRight: StyleString.imgRadius, - bottomLeft: StyleString.imgRadius, - bottomRight: StyleString.imgRadius, - ), - child: AspectRatio( - aspectRatio: 0.65, - child: LayoutBuilder(builder: (context, boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: bangumiItem.cover, - width: maxWidth, - height: maxHeight, - ), - ), - if (bangumiItem.badge != null) - PBadge( - text: bangumiItem.badge, - top: 6, - right: 6, - bottom: null, - left: null), - if (bangumiItem.order != null) - PBadge( - text: bangumiItem.order, - top: null, - right: null, - bottom: 6, - left: 6, - type: 'gray', - ), - ], - ); - }), - ), - ), - BangumiContent(bangumiItem: bangumiItem) - ], + }), + ), ), - ), + BangumiContent(bangumiItem: bangumiItem) + ], ), ); } diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 1c4008ff..79e5c073 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; @@ -7,6 +8,7 @@ import 'package:pilipala/http/search.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import '../../../common/widgets/badge.dart'; @@ -61,6 +63,11 @@ class FavVideoCardH extends StatelessWidget { epId != null ? SearchType.media_bangumi : SearchType.video, }); }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), child: Column( children: [ Padding( diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index e2e20e73..80d08e7b 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -3,8 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; @@ -78,15 +76,6 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { return VideoCardH( videoItem: _hotController.videoList[index], showPubdate: true, - longPress: () { - _hotController.popupDialog = _createPopupDialog( - _hotController.videoList[index]); - Overlay.of(context) - .insert(_hotController.popupDialog!); - }, - longPressEnd: () { - _hotController.popupDialog?.remove(); - }, ); }, childCount: _hotController.videoList.length), ), @@ -122,14 +111,4 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { ), ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _hotController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _hotController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index d4695154..7c6e158d 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -84,7 +84,7 @@ class _LaterPageState extends State { return VideoCardH( videoItem: videoItem, source: 'later', - longPress: () => _laterController.toViewDel( + onPressedFn: () => _laterController.toViewDel( aid: videoItem.aid)); }, childCount: _laterController.laterList.length), ) diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index c61d20b3..83605495 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; @@ -112,16 +110,6 @@ class _LivePageState extends State ); } - OverlayEntry _createPopupDialog(liveItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _liveController.popupDialog?.remove, - child: OverlayPop( - videoItem: liveItem, closeFn: _liveController.popupDialog?.remove), - ), - ); - } - Widget contentGrid(ctr, liveList) { // double maxWidth = Get.size.width; // int baseWidth = 500; @@ -152,14 +140,6 @@ class _LivePageState extends State ? LiveCardV( liveItem: liveList[index], crossAxisCount: crossAxisCount, - longPress: () { - _liveController.popupDialog = - _createPopupDialog(liveList[index]); - Overlay.of(context).insert(_liveController.popupDialog!); - }, - longPressEnd: () { - _liveController.popupDialog?.remove(); - }, ) : const VideoCardVSkeleton(); }, diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index 9218d4fb..f70ba82b 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/models/live/item.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -9,81 +11,66 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; class LiveCardV extends StatelessWidget { final LiveItemModel liveItem; final int crossAxisCount; - final Function()? longPress; - final Function()? longPressEnd; const LiveCardV({ Key? key, required this.liveItem, required this.crossAxisCount, - this.longPress, - this.longPressEnd, }) : super(key: key); @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(liveItem.roomId); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', - arguments: {'liveItem': liveItem, 'heroTag': heroTag}); - }, - child: Column( - children: [ - ClipRRect( - borderRadius: const BorderRadius.all(StyleString.imgRadius), - child: AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: liveItem.cover!, - width: maxWidth, - height: maxHeight, + return InkWell( + onLongPress: () => imageSaveDialog( + context, + liveItem, + SmartDialog.dismiss, + ), + borderRadius: BorderRadius.circular(16), + onTap: () async { + Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', + arguments: {'liveItem': liveItem, 'heroTag': heroTag}); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: const BorderRadius.all(StyleString.imgRadius), + child: AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: liveItem.cover!, + width: maxWidth, + height: maxHeight, + ), + ), + if (crossAxisCount != 1) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: AnimatedOpacity( + opacity: 1, + duration: const Duration(milliseconds: 200), + child: VideoStat( + liveItem: liveItem, ), ), - if (crossAxisCount != 1) - Positioned( - left: 0, - right: 0, - bottom: 0, - child: AnimatedOpacity( - opacity: 1, - duration: const Duration(milliseconds: 200), - child: VideoStat( - liveItem: liveItem, - ), - ), - ), - ], - ); - }), - ), - ), - LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount) - ], + ), + ], + ); + }), + ), ), - ), + LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount) + ], ), ); } diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 4df74b70..85b763b7 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/http/search.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; class MemberSeasonsItem extends StatelessWidget { @@ -29,6 +31,11 @@ class MemberSeasonsItem extends StatelessWidget { Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); }, + onLongPress: () => imageSaveDialog( + context, + seasonItem, + SmartDialog.dismiss, + ), child: Column( children: [ AspectRatio( diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 04631a8c..72d81f95 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -3,8 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; @@ -82,15 +80,6 @@ class _ZonePageState extends State return VideoCardH( videoItem: _zoneController.videoList[index], showPubdate: true, - longPress: () { - _zoneController.popupDialog = _createPopupDialog( - _zoneController.videoList[index]); - Overlay.of(context) - .insert(_zoneController.popupDialog!); - }, - longPressEnd: () { - _zoneController.popupDialog?.remove(); - }, ); }, childCount: _zoneController.videoList.length), ), @@ -126,14 +115,4 @@ class _ZonePageState extends State ), ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _zoneController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/pages/search_panel/widgets/live_panel.dart b/lib/pages/search_panel/widgets/live_panel.dart index 6fb5f5b8..5f797f09 100644 --- a/lib/pages/search_panel/widgets/live_panel.dart +++ b/lib/pages/search_panel/widgets/live_panel.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; Widget searchLivePanel(BuildContext context, ctr, list) { @@ -42,15 +44,15 @@ class LiveItem extends StatelessWidget { Get.toNamed('/liveRoom?roomid=${liveItem.roomid}', arguments: {'liveItem': liveItem, 'heroTag': heroTag}); }, + onLongPress: () => imageSaveDialog( + context, + liveItem, + SmartDialog.dismiss, + ), child: Column( children: [ ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: StyleString.imgRadius, - topRight: StyleString.imgRadius, - bottomLeft: StyleString.imgRadius, - bottomRight: StyleString.imgRadius, - ), + borderRadius: const BorderRadius.all(StyleString.imgRadius), child: AspectRatio( aspectRatio: StyleString.aspectRatio, child: LayoutBuilder(builder: (context, boxConstraints) { @@ -108,7 +110,7 @@ class LiveContent extends StatelessWidget { RichText( text: TextSpan( children: [ - for (var i in liveItem.title) ...[ + for (var i in liveItem.titleList) ...[ TextSpan( text: i['text'], style: TextStyle( diff --git a/lib/pages/search_panel/widgets/media_bangumi_panel.dart b/lib/pages/search_panel/widgets/media_bangumi_panel.dart index 18799d3a..7d88b183 100644 --- a/lib/pages/search_panel/widgets/media_bangumi_panel.dart +++ b/lib/pages/search_panel/widgets/media_bangumi_panel.dart @@ -63,7 +63,7 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { style: TextStyle( color: Theme.of(context).colorScheme.onSurface), children: [ - for (var i in i.title) ...[ + for (var i in i.titleList) ...[ TextSpan( text: i['text'], style: TextStyle( diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index b96ff004..c24a007c 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -35,7 +35,11 @@ class SearchVideoPanel extends StatelessWidget { padding: index == 0 ? const EdgeInsets.only(top: 2) : EdgeInsets.zero, - child: VideoCardH(videoItem: i, showPubdate: true), + child: VideoCardH( + videoItem: i, + showPubdate: true, + source: 'search', + ), ); }, ), diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart index 11aebc39..dcdee4ef 100644 --- a/lib/pages/subscription_detail/widget/sub_video_card.dart +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; @@ -5,6 +6,7 @@ import 'package:pilipala/common/widgets/stat/danmu.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import '../../../common/widgets/badge.dart'; @@ -40,6 +42,11 @@ class SubVideoCardH extends StatelessWidget { 'videoType': SearchType.video, }); }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), child: Column( children: [ Padding( diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index 0912724e..fe3b0dca 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import './controller.dart'; @@ -54,20 +52,6 @@ class _RelatedVideoPanelState extends State child: VideoCardH( videoItem: relatedVideoList[index], showPubdate: true, - longPress: () { - try { - _releatedController.popupDialog = - _createPopupDialog(_releatedController - .relatedVideoList[index]); - Overlay.of(context) - .insert(_releatedController.popupDialog!); - } catch (err) { - return {}; - } - }, - longPressEnd: () { - _releatedController.popupDialog?.remove(); - }, ), ); } @@ -89,15 +73,4 @@ class _RelatedVideoPanelState extends State }, ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (BuildContext context) => AnimatedDialog( - closeFn: _releatedController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, - closeFn: _releatedController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/utils/download.dart b/lib/utils/download.dart index 2aff8999..42dbbecf 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -15,24 +15,7 @@ class DownloadUtils { PermissionStatus status = await Permission.storage.status; if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('存储权限未授权'), - actions: [ - TextButton( - onPressed: () async { - openAppSettings(); - }, - child: const Text('去授权'), - ) - ], - ); - }, - ); + await permissionDialog('提示', '存储权限未授权'); return false; } else { return true; @@ -45,24 +28,7 @@ class DownloadUtils { PermissionStatus status = await Permission.photos.status; if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('相册权限未授权'), - actions: [ - TextButton( - onPressed: () async { - openAppSettings(); - }, - child: const Text('去授权'), - ) - ], - ); - }, - ); + await permissionDialog('提示', '相册权限未授权'); return false; } else { return true; @@ -72,17 +38,16 @@ class DownloadUtils { static Future downloadImg(String imgUrl, {String imgType = 'cover'}) async { try { - if (!Platform.isAndroid || !await requestPhotoPer()) { - return false; - } - final androidInfo = await DeviceInfoPlugin().androidInfo; - if (androidInfo.version.sdkInt <= 32) { - if (!await requestStoragePer()) { - return false; - } - } else { - if (!await requestPhotoPer()) { - return false; + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt <= 32) { + if (!await requestStoragePer()) { + return false; + } + } else { + if (!await requestPhotoPer()) { + return false; + } } } @@ -101,13 +66,38 @@ class DownloadUtils { ); SmartDialog.dismiss(); if (result.isSuccess) { - await SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 '); + SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 '); + return true; + } else { + await permissionDialog('保存失败', '相册权限未授权'); + return false; } - return true; } catch (err) { SmartDialog.dismiss(); SmartDialog.showToast(err.toString()); - return true; + return false; } } + + static Future permissionDialog(String title, String content, + {Function? onGranted}) async { + await SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () async { + openAppSettings(); + }, + child: const Text('去授权'), + ) + ], + ); + }, + ); + } } diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart new file mode 100644 index 00000000..0cd6915c --- /dev/null +++ b/lib/utils/image_save.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/download.dart'; + +Future imageSaveDialog(context, videoItem, closeFn) { + final double imgWidth = + MediaQuery.sizeOf(context).width - StyleString.safeSpace * 2; + return SmartDialog.show( + animationType: SmartAnimationType.centerScale_otherSlide, + builder: (context) => Container( + margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.circular(10.0), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + NetworkImgLayer( + width: imgWidth, + height: imgWidth / StyleString.aspectRatio, + src: videoItem.pic! as String, + quality: 100, + ), + Positioned( + right: 8, + top: 8, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.3), + borderRadius: + const BorderRadius.all(Radius.circular(20))), + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => closeFn!(), + icon: const Icon( + Icons.close, + size: 18, + color: Colors.white, + ), + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(12, 10, 8, 10), + child: Row( + children: [ + Expanded( + child: Text( + videoItem.title! as String, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + const SizedBox(width: 4), + IconButton( + tooltip: '保存封面图', + onPressed: () async { + bool saveStatus = await DownloadUtils.downloadImg( + videoItem.pic != null + ? videoItem.pic as String + : videoItem.cover as String, + ); + // 保存成功,自动关闭弹窗 + if (saveStatus) { + closeFn?.call(); + } + }, + icon: const Icon(Icons.download, size: 20), + ) + ], + ), + ), + ], + ), + ), + ); +} From e361e09ddfefb964ebd0f7135b8ce788d1e00f60 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:02:34 +0800 Subject: [PATCH 073/438] =?UTF-8?q?fix:=20=E8=AE=A2=E9=98=85=E8=AF=A6?= =?UTF-8?q?=E6=83=85type=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/subscription/widgets/item.dart | 1 + lib/pages/subscription_detail/controller.dart | 11 +++++++---- lib/pages/subscription_detail/view.dart | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 5b2a0134..0d424611 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -25,6 +25,7 @@ class SubItem extends StatelessWidget { parameters: { 'heroTag': heroTag, 'seasonId': subFolderItem.id.toString(), + 'type': subFolderItem.type.toString(), }, ), child: Padding( diff --git a/lib/pages/subscription_detail/controller.dart b/lib/pages/subscription_detail/controller.dart index 4245df2c..e69f4be0 100644 --- a/lib/pages/subscription_detail/controller.dart +++ b/lib/pages/subscription_detail/controller.dart @@ -14,13 +14,16 @@ class SubDetailController extends GetxController { RxList subList = [].obs; RxString loadingText = '加载中...'.obs; int mediaCount = 0; + late int channelType; @override void onInit() { item = Get.arguments; - if (Get.parameters.keys.isNotEmpty) { - seasonId = int.parse(Get.parameters['seasonId']!); - heroTag = Get.parameters['heroTag']!; + final parameters = Get.parameters; + if (parameters.isNotEmpty) { + seasonId = int.tryParse(parameters['seasonId'] ?? '') ?? 0; + heroTag = parameters['heroTag'] ?? ''; + channelType = int.tryParse(parameters['type'] ?? '') ?? 0; } super.onInit(); } @@ -31,7 +34,7 @@ class SubDetailController extends GetxController { return; } isLoadingMore = true; - var res = type == 21 + var res = channelType == 21 ? await UserHttp.userSeasonList( seasonId: seasonId, ps: 20, diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 63352429..4ca241fe 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -198,8 +198,8 @@ class _SubDetailPageState extends State { future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { if (_subDetailController.item.mediaCount == 0) { return const NoData(); } else { @@ -219,7 +219,7 @@ class _SubDetailPageState extends State { } } else { return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求异常', fn: () => setState(() {}), ); } From 91ad0fa03dc44ddc3694367b8902db0be34003b0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:22:52 +0800 Subject: [PATCH 074/438] =?UTF-8?q?fix:=20=E6=9F=A5=E7=9C=8B=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E8=AF=A6=E6=83=85=E7=82=B9=E5=87=BB=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 468d2541..ef4cd15c 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -44,7 +44,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem, null); + replyReply!(replyItem); } }, onLongPress: () { @@ -358,7 +358,7 @@ class ReplyItemRow extends StatelessWidget { InkWell( // 一楼点击评论展开评论详情 // onTap: () { - // replyReply?.call(replyItem, replies![i]); + // replyReply?.call(replyItem); // }, onLongPress: () { feedBack(); From 291312bde70df30258692759fcb31ed030f0b133 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:44:38 +0800 Subject: [PATCH 075/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E8=A7=86=E9=A2=91=E9=A1=B5pic=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 16 ++++++++++++++++ lib/pages/video/detail/controller.dart | 2 +- lib/utils/url_utils.dart | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 18481ea8..cf1a1b49 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -163,4 +163,20 @@ class SearchHttp { }; } } + + static Future> ab2cWithPic( + {int? aid, String? bvid}) async { + Map data = {}; + if (aid != null) { + data['aid'] = aid; + } else if (bvid != null) { + data['bvid'] = bvid; + } + final dynamic res = + await Request().get(Api.ab2c, data: {...data}); + return { + 'cid': res.data['data'].first['cid'], + 'pic': res.data['data'].first['first_frame'], + }; + } } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 6dd6425f..f7f9e848 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -547,7 +547,7 @@ class VideoDetailController extends GetxController } void updateCover(String? pic) { - if (pic != null && pic != '') { + if (pic != null) { cover.value = videoItem['pic'] = pic; } } diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index cf0ef9e2..20f9cb0d 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -42,12 +42,14 @@ class UrlUtils { final Map matchRes = IdUtils.matchAvorBv(input: pathSegment); if (matchRes.containsKey('BV')) { final String bv = matchRes['BV']; - final int cid = await SearchHttp.ab2c(bvid: bv); + final Map res = await SearchHttp.ab2cWithPic(bvid: bv); + final int cid = res['cid']; + final String pic = res['pic']; final String heroTag = Utils.makeHeroTag(bv); await Get.toNamed( '/video?bvid=$bv&cid=$cid', arguments: { - 'pic': '', + 'pic': pic, 'heroTag': heroTag, }, ); From 8082550da6d4a8c6a722243085418186fd57ab7d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 22:51:55 +0800 Subject: [PATCH 076/438] =?UTF-8?q?fix:=20=E5=85=A8=E5=B1=8F=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index b87350a5..bcacbbbb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -535,20 +535,20 @@ class _VideoDetailPageState extends State controller: _extendNestCtr, headerSliverBuilder: (BuildContext context2, bool innerBoxIsScrolled) { - final Orientation orientation = - MediaQuery.of(context).orientation; - final bool isFullScreen = - plPlayerController?.isFullScreen.value == true; - final double expandedHeight = - orientation == Orientation.landscape || isFullScreen - ? (MediaQuery.sizeOf(context).height - - (orientation == Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top)) - : videoHeight.value; return [ Obx( () { + final Orientation orientation = + MediaQuery.of(context).orientation; + final bool isFullScreen = + plPlayerController?.isFullScreen.value == true; + final double expandedHeight = + orientation == Orientation.landscape || isFullScreen + ? (MediaQuery.sizeOf(context).height - + (orientation == Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top)) + : videoHeight.value; if (orientation == Orientation.landscape || isFullScreen) { enterFullScreen(); From b85067f7ebdc2e257e4a826f0c74ff41125f40f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 23:41:18 +0800 Subject: [PATCH 077/438] =?UTF-8?q?opt:=20=E7=B3=BB=E7=BB=9F=E7=BA=A7?= =?UTF-8?q?=E8=B7=B3=E8=BD=ACav=E3=80=81bangumi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 69fa717d..5bfb955c 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -167,8 +167,21 @@ class PiliSchame { print('bilibili.com host: $host'); print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; - if (lastPathSegment.contains('BV')) { - _videoPush(null, lastPathSegment); + if (path.startsWith('/video')) { + if (lastPathSegment.contains('BV')) { + _videoPush(null, lastPathSegment); + } + if (lastPathSegment.contains('av')) { + _videoPush(matchNum(lastPathSegment)[0], null); + } + } + if (path.startsWith('/bangumi')) { + if (lastPathSegment.contains('ss')) { + _bangumiPush(matchNum(lastPathSegment).first, null); + } + if (lastPathSegment.contains('ep')) { + _bangumiPush(null, matchNum(lastPathSegment).first); + } } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); From 61505c61273dab58fd804ccce6485af4afc19e84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 2 May 2024 22:19:20 +0800 Subject: [PATCH 078/438] opt: RoutePush bangumi --- lib/common/widgets/video_card_v.dart | 25 +++-------- lib/pages/bangumi/widgets/bangumu_card_v.dart | 37 +++------------ lib/pages/dynamics/controller.dart | 21 +-------- lib/pages/history/widgets/item.dart | 27 +++-------- .../widgets/media_bangumi_panel.dart | 25 ++--------- .../whisper_detail/widget/chat_item.dart | 45 ++++++++++++++----- lib/utils/app_scheme.dart | 44 ++++-------------- lib/utils/route_push.dart | 44 ++++++++++++++++++ 8 files changed, 111 insertions(+), 157 deletions(-) create mode 100644 lib/utils/route_push.dart diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 6a97d7e7..15fbc6fc 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -3,14 +3,13 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; import '../../models/model_rec_video_item.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; -import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; -import '../../models/common/search_type.dart'; import '../../utils/id_utils.dart'; import '../../utils/utils.dart'; import '../constants.dart'; @@ -42,23 +41,11 @@ class VideoCardV extends StatelessWidget { return; } int epId = videoItem.param; - SmartDialog.showLoading(msg: '资源获取中'); - var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId); - if (result['status']) { - var bangumiDetail = result['data']; - int cid = bangumiDetail.episodes!.first.cid; - String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid); - SmartDialog.dismiss().then( - (value) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid&epId=$epId', - arguments: { - 'pic': videoItem.pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - }, - ), - ); - } + RoutePush.bangumiPush( + null, + epId, + heroTag: heroTag, + ); break; case 'av': String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid); diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index 3c8f6d2a..a1d7a931 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; -import 'package:pilipala/http/search.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/bangumi/list.dart'; -import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -24,32 +21,12 @@ class BangumiCardV extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(bangumiItem.mediaId); return InkWell( - onTap: () async { - final int seasonId = bangumiItem.seasonId!; - SmartDialog.showLoading(msg: '获取中...'); - final res = await SearchHttp.bangumiInfo(seasonId: seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - if (res['data'].episodes.isEmpty) { - SmartDialog.showToast('资源加载失败'); - return; - } - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } - }); + onTap: () { + RoutePush.bangumiPush( + bangumiItem.seasonId, + null, + heroTag: heroTag, + ); }, onLongPress: () => imageSaveDialog(context, bangumiItem, SmartDialog.dismiss), diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index b7676663..6814704c 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -14,6 +14,7 @@ import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; @@ -220,25 +221,7 @@ class DynamicsController extends GetxController { print('DYNAMIC_TYPE_PGC_UNION 番剧'); DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc; if (pgc.epid != null) { - SmartDialog.showLoading(msg: '获取中...'); - var res = await SearchHttp.bangumiInfo(epId: pgc.epid); - SmartDialog.dismiss(); - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } + RoutePush.bangumiPush(null, pgc.epid); } break; } diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 39c6931d..ba5f6b5c 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -7,13 +7,13 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/business_type.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/pages/history_search/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; class HistoryItem extends StatelessWidget { @@ -101,26 +101,11 @@ class HistoryItem extends StatelessWidget { } } else { if (videoItem.history.epid != '') { - SmartDialog.showLoading(msg: '获取中...'); - var res = - await SearchHttp.bangumiInfo(epId: videoItem.history.epid); - SmartDialog.dismiss(); - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } + RoutePush.bangumiPush( + null, + videoItem.history.epid, + heroTag: heroTag, + ); } } } else { diff --git a/lib/pages/search_panel/widgets/media_bangumi_panel.dart b/lib/pages/search_panel/widgets/media_bangumi_panel.dart index 7d88b183..5bba0ab8 100644 --- a/lib/pages/search_panel/widgets/media_bangumi_panel.dart +++ b/lib/pages/search_panel/widgets/media_bangumi_panel.dart @@ -7,6 +7,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; Widget searchMbangumiPanel(BuildContext context, ctr, list) { @@ -108,28 +109,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { SizedBox( height: 32, child: ElevatedButton( - onPressed: () async { - SmartDialog.showLoading(msg: '获取中...'); - var res = await SearchHttp.bangumiInfo( - seasonId: i.seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${i.seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } - }); + onPressed: () { + RoutePush.bangumiPush(i.seasonId, null); }, child: const Text('观看'), ), diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 4fd49254..743cf901 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -49,6 +50,13 @@ class ChatItem extends StatelessWidget { this.e_infos, }); + static List matchNum(String str) { + final RegExp regExp = RegExp(r'\d+'); + final Iterable matches = regExp.allMatches(str); + + return matches.map((Match match) => int.parse(match.group(0)!)).toList(); + } + @override Widget build(BuildContext context) { bool isOwner = @@ -154,16 +162,33 @@ class ChatItem extends StatelessWidget { GestureDetector( onTap: () async { SmartDialog.showLoading(); - var bvid = content["bvid"]; + final String bvid = content["bvid"]; + // 16番剧 5投稿 + final int source = content["source"]; + final String? url = content["url"]; + final int cid = await SearchHttp.ab2c(bvid: bvid); final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': content['thumb'], - 'heroTag': heroTag, - }), - ); + await SmartDialog.dismiss(); + if (source == 5) { + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': content['thumb'], + 'heroTag': heroTag, + }, + ); + } + if (source == 16) { + if (url != null) { + final String area = url.split('/').last; + if (area.startsWith('ep')) { + RoutePush.bangumiPush(null, matchNum(area).first); + } else if (area.startsWith('ss')) { + RoutePush.bangumiPush(matchNum(area).first, null); + } + } + } }, child: NetworkImgLayer( width: 220, @@ -183,7 +208,7 @@ class ChatItem extends StatelessWidget { ), const SizedBox(height: 1), Text( - content['author'], + content['author'] ?? '', style: TextStyle( letterSpacing: 0.6, height: 1.5, @@ -206,7 +231,7 @@ class ChatItem extends StatelessWidget { SmartDialog.dismiss().then( (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: { - 'pic': content['thumb'], + 'pic': content['thumb'] ?? '', 'heroTag': heroTag, }), ); diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 5bfb955c..5b77434d 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -2,6 +2,7 @@ import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/route_push.dart'; import '../http/search.dart'; import '../models/common/search_type.dart'; import 'id_utils.dart'; @@ -68,7 +69,7 @@ class PiliSchame { } else if (host == 'bangumi') { if (path.startsWith('/season')) { final String seasonId = path.split('/').last; - _bangumiPush(int.parse(seasonId), null); + RoutePush.bangumiPush(int.parse(seasonId), null); } } else if (host == 'opus') { if (path.startsWith('/detail')) { @@ -126,35 +127,6 @@ class PiliSchame { } } - // 番剧跳转 - static Future _bangumiPush(int? seasonId, int? epId) async { - SmartDialog.showLoading(msg: '获取中...'); - try { - var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId); - if (result['status']) { - var bangumiDetail = result['data']; - final int cid = bangumiDetail.episodes!.first.cid; - final String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid); - final String heroTag = Utils.makeHeroTag(cid); - var epId = bangumiDetail.episodes!.first.id; - SmartDialog.dismiss().then( - (e) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid&epId=$epId', - arguments: { - 'pic': bangumiDetail.cover, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - }, - ), - ); - } else { - SmartDialog.showToast(result['msg']); - } - } catch (e) { - SmartDialog.showToast('番剧获取失败:$e'); - } - } - static Future _fullPathPush(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 @@ -177,10 +149,10 @@ class PiliSchame { } if (path.startsWith('/bangumi')) { if (lastPathSegment.contains('ss')) { - _bangumiPush(matchNum(lastPathSegment).first, null); + RoutePush.bangumiPush(matchNum(lastPathSegment).first, null); } if (lastPathSegment.contains('ep')) { - _bangumiPush(null, matchNum(lastPathSegment).first); + RoutePush.bangumiPush(null, matchNum(lastPathSegment).first); } } } else if (host.contains('live')) { @@ -233,9 +205,9 @@ class PiliSchame { case 'bangumi': print('番剧'); if (area.startsWith('ep')) { - _bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, matchNum(area).first); } else if (area.startsWith('ss')) { - _bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(matchNum(area).first, null); } break; case 'video': @@ -276,12 +248,12 @@ class PiliSchame { static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - _bangumiPush(null, matchNum(seasonId).first); + RoutePush.bangumiPush(null, matchNum(seasonId).first); } static void _handleSeasonPath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - _bangumiPush(matchNum(seasonId).first, null); + RoutePush.bangumiPush(matchNum(seasonId).first, null); } static String _extractIdFromPath(String lastPathSegment) { diff --git a/lib/utils/route_push.dart b/lib/utils/route_push.dart new file mode 100644 index 00000000..e00123b4 --- /dev/null +++ b/lib/utils/route_push.dart @@ -0,0 +1,44 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/bangumi/info.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/utils.dart'; + +class RoutePush { + // 番剧跳转 + static Future bangumiPush(int? seasonId, int? epId, + {String? heroTag}) async { + SmartDialog.showLoading(msg: '获取中...'); + try { + var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId); + await SmartDialog.dismiss(); + if (result['status']) { + if (result['data'].episodes.isEmpty) { + SmartDialog.showToast('资源获取失败'); + return; + } + final BangumiInfoModel bangumiDetail = result['data']; + final EpisodeItem episode = bangumiDetail.episodes!.first; + final int epId = episode.id!; + final int cid = episode.cid!; + final String bvid = episode.bvid!; + final String cover = episode.cover!; + final Map arguments = { + 'pic': cover, + 'videoType': SearchType.media_bangumi, + // 'bangumiItem': bangumiDetail, + }; + arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid&epId=$epId', + arguments: arguments, + ); + } else { + SmartDialog.showToast(result['msg']); + } + } catch (e) { + SmartDialog.showToast('番剧获取失败:$e'); + } + } +} From b8efe249deae10161c0c6c5218bfa49f0e2c4bae Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 2 May 2024 23:33:58 +0800 Subject: [PATCH 079/438] =?UTF-8?q?opt:=20up=E4=B8=BB=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E7=95=AA=E5=89=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 17 ++++++++++++++ .../whisper_detail/widget/chat_item.dart | 11 ++------- lib/utils/app_scheme.dart | 23 +++++++------------ lib/utils/url_utils.dart | 2 +- lib/utils/utils.dart | 7 ++++++ 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index df0c29b7..4c0729a3 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/http/constants.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; +import 'package:pilipala/utils/url_utils.dart'; import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; @@ -52,6 +55,20 @@ class VideoCardH extends StatelessWidget { SmartDialog.showToast('课堂视频暂不支持播放'); return; } + if (showCharge && videoItem?.typeid == 33) { + final String redirectUrl = await UrlUtils.parseRedirectUrl( + '${HttpString.baseUrl}/video/$bvid/'); + final String lastPathSegment = redirectUrl.split('/').last; + if (lastPathSegment.contains('ss')) { + RoutePush.bangumiPush( + Utils.matchNum(lastPathSegment).first, null); + } + if (lastPathSegment.contains('ep')) { + RoutePush.bangumiPush( + null, Utils.matchNum(lastPathSegment).first); + } + return; + } final int cid = videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); Get.toNamed('/video?bvid=$bvid&cid=$cid', diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 743cf901..ad11e4c3 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -50,13 +50,6 @@ class ChatItem extends StatelessWidget { this.e_infos, }); - static List matchNum(String str) { - final RegExp regExp = RegExp(r'\d+'); - final Iterable matches = regExp.allMatches(str); - - return matches.map((Match match) => int.parse(match.group(0)!)).toList(); - } - @override Widget build(BuildContext context) { bool isOwner = @@ -183,9 +176,9 @@ class ChatItem extends StatelessWidget { if (url != null) { final String area = url.split('/').last; if (area.startsWith('ep')) { - RoutePush.bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, Utils.matchNum(area).first); } else if (area.startsWith('ss')) { - RoutePush.bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(Utils.matchNum(area).first, null); } } } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 5b77434d..fff78b39 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -144,15 +144,15 @@ class PiliSchame { _videoPush(null, lastPathSegment); } if (lastPathSegment.contains('av')) { - _videoPush(matchNum(lastPathSegment)[0], null); + _videoPush(Utils.matchNum(lastPathSegment)[0], null); } } if (path.startsWith('/bangumi')) { if (lastPathSegment.contains('ss')) { - RoutePush.bangumiPush(matchNum(lastPathSegment).first, null); + RoutePush.bangumiPush(Utils.matchNum(lastPathSegment).first, null); } if (lastPathSegment.contains('ep')) { - RoutePush.bangumiPush(null, matchNum(lastPathSegment).first); + RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first); } } } else if (host.contains('live')) { @@ -205,9 +205,9 @@ class PiliSchame { case 'bangumi': print('番剧'); if (area.startsWith('ep')) { - RoutePush.bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, Utils.matchNum(area).first); } else if (area.startsWith('ss')) { - RoutePush.bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(Utils.matchNum(area).first, null); } break; case 'video': @@ -223,7 +223,7 @@ class PiliSchame { break; case 'read': print('专栏'); - String id = 'cv${matchNum(query!['id']!).first}'; + String id = 'cv${Utils.matchNum(query!['id']!).first}'; Get.toNamed('/htmlRender', parameters: { 'url': value.dataString!, 'title': '', @@ -239,21 +239,14 @@ class PiliSchame { } } - static List matchNum(String str) { - final RegExp regExp = RegExp(r'\d+'); - final Iterable matches = regExp.allMatches(str); - - return matches.map((Match match) => int.parse(match.group(0)!)).toList(); - } - static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - RoutePush.bangumiPush(null, matchNum(seasonId).first); + RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); } static void _handleSeasonPath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - RoutePush.bangumiPush(matchNum(seasonId).first, null); + RoutePush.bangumiPush(Utils.matchNum(seasonId).first, null); } static String _extractIdFromPath(String lastPathSegment) { diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index 20f9cb0d..99886e09 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -16,7 +16,7 @@ class UrlUtils { }; try { final response = await dio.get(url); - if (response.statusCode == 302) { + if (response.statusCode == 302 || response.statusCode == 301) { redirectUrl = response.headers['location']?.first as String; if (redirectUrl.endsWith('/')) { redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index b40e9bfe..e50c295c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -383,4 +383,11 @@ class Utils { List randomBytes = generateRandomBytes(minLength, maxLength); return base64.encode(randomBytes); } + + static List matchNum(String str) { + final RegExp regExp = RegExp(r'\d+'); + final Iterable matches = regExp.allMatches(str); + + return matches.map((Match match) => int.parse(match.group(0)!)).toList(); + } } From b2eaec15b165187a201ddb8d1c0b17d948f025f7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 15:58:54 +0800 Subject: [PATCH 080/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=8A=9F=E8=83=BD=E6=8B=93=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 218 +++++++++++++++------------ lib/common/widgets/video_card_v.dart | 10 ++ 2 files changed, 130 insertions(+), 98 deletions(-) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 4c0729a3..1265477f 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/constants.dart'; +import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/url_utils.dart'; @@ -276,115 +277,29 @@ class VideoContent extends StatelessWidget { theme: 'gray', danmu: videoItem.stat.danmaku as int, ), - const Spacer(), - // SizedBox( - // width: 20, - // height: 20, - // child: IconButton( - // tooltip: '稍后再看', - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () async { - // var res = - // await UserHttp.toViewLater(bvid: videoItem.bvid); - // SmartDialog.showToast(res['msg']); - // }, - // icon: Icon( - // Icons.more_vert_outlined, - // color: Theme.of(context).colorScheme.outline, - // size: 14, - // ), - // ), - // ), if (source == 'normal') SizedBox( width: 24, height: 24, - child: PopupMenuButton( + child: IconButton( padding: EdgeInsets.zero, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(videoItem: videoItem); + }, + ); + }, icon: Icon( Icons.more_vert_outlined, color: Theme.of(context).colorScheme.outline, size: 14, ), - position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => - >[ - PopupMenuItem( - onTap: () async { - var res = await UserHttp.toViewLater( - bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], - ), - ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: - SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['code'] == 0 - ? '成功' - : res['msg']); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], - ), - ), - ], ), ), if (source == 'later') ...[ @@ -408,3 +323,110 @@ class VideoContent extends StatelessWidget { ); } } + +class MorePanel extends StatelessWidget { + final dynamic videoItem; + const MorePanel({super.key, required this.videoItem}); + + Future menuActionHandler(String type) async { + switch (type) { + case 'block': + blockUser(); + break; + case 'watchLater': + var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + Get.back(); + break; + default: + } + } + + void blockUser() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + ), + ListTile( + onTap: () async => await menuActionHandler('block'), + minLeadingWidth: 0, + leading: const Icon(Icons.block, size: 19), + title: Text( + '拉黑up主 「${videoItem.owner.name}」', + style: Theme.of(context).textTheme.titleSmall, + ), + ), + ListTile( + onTap: () async => await menuActionHandler('watchLater'), + minLeadingWidth: 0, + leading: const Icon(Icons.watch_later_outlined, size: 19), + title: + Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), + ), + ListTile( + onTap: () => + imageSaveDialog(context, videoItem, SmartDialog.dismiss), + minLeadingWidth: 0, + leading: const Icon(Icons.photo_outlined, size: 19), + title: + Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall), + ), + const SizedBox(height: 20), + ], + ), + ); + } +} diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 15fbc6fc..7b56152f 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -233,6 +233,7 @@ class VideoContent extends StatelessWidget { width: 24, height: 24, child: IconButton( + padding: EdgeInsets.zero, onPressed: () { feedBack(); showModalBottomSheet( @@ -386,6 +387,15 @@ class MorePanel extends StatelessWidget { title: Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), ), + ListTile( + onTap: () => + imageSaveDialog(context, videoItem, SmartDialog.dismiss), + minLeadingWidth: 0, + leading: const Icon(Icons.photo_outlined, size: 19), + title: + Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall), + ), + const SizedBox(height: 20), ], ), ); From 81af23e870a4fcb5ff11ee2981c508d520548999 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 21:04:03 +0800 Subject: [PATCH 081/438] mod: MainActivity --- android/app/src/main/AndroidManifest.xml | 2 +- .../kotlin/com/guozhigq/pilipala/MainActivity.kt | 6 ++++-- pubspec.lock | 14 +++++++------- pubspec.yaml | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c52d8447..f119eb1e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -45,7 +45,7 @@ android:fullBackupContent="false" tools:replace="android:allowBackup"> Date: Fri, 3 May 2024 21:41:04 +0800 Subject: [PATCH 082/438] =?UTF-8?q?mod:=20=E5=BA=95=E9=83=A8=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E6=A0=8Fsdk=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 43bcf930..066bb599 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -67,9 +68,8 @@ void main() async { // 小白条、导航栏沉浸 if (Platform.isAndroid) { - List versionParts = Platform.version.split('.'); - int androidVersion = int.parse(versionParts[0]); - if (androidVersion >= 29) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 29) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( From a32be2c5c622f5f05544e2c6d214efa7bdf4efb7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 21:42:01 +0800 Subject: [PATCH 083/438] =?UTF-8?q?fix:=20=E5=8D=8A=E5=B1=8F=E6=97=B6?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=AE=8C=E6=88=90=E7=8A=B6=E6=80=81=E6=A0=8F?= =?UTF-8?q?=E6=B6=88=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 35 +--------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 4b6c44a1..2fc16924 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -978,41 +978,8 @@ class PlPlayerController { } else { await landScape(); } - - // bool isValid = - // direction.value == 'vertical' || mode == FullScreenMode.vertical - // ? true - // : false; - // var result = await showDialog( - // context: Get.context!, - // useSafeArea: false, - // builder: (context) => Dialog.fullscreen( - // backgroundColor: Colors.black, - // child: SafeArea( - // // 忽略手机安全区域 - // top: isValid, - // left: false, - // right: false, - // bottom: isValid, - // child: PLVideoPlayer( - // controller: this, - // headerControl: headerControl, - // bottomControl: bottomControl, - // danmuWidget: danmuWidget, - // ), - // ), - // ), - // ); - // if (result == null) { - // // 退出全屏 - // StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); - // exitFullScreen(); - // await verticalScreen(); - // toggleFullScreen(false); - // } - } else if (isFullScreen.value) { + } else if (isFullScreen.value && !status) { StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); - // Get.back(); exitFullScreen(); await verticalScreen(); toggleFullScreen(false); From dafbb838636298a75edd14d98e1079cf2f2a805e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 22:58:14 +0800 Subject: [PATCH 084/438] =?UTF-8?q?opt:=20=E5=AA=92=E4=BD=93=E5=BA=93?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E7=99=BB=E5=BD=95=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/dynamics.dart | 1 + lib/http/user.dart | 24 +++++++++++++++---- lib/pages/dynamics/controller.dart | 4 ++-- lib/pages/dynamics/view.dart | 32 ++++++++++++-------------- lib/pages/fav/controller.dart | 9 ++++++-- lib/pages/fav/view.dart | 19 +++++++++++---- lib/pages/history/controller.dart | 7 ++++++ lib/pages/history/view.dart | 19 +++++++++++---- lib/pages/later/controller.dart | 14 +++++++++++ lib/pages/later/view.dart | 21 ++++++++++++----- lib/pages/mine/controller.dart | 10 ++------ lib/pages/subscription/controller.dart | 9 ++++++-- lib/pages/subscription/view.dart | 15 ++++++++++-- lib/utils/route_push.dart | 24 +++++++++++++++++++ 14 files changed, 157 insertions(+), 51 deletions(-) diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 63dea4ff..69619361 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -41,6 +41,7 @@ class DynamicsHttp { 'status': false, 'data': [], 'msg': res.data['message'], + 'code': res.data['code'], }; } } diff --git a/lib/http/user.dart b/lib/http/user.dart index dfdf187e..972acfdd 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -62,7 +62,8 @@ class UserHttp { return { 'status': false, 'data': [], - 'msg': res.data['message'] ?? '账号未登录' + 'msg': res.data['message'], + 'code': res.data['code'], }; } } @@ -111,7 +112,12 @@ class UserHttp { 'data': {'list': list, 'count': res.data['data']['count']} }; } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } @@ -126,7 +132,12 @@ class UserHttp { if (res.data['code'] == 0) { return {'status': true, 'data': HistoryData.fromJson(res.data['data'])}; } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } @@ -326,7 +337,12 @@ class UserHttp { 'data': SubFolderModelData.fromJson(res.data['data']) }; } else { - return {'status': false, 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 6814704c..9bed3685 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -71,7 +71,7 @@ class DynamicsController extends GetxController { Future queryFollowDynamic({type = 'init'}) async { if (!userLogin.value) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (type == 'init') { dynamicsList.clear(); @@ -229,7 +229,7 @@ class DynamicsController extends GetxController { Future queryFollowUp({type = 'init'}) async { if (!userLogin.value) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (type == 'init') { upData.value.upList = []; diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 82a555b1..da15239d 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -11,6 +11,7 @@ import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/main_stream.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; @@ -224,8 +225,8 @@ class _DynamicsPageState extends State if (snapshot.data == null) { return const SliverToBoxAdapter(child: SizedBox()); } - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { List list = _dynamicsController.dynamicsList; return Obx( @@ -248,24 +249,21 @@ class _DynamicsPageState extends State } }, ); - } else if (data['msg'] == "账号未登录") { - return HttpError( - errMsg: data['msg'], - btnText: "去登录", - fn: () { - mineController.onLogin(); - }, - ); } else { return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, fn: () { - setState(() { - _futureBuilderFuture = - _dynamicsController.queryFollowDynamic(); - _futureBuilderFutureUp = - _dynamicsController.queryFollowUp(); - }); + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _dynamicsController.queryFollowDynamic(); + _futureBuilderFutureUp = + _dynamicsController.queryFollowUp(); + }); + } }, ); } diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 2307d303..8fcbf971 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -17,10 +17,15 @@ class FavController extends GetxController { int pageSize = 60; RxBool hasMore = true.obs; - Future queryFavFolder({type = 'init'}) async { + @override + void onInit() { userInfo = userInfoCache.get('userInfoCache'); + super.onInit(); + } + + Future queryFavFolder({type = 'init'}) async { if (userInfo == null) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (!hasMore.value) { return; diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 424a885d..b8d37f50 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/fav/widgets/item.dart'; +import 'package:pilipala/utils/route_push.dart'; class FavPage extends StatefulWidget { const FavPage({super.key}); @@ -57,8 +58,8 @@ class _FavPageState extends State { future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => ListView.builder( controller: scrollController, @@ -74,8 +75,18 @@ class _FavPageState extends State { physics: const NeverScrollableScrollPhysics(), slivers: [ HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _favController.queryFavFolder(); + }); + } + }, ), ], ); diff --git a/lib/pages/history/controller.dart b/lib/pages/history/controller.dart index a1f18113..64953e3b 100644 --- a/lib/pages/history/controller.dart +++ b/lib/pages/history/controller.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/user/history.dart'; +import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/utils/storage.dart'; class HistoryController extends GetxController { @@ -15,14 +16,20 @@ class HistoryController extends GetxController { RxBool isLoading = false.obs; RxBool enableMultiple = false.obs; RxInt checkedCount = 0.obs; + Box userInfoCache = GStrorage.userInfo; + UserInfoData? userInfo; @override void onInit() { super.onInit(); historyStatus(); + userInfo = userInfoCache.get('userInfoCache'); } Future queryHistoryList({type = 'init'}) async { + if (userInfo == null) { + return {'status': false, 'msg': '账号未登录', 'code': -101}; + } int max = 0; int viewAt = 0; if (type == 'onload') { diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index 92e1eee7..f9695aed 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/pages/history/index.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'widgets/item.dart'; @@ -183,8 +184,8 @@ class _HistoryPageState extends State { if (snapshot.data == null) { return const SliverToBoxAdapter(child: SizedBox()); } - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => _historyController.historyList.isNotEmpty ? SliverList( @@ -209,8 +210,18 @@ class _HistoryPageState extends State { ); } else { return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _historyController.queryHistoryList(); + }); + } + }, ); } } else { diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 3de51901..f7dfea9f 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -1,16 +1,30 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/model_hot_video_item.dart'; +import 'package:pilipala/models/user/info.dart'; +import 'package:pilipala/utils/storage.dart'; class LaterController extends GetxController { final ScrollController scrollController = ScrollController(); RxList laterList = [].obs; int count = 0; RxBool isLoading = false.obs; + Box userInfoCache = GStrorage.userInfo; + UserInfoData? userInfo; + + @override + void onInit() { + super.onInit(); + userInfo = userInfoCache.get('userInfoCache'); + } Future queryLaterList() async { + if (userInfo == null) { + return {'status': false, 'msg': '账号未登录', 'code': -101}; + } isLoading.value = true; var res = await UserHttp.seeYouLater(); if (res['status']) { diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 7c6e158d..0a02cb79 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/pages/later/index.dart'; +import 'package:pilipala/utils/route_push.dart'; class LaterPage extends StatefulWidget { const LaterPage({super.key}); @@ -72,8 +73,8 @@ class _LaterPageState extends State { future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => _laterController.laterList.isNotEmpty && !_laterController.isLoading.value @@ -96,10 +97,18 @@ class _LaterPageState extends State { ); } else { return HttpError( - errMsg: data['msg'], - fn: () => setState(() { - _futureBuilderFuture = _laterController.queryLaterList(); - }), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _laterController.queryLaterList(); + }); + } + }, ); } } else { diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 5ad9e852..a61bb820 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -6,6 +6,7 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { @@ -33,14 +34,7 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); + RoutePush.loginPush(); // Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index 7be8d22c..d8a76d44 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -17,10 +17,15 @@ class SubController extends GetxController { int pageSize = 20; RxBool hasMore = true.obs; - Future querySubFolder({type = 'init'}) async { + @override + void onInit() { + super.onInit(); userInfo = userInfoCache.get('userInfoCache'); + } + + Future querySubFolder({type = 'init'}) async { if (userInfo == null) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } var res = await UserHttp.userSubFolder( pn: currentPage, diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index 2d7d0cb5..5e6e4664 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'controller.dart'; import 'widgets/item.dart'; @@ -68,8 +69,18 @@ class _SubPageState extends State { physics: const NeverScrollableScrollPhysics(), slivers: [ HttpError( - errMsg: data?['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _subController.querySubFolder(); + }); + } + }, ), ], ); diff --git a/lib/utils/route_push.dart b/lib/utils/route_push.dart index e00123b4..9ee28846 100644 --- a/lib/utils/route_push.dart +++ b/lib/utils/route_push.dart @@ -41,4 +41,28 @@ class RoutePush { SmartDialog.showToast('番剧获取失败:$e'); } } + + // 登录跳转 + static Future loginPush() async { + await Get.toNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + } + + // 登录跳转 + static Future loginRedirectPush() async { + await Get.offAndToNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + } } From 15f7c4a8021658bfb5012d14b9a04b94cc5ea24e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 4 May 2024 23:22:24 +0800 Subject: [PATCH 085/438] =?UTF-8?q?v1.0.23=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.23.0504.md | 14 ++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.23.0504.md diff --git a/change_log/1.0.23.0504.md b/change_log/1.0.23.0504.md new file mode 100644 index 00000000..afd401fa --- /dev/null +++ b/change_log/1.0.23.0504.md @@ -0,0 +1,14 @@ +## 1.0.23 + +### 功能 ++ 封面下载 + + +### 修复 ++ 全屏问题 ++ 视频播放器灰屏问题 ++ 评论区点击区域问题 + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index fd34a8f6..a269cd6f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.22+1022 +version: 1.0.23+1023 environment: sdk: ">=3.0.0 <4.0.0" From bb4325768f0e12d018e2d5ff33d434f056b35fed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 10:06:26 +0800 Subject: [PATCH 086/438] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E5=B0=81=E9=9D=A2=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 3 +++ lib/pages/video/detail/view.dart | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index bda8ee5c..d2772478 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -34,6 +34,9 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { final int defaultImgQuality = GlobalData().imgQuality; + if (src == '' || src == null) { + return placeholder(context); + } final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; int? memCacheWidth, memCacheHeight; diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index bcacbbbb..2c35858f 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -323,11 +323,13 @@ class _VideoDetailPageState extends State children: [ GestureDetector( onTap: handlePlay, - child: Image.network( - vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight, - fit: BoxFit.cover, // 适应方式根据需要调整 + child: Obx( + () => NetworkImgLayer( + src: vdCtr.cover.value, + width: Get.width, + height: videoHeight, + type: 'emote', + ), ), ), buildCustomAppBar(), From 93d8e19a8ca1d74b7ff3eb6c389aa38201954dc0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 17:00:35 +0800 Subject: [PATCH 087/438] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E8=AF=A6=E6=83=85=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 2 +- lib/pages/dynamics/widgets/dynamic_panel.dart | 3 +-- lib/pages/dynamics/widgets/video_panel.dart | 13 +++++-------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index bd07921b..e83b5547 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -196,7 +196,7 @@ class _DynamicDetailPageState extends State centerTitle: false, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream.distinct(), + stream: titleStreamC.stream, initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index d273a1a6..cc5dcbb1 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; -import '../../../models/dynamics/result.dart'; import 'action_panel.dart'; import 'author_panel.dart'; import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - final DynamicItemModel item; + final dynamic item; final String? source; DynamicPanel({required this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 32a6e21c..828fb283 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -80,14 +80,11 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) { double width = box.maxWidth; return Stack( children: [ - Hero( - tag: content.bvid, - child: NetworkImgLayer( - type: floor == 1 ? 'emote' : null, - width: width, - height: width / StyleString.aspectRatio, - src: content.cover, - ), + NetworkImgLayer( + type: floor == 1 ? 'emote' : null, + width: width, + height: width / StyleString.aspectRatio, + src: content.cover, ), if (content.badge != null && type == 'pgc') PBadge( From e65389040f5be4a628928502f6488bd759035532 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 17:53:54 +0800 Subject: [PATCH 088/438] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E8=AE=A1?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 ++ lib/http/search.dart | 24 +++++++++ lib/models/search/all.dart | 9 ++++ lib/pages/search_result/controller.dart | 19 +++++++ lib/pages/search_result/view.dart | 68 +++++++++++++------------ 5 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 lib/models/search/all.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 1735902c..42fc03b8 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -520,4 +520,7 @@ class Api { /// 删除收藏夹 static const String delFavFolder = '/x/v3/fav/folder/del'; + + /// 搜索结果计数 + static const String searchCount = '/x/web-interface/wbi/search/all/v2'; } diff --git a/lib/http/search.dart b/lib/http/search.dart index cf1a1b49..9b493a84 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -1,5 +1,7 @@ import 'dart:convert'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/search/all.dart'; +import 'package:pilipala/utils/wbi_sign.dart'; import '../models/bangumi/info.dart'; import '../models/common/search_type.dart'; import '../models/search/hot.dart'; @@ -179,4 +181,26 @@ class SearchHttp { 'pic': res.data['data'].first['first_frame'], }; } + + static Future> searchCount( + {required String keyword}) async { + Map data = { + 'keyword': keyword, + 'web_location': 333.999, + }; + Map params = await WbiSign().makSign(data); + final dynamic res = await Request().get(Api.searchCount, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SearchAllModel.fromJson(res.data['data']), + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': '请求错误 🙅', + }; + } + } } diff --git a/lib/models/search/all.dart b/lib/models/search/all.dart new file mode 100644 index 00000000..796400da --- /dev/null +++ b/lib/models/search/all.dart @@ -0,0 +1,9 @@ +class SearchAllModel { + SearchAllModel({this.topTList}); + + Map? topTList; + + SearchAllModel.fromJson(Map json) { + topTList = json['top_tlist']; + } +} diff --git a/lib/pages/search_result/controller.dart b/lib/pages/search_result/controller.dart index 9914d82b..02a9e63b 100644 --- a/lib/pages/search_result/controller.dart +++ b/lib/pages/search_result/controller.dart @@ -1,8 +1,11 @@ import 'package:get/get.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/common/search_type.dart'; class SearchResultController extends GetxController { String? keyword; int tabIndex = 0; + RxList searchTabs = [].obs; @override void onInit() { @@ -10,5 +13,21 @@ class SearchResultController extends GetxController { if (Get.parameters.keys.isNotEmpty) { keyword = Get.parameters['keyword']; } + searchTabs.value = SearchType.values + .map((type) => {'label': type.label, 'id': type.type}) + .toList(); + querySearchCount(); + } + + Future querySearchCount() async { + var result = await SearchHttp.searchCount(keyword: keyword!); + if (result['status']) { + for (var i in searchTabs) { + final count = result['data'].topTList[i['id']]; + i['count'] = count > 99 ? '99+' : count.toString(); + } + searchTabs.refresh(); + } + return result; } } diff --git a/lib/pages/search_result/view.dart b/lib/pages/search_result/view.dart index ff5bf780..96fdd91d 100644 --- a/lib/pages/search_result/view.dart +++ b/lib/pages/search_result/view.dart @@ -13,7 +13,7 @@ class SearchResultPage extends StatefulWidget { class _SearchResultPageState extends State with TickerProviderStateMixin { - late SearchResultController? _searchResultController; + late SearchResultController _searchResultController; late TabController? _tabController; @override @@ -25,7 +25,7 @@ class _SearchResultPageState extends State _tabController = TabController( vsync: this, length: SearchType.values.length, - initialIndex: _searchResultController!.tabIndex, + initialIndex: _searchResultController.tabIndex, ); } @@ -46,7 +46,7 @@ class _SearchResultPageState extends State child: SizedBox( width: double.infinity, child: Text( - '${_searchResultController!.keyword}', + '${_searchResultController.keyword}', style: Theme.of(context).textTheme.titleMedium, ), ), @@ -64,35 +64,39 @@ class _SearchResultPageState extends State splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明 highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明 ), - child: TabBar( - controller: _tabController, - tabs: [ - for (var i in SearchType.values) Tab(text: i.label), - ], - isScrollable: true, - indicatorWeight: 0, - indicatorPadding: - const EdgeInsets.symmetric(horizontal: 3, vertical: 8), - indicator: BoxDecoration( - color: Theme.of(context).colorScheme.secondaryContainer, - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - indicatorSize: TabBarIndicatorSize.tab, - labelColor: Theme.of(context).colorScheme.onSecondaryContainer, - labelStyle: const TextStyle(fontSize: 13), - dividerColor: Colors.transparent, - unselectedLabelColor: Theme.of(context).colorScheme.outline, - tabAlignment: TabAlignment.start, - onTap: (index) { - if (index == _searchResultController!.tabIndex) { - Get.find( - tag: SearchType.values[index].type + - _searchResultController!.keyword!) - .animateToTop(); - } + child: Obx( + () => (TabBar( + controller: _tabController, + tabs: [ + for (var i in _searchResultController.searchTabs) + Tab(text: "${i['label']} ${i['count'] ?? ''}") + ], + isScrollable: true, + indicatorWeight: 0, + indicatorPadding: + const EdgeInsets.symmetric(horizontal: 3, vertical: 8), + indicator: BoxDecoration( + color: Theme.of(context).colorScheme.secondaryContainer, + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + indicatorSize: TabBarIndicatorSize.tab, + labelColor: + Theme.of(context).colorScheme.onSecondaryContainer, + labelStyle: const TextStyle(fontSize: 13), + dividerColor: Colors.transparent, + unselectedLabelColor: Theme.of(context).colorScheme.outline, + tabAlignment: TabAlignment.start, + onTap: (index) { + if (index == _searchResultController.tabIndex) { + Get.find( + tag: SearchType.values[index].type + + _searchResultController.keyword!) + .animateToTop(); + } - _searchResultController!.tabIndex = index; - }, + _searchResultController.tabIndex = index; + }, + )), ), ), ), @@ -102,7 +106,7 @@ class _SearchResultPageState extends State children: [ for (var i in SearchType.values) ...{ SearchPanel( - keyword: _searchResultController!.keyword, + keyword: _searchResultController.keyword, searchType: i, tag: DateTime.now().millisecondsSinceEpoch.toString(), ) From db824681fd6a8d9b53df047315e169c9df5f33df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 21:00:12 +0800 Subject: [PATCH 089/438] =?UTF-8?q?fix:=20=E9=80=80=E5=87=BA=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=8C=89=E9=92=AE=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 6a5f22ec..d359391e 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -334,7 +334,7 @@ class _PLVideoPlayerState extends State color: Colors.white, ), ), - fuc: () => _.triggerFullScreen(), + fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value), ), }; final List list = []; From 0162b9614e278fabd83066f35823d259f28db733 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 6 May 2024 23:14:12 +0800 Subject: [PATCH 090/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ef4cd15c..b5972ed8 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,6 +12,7 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -642,23 +643,25 @@ InlineSpan buildContent( '', ); } else { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - if (redirectUrl == matchStr) { - Clipboard.setData(ClipboardData(text: matchStr)); - SmartDialog.showToast('地址可能有误'); - return; - } - final String pathSegment = Uri.parse(redirectUrl).path; - final String lastPathSegment = - pathSegment.split('/').last; - if (lastPathSegment.startsWith('BV')) { + final String pathSegment = Uri.parse(matchStr).path; + Map matchRes = IdUtils.matchAvorBv(input: pathSegment); + List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { UrlUtils.matchUrlPush( - lastPathSegment, + matchRes.containsKey('AV') + ? matchRes['AV']! as int + : matchRes['BV'], title, - redirectUrl, + matchStr, ); } else { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + if (redirectUrl == matchStr) { + Clipboard.setData(ClipboardData(text: matchStr)); + SmartDialog.showToast('地址可能有误'); + return; + } Get.toNamed( '/webview', parameters: { From 2fefe325877f12bb99cbf29f0dcf9f334798b8de Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 6 May 2024 23:24:19 +0800 Subject: [PATCH 091/438] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=8D=87=E7=BA=A7=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index b381691a..81d3c3f4 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -218,7 +218,7 @@ class AboutController extends GetxController { RxString currentVersion = ''.obs; RxString remoteVersion = ''.obs; late LatestDataModel remoteAppInfo; - RxBool isUpdate = true.obs; + RxBool isUpdate = false.obs; RxBool isLoading = true.obs; late LatestDataModel data; From 4f10e6a869d1d61ea10343a00721d259a30572e7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 00:01:44 +0800 Subject: [PATCH 092/438] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E6=9C=AA?= =?UTF-8?q?=E8=AF=BB=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/controller.dart | 2 +- lib/pages/main/view.dart | 64 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index a77d9304..2ef2bf7f 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -40,10 +40,10 @@ class MainController extends GetxController { dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( SettingBoxKey.dynamicBadgeMode, defaultValue: DynamicBadgeMode.number.code)]; + setNavBarConfig(); if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { getUnreadDynamic(); } - setNavBarConfig(); } void onBackPressed(BuildContext context) { diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 617fcf83..29573501 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -138,14 +138,14 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), child: GlobalData().enableMYBar - ? NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: _mainController.selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: Obx( - () => Badge( + ? Obx( + () => NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: _mainController.selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: Badge( label: _mainController .dynamicBadgeType.value == DynamicBadgeMode.number @@ -159,25 +159,25 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { e['count'] > 0, child: e['icon'], ), - ), - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), ) - : BottomNavigationBar( - currentIndex: _mainController.selectedIndex, - type: BottomNavigationBarType.fixed, - onTap: (value) => setIndex(value), - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - items: [ - ..._mainController.navigationBars.map((e) { - return BottomNavigationBarItem( - icon: Obx( - () => Badge( + : Obx( + () => BottomNavigationBar( + currentIndex: _mainController.selectedIndex, + type: BottomNavigationBarType.fixed, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: Badge( label: _mainController .dynamicBadgeType.value == DynamicBadgeMode.number @@ -191,12 +191,12 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { e['count'] > 0, child: e['icon'], ), - ), - activeIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), ), ); }, From 7ff6884342be8d0bd54feed50c8fd8f9c3410717 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 23:10:27 +0800 Subject: [PATCH 093/438] =?UTF-8?q?fix:=20pathSegments=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/webview/controller.dart | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index f26f4284..40f84ff6 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -52,15 +52,19 @@ class WebviewController extends GetxController { loadProgress.value = progress; }, onPageStarted: (String url) { - final String str = Uri.parse(url).pathSegments[0]; - final Map matchRes = IdUtils.matchAvorBv(input: str); - final List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { - Get.offAndToNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, - ); + final List pathSegments = Uri.parse(url).pathSegments; + if (pathSegments.isNotEmpty && + url != 'https://passport.bilibili.com/h5-app/passport/login') { + final String str = pathSegments[0]; + final Map matchRes = IdUtils.matchAvorBv(input: str); + final List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { + if (matchKeys.first == 'BV') { + Get.offAndToNamed( + '/searchResult', + parameters: {'keyword': matchRes['BV']}, + ); + } } } }, From c2a176ad2fd5d20e4ad4aed1f68102b5425b45c2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 23:53:31 +0800 Subject: [PATCH 094/438] fix: issues #718 --- lib/services/audio_handler.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index b0ca8cd7..bf98298b 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController player = PlPlayerController.getInstance(); VideoPlayerServiceHandler() { revalidateSetting(); From 250131d2b0925fef686eb4634657bd80eab085bb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 22:42:18 +0800 Subject: [PATCH 095/438] =?UTF-8?q?fix:=20=E5=88=86p=E5=AD=97=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/controller.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 2ced79f0..9ed04870 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -450,6 +450,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); + videoDetailCtr.getSubtitle(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From 6380c138e8f0e2c85f6ae73b8b24deccd6d78b33 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 23:19:02 +0800 Subject: [PATCH 096/438] =?UTF-8?q?typo:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/interceptor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index a5359283..10b5aee5 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -46,7 +46,7 @@ class ApiInterceptor extends Interceptor { // 处理网络请求错误 // handler.next(err); String url = err.requestOptions.uri.toString(); - if (!url.contains('heartBeat')) { + if (!url.contains('heartbeat')) { SmartDialog.showToast( await dioError(err), displayType: SmartToastType.onlyRefresh, From 5807999296c0806309e3dd809c068764147ee9be Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 23:19:29 +0800 Subject: [PATCH 097/438] =?UTF-8?q?mod:=20scheme=20BVAV=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index fff78b39..8502dc7f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -4,7 +4,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/route_push.dart'; import '../http/search.dart'; -import '../models/common/search_type.dart'; import 'id_utils.dart'; import 'url_utils.dart'; import 'utils.dart'; @@ -140,11 +139,13 @@ class PiliSchame { print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; if (path.startsWith('/video')) { - if (lastPathSegment.contains('BV')) { - _videoPush(null, lastPathSegment); - } - if (lastPathSegment.contains('av')) { - _videoPush(Utils.matchNum(lastPathSegment)[0], null); + Map matchRes = IdUtils.matchAvorBv(input: path); + if (matchRes.containsKey('AV')) { + _videoPush(matchRes['AV']! as int, null); + } else if (matchRes.containsKey('BV')) { + _videoPush(null, matchRes['BV'] as String); + } else { + SmartDialog.showToast('投稿匹配失败'); } } if (path.startsWith('/bangumi')) { From 37654c5eab465672eba646855431eba229ed7f2e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 9 May 2024 00:00:59 +0800 Subject: [PATCH 098/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index b5972ed8..4ff13065 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -657,11 +657,11 @@ InlineSpan buildContent( } else { final String redirectUrl = await UrlUtils.parseRedirectUrl(matchStr); - if (redirectUrl == matchStr) { - Clipboard.setData(ClipboardData(text: matchStr)); - SmartDialog.showToast('地址可能有误'); - return; - } + // if (redirectUrl == matchStr) { + // Clipboard.setData(ClipboardData(text: matchStr)); + // SmartDialog.showToast('地址可能有误'); + // return; + // } Get.toNamed( '/webview', parameters: { From b8e3764345aaf66e2cbad0c73f8d5d2ce2e1f8d0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 10 May 2024 23:45:51 +0800 Subject: [PATCH 099/438] fix: login Box has already been closed --- lib/pages/webview/controller.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index 40f84ff6..bdacc652 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -110,6 +110,9 @@ class WebviewController extends GetxController { SmartDialog.showToast('登录成功'); try { Box userInfoCache = GStrorage.userInfo; + if (!userInfoCache.isOpen) { + userInfoCache = await Hive.openBox('userInfo'); + } await userInfoCache.put('userInfoCache', result['data']); final HomeController homeCtr = Get.find(); From 14b7ac438f25d97711a70301eb5ded8ad455da5a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 11 May 2024 23:50:41 +0800 Subject: [PATCH 100/438] =?UTF-8?q?fix:=20=E7=9B=B4=E6=92=AD=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/widgets/bottom_control.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index e5a9d6c9..4dd7c538 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -153,7 +153,8 @@ class _BottomControlState extends State { size: 20, color: Colors.white, ), - fuc: () => widget.controller!.triggerFullScreen(), + fuc: () => widget.controller!.triggerFullScreen( + status: !(widget.controller!.isFullScreen.value)), ), ], ), From 79bde1b7ff03158a36d18fd8885bc3fa080fd3b2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:16:25 +0800 Subject: [PATCH 101/438] =?UTF-8?q?mod:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E9=95=BF=E6=8C=89=E5=9C=86=E8=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index ba5f6b5c..00ebd511 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -198,7 +198,8 @@ class HistoryItem extends StatelessWidget { duration: const Duration(milliseconds: 200), child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular( + StyleString.imgRadius.x), color: Colors.black.withOpacity( ctr!.enableMultiple.value && videoItem.checked From 68d35a7ed8830c4b352389dd59c4aa60d335ac34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:37:22 +0800 Subject: [PATCH 102/438] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E5=88=86?= =?UTF-8?q?=E5=8C=BAUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 +- lib/http/search.dart | 2 + lib/pages/search/widgets/search_text.dart | 7 +- lib/pages/search_panel/controller.dart | 14 +- .../search_panel/widgets/video_panel.dart | 125 +++++++++++++++++- 5 files changed, 142 insertions(+), 8 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..77c168d0 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -189,7 +189,7 @@ class Api { 'https://s.search.bilibili.com/main/suggest'; // 分类搜索 - static const String searchByType = '/x/web-interface/search/type'; + static const String searchByType = '/x/web-interface/wbi/search/type'; // 记录视频播放进度 // https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/report.md diff --git a/lib/http/search.dart b/lib/http/search.dart index 9b493a84..70980547 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,6 +75,7 @@ class SearchHttp { required page, String? order, int? duration, + int? tid, }) async { var reqData = { 'search_type': searchType.type, @@ -84,6 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, + if (tid != null) 'tid': tid, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..5e91a4cd 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -5,12 +5,14 @@ class SearchText extends StatelessWidget { final Function? onSelect; final int? searchTextIdx; final Function? onLongSelect; + final bool isSelect; const SearchText({ super.key, this.searchText, this.onSelect, this.searchTextIdx, this.onLongSelect, + this.isSelect = false, }); @override @@ -34,7 +36,10 @@ class SearchText extends StatelessWidget { child: Text( searchText!, style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant), + color: isSelect + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), ), diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 85952e90..56d83601 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,14 +16,18 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; + // 视频分区筛选 仅用于搜索视频 + RxInt tid = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( - searchType: searchType!, - keyword: keyword!, - page: page.value, - order: searchType!.type != 'video' ? null : order.value, - duration: searchType!.type != 'video' ? null : duration.value); + searchType: searchType!, + keyword: keyword!, + page: page.value, + order: searchType!.type != 'video' ? null : order.value, + duration: searchType!.type != 'video' ? null : duration.value, + tid: searchType!.type != 'video' ? null : tid.value, + ); if (result['status']) { if (type == 'onRefresh') { resultList.value = result['data'].list; diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..06ee5d4b 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -3,6 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/search/widgets/search_text.dart'; import 'package:pilipala/pages/search_panel/index.dart'; class SearchVideoPanel extends StatelessWidget { @@ -94,7 +95,7 @@ class SearchVideoPanel extends StatelessWidget { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), ), - onPressed: () => controller.onShowFilterDialog(ctr), + onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( Icons.filter_list_outlined, size: 18, @@ -165,7 +166,33 @@ class VideoPanelController extends GetxController { {'label': '30-60分钟', 'value': 3}, {'label': '60分钟+', 'value': 4}, ]; + List> partFiltersList = [ + {'label': '全部', 'value': -1}, + {'label': '动画', 'value': 1}, + {'label': '番剧', 'value': 13}, + {'label': '国创', 'value': 167}, + {'label': '音乐', 'value': 3}, + {'label': '舞蹈', 'value': 129}, + {'label': '游戏', 'value': 4}, + {'label': '知识', 'value': 36}, + {'label': '科技', 'value': 188}, + {'label': '运动', 'value': 234}, + {'label': '汽车', 'value': 223}, + {'label': '生活', 'value': 160}, + {'label': '美食', 'value': 211}, + {'label': '动物', 'value': 217}, + {'label': '鬼畜', 'value': 119}, + {'label': '时尚', 'value': 155}, + {'label': '资讯', 'value': 202}, + {'label': '娱乐', 'value': 5}, + {'label': '影视', 'value': 181}, + {'label': '记录', 'value': 177}, + {'label': '电影', 'value': 23}, + {'label': '电视', 'value': 11}, + ]; + RxInt currentTimeFilterval = 0.obs; + RxInt currentPartFilterval = (-1).obs; @override void onInit() { @@ -219,4 +246,100 @@ class VideoPanelController extends GetxController { }, ); } + + onShowFilterSheet(searchPanelCtr) { + showModalBottomSheet( + context: Get.context!, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return Container( + color: Theme.of(Get.context!).colorScheme.surface, + padding: const EdgeInsets.only(top: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('内容时长'), + ), + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 14, + ), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in timeFiltersList) + Obx( + () => SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentTimeFilterval.value == i['value'], + onSelect: (value) async { + currentTimeFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.duration.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ), + ) + ], + ), + ), + const ListTile( + title: Text('内容分区'), + ), + Padding( + padding: const EdgeInsets.only(left: 14, right: 14), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in partFiltersList) + SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: currentPartFilterval.value == i['value'], + onSelect: (value) async { + currentPartFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.tid.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ) + ], + ), + ) + ], + ), + ); + }, + ); + }, + ); + } } From 31e8ee8d34023b6d541088836432eba556123853 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:55:17 +0800 Subject: [PATCH 103/438] =?UTF-8?q?fix:=20=E7=95=AA=E5=89=A7=E6=97=A0?= =?UTF-8?q?=E8=AF=84=E5=88=86=E5=AF=BC=E8=87=B4=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 6876da79..95d4d898 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -194,7 +194,8 @@ class _BangumiInfoState extends State { src: widget.bangumiDetail!.cover!, ), PBadge( - text: '评分 ${widget.bangumiDetail!.rating!['score']!}', + text: + '评分 ${widget.bangumiDetail?.rating?['score']! ?? '暂无'}', top: null, right: 6, bottom: 6, From 44f2a168ce79b62c315f5706152240c3396a1562 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 15 May 2024 23:55:04 +0800 Subject: [PATCH 104/438] =?UTF-8?q?opt:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=8D=95=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 3 +-- lib/pages/video/detail/controller.dart | 2 +- lib/plugin/pl_player/controller.dart | 19 +++++++++---------- lib/plugin/pl_player/view.dart | 10 +++++----- lib/services/audio_handler.dart | 2 +- lib/services/audio_session.dart | 4 ++-- lib/services/shutdown_timer_service.dart | 4 ++-- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 5c2a9800..4e67fa2c 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -17,8 +17,7 @@ class LiveRoomController extends GetxController { double volume = 0.0; // 静音状态 RxBool volumeOff = false.obs; - PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'live'); + PlPlayerController plPlayerController = PlPlayerController(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; late bool enableCDN; late int currentQn; diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index f7f9e848..722af317 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -74,7 +74,7 @@ class VideoDetailController extends GetxController final scaffoldKey = GlobalKey(); RxString bgCover = ''.obs; RxString cover = ''.obs; - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = PlPlayerController(); late VideoItem firstVideo; late AudioItem firstAudio; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 93d67703..03c2efbe 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -123,6 +123,7 @@ class PlPlayerController { PreferredSizeWidget? bottomControl; Widget? danmuWidget; late RxList subtitles; + String videoType = 'archive'; /// 数据加载监听 Stream get onDataStatusChanged => dataStatus.status.stream; @@ -220,7 +221,7 @@ class PlPlayerController { Rx get playerCount => _playerCount; /// - Rx get videoType => _videoType; + // Rx get videoType => _videoType; /// 弹幕开关 Rx isOpenDanmu = false.obs; @@ -274,8 +275,7 @@ class PlPlayerController { } // 添加一个私有构造函数 - PlPlayerController._() { - _videoType = videoType; + PlPlayerController._internal(this.videoType) { isOpenDanmu.value = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); blockTypes = @@ -330,11 +330,11 @@ class PlPlayerController { } // 获取实例 传参 - static PlPlayerController getInstance({ + factory PlPlayerController({ String videoType = 'archive', }) { // 如果实例尚未创建,则创建一个新实例 - _instance ??= PlPlayerController._(); + _instance ??= PlPlayerController._internal(videoType); if (videoType != 'none') { _instance!._playerCount.value += 1; _videoType.value = videoType; @@ -443,7 +443,7 @@ class PlPlayerController { configuration: PlayerConfiguration( // 默认缓存 5M 大小 bufferSize: - videoType.value == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024, + videoType == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024, ), ); @@ -541,7 +541,7 @@ class PlPlayerController { } /// 设置倍速 - if (videoType.value == 'live') { + if (videoType == 'live') { await setPlaybackSpeed(1.0); } else { if (_playbackSpeed.value != 1.0) { @@ -933,7 +933,7 @@ class PlPlayerController { /// 设置长按倍速状态 live模式下禁用 void setDoubleSpeedStatus(bool val) { - if (videoType.value == 'live') { + if (videoType == 'live') { return; } if (controlsLock.value) { @@ -1015,7 +1015,7 @@ class PlPlayerController { if (!_enableHeart) { return false; } - if (videoType.value == 'live') { + if (videoType == 'live') { return; } // 播放状态变化时,更新 @@ -1114,7 +1114,6 @@ class PlPlayerController { // _buffered.close(); // _showControls.close(); // _controlsLock.close(); - // playerStatus.status.close(); // dataStatus.status.close(); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d359391e..cff4611a 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -652,7 +652,7 @@ class _PLVideoPlayerState extends State }, onDoubleTapDown: (TapDownDetails details) { // live模式下禁用 锁定时🔒禁用 - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } final double totalWidth = MediaQuery.sizeOf(context).width; @@ -679,7 +679,7 @@ class _PLVideoPlayerState extends State /// 水平位置 快进 live模式下禁用 onHorizontalDragUpdate: (DragUpdateDetails details) { // live模式下禁用 锁定时🔒禁用 - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } // final double tapPosition = details.localPosition.dx; @@ -695,7 +695,7 @@ class _PLVideoPlayerState extends State _.onChangedSliderStart(); }, onHorizontalDragEnd: (DragEndDetails details) { - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } _.onChangedSliderEnd(); @@ -826,7 +826,7 @@ class _PLVideoPlayerState extends State return const SizedBox(); } - if (_.videoType.value == 'live') { + if (_.videoType == 'live') { return const SizedBox(); } if (value > max || max <= 0) { @@ -879,7 +879,7 @@ class _PLVideoPlayerState extends State // 锁 Obx( () => Visibility( - visible: _.videoType.value != 'live' && _.isFullScreen.value, + visible: _.videoType != 'live' && _.isFullScreen.value, child: Align( alignment: Alignment.centerLeft, child: FractionalTranslation( diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index bf98298b..853c58d0 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(); + PlPlayerController player = PlPlayerController(); VideoPlayerServiceHandler() { revalidateSetting(); diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 53b497ae..d1d2a466 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -18,7 +18,7 @@ class AudioSessionHandler { session.configure(const AudioSessionConfiguration.music()); session.interruptionEventStream.listen((event) { - final player = PlPlayerController.getInstance(videoType: 'none'); + final player = PlPlayerController(videoType: 'none'); if (event.begin) { if (!player.playerStatus.playing) return; switch (event.type) { @@ -51,7 +51,7 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - final player = PlPlayerController.getInstance(videoType: 'none'); + final player = PlPlayerController(videoType: 'none'); if (player.playerStatus.playing) { player.pause(); } diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart index 156b63c8..06993f62 100644 --- a/lib/services/shutdown_timer_service.dart +++ b/lib/services/shutdown_timer_service.dart @@ -89,7 +89,7 @@ class ShutdownTimerService { return; } PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController(videoType: 'none'); if (!exitApp && !waitForPlayingCompleted) { if (!plPlayerController.playerStatus.playing) { //仅提示用户 @@ -124,7 +124,7 @@ class ShutdownTimerService { } else { //暂停播放 PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController(videoType: 'none'); if (plPlayerController.playerStatus.playing) { plPlayerController.pause(); waitForPlayingCompleted = true; From f23a21f6dce39cffc0fb2879a321d3f3b514ba05 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 16 May 2024 23:04:47 +0800 Subject: [PATCH 105/438] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/dynamics/result.dart | 3 + lib/pages/dynamics/widgets/forward_panel.dart | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index bc7105d1..64a6e5b1 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -415,6 +415,7 @@ class DynamicMajorModel { this.type, this.courses, this.common, + this.music, }); DynamicArchiveModel? archive; @@ -431,6 +432,7 @@ class DynamicMajorModel { String? type; Map? courses; Map? common; + Map? music; DynamicMajorModel.fromJson(Map json) { archive = json['archive'] != null @@ -455,6 +457,7 @@ class DynamicMajorModel { type = json['type']; courses = json['courses'] ?? {}; common = json['common'] ?? {}; + music = json['music'] ?? {}; } } diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart index 76c7b24e..f8b90a81 100644 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -238,6 +238,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { ), ), ); + case 'DYNAMIC_TYPE_MUSIC': + final Map music = item.modules.moduleDynamic.major.music; + return Padding( + padding: const EdgeInsets.only(top: 8), + child: InkWell( + onTap: () { + Get.toNamed('/webview', parameters: { + 'url': "https:${music['jump_url']}", + 'type': 'url', + 'pageTitle': music['title'] + }); + }, + child: Container( + width: double.infinity, + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: Row( + children: [ + NetworkImgLayer( + width: 45, + height: 45, + src: music['cover'], + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + music['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + music['label'], + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ) + ], + ), + // TextButton(onPressed: () {}, child: Text('123')) + ), + ), + ); default: return const SizedBox( width: double.infinity, From eae39c480c9f99887695f1755fd6acb36f5720bc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 18 May 2024 19:29:57 +0800 Subject: [PATCH 106/438] =?UTF-8?q?mod:=20playInit=20autoplay=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 4 ++-- lib/pages/video/detail/view.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 722af317..38c62d7e 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -233,7 +233,7 @@ class VideoDetailController extends GetxController audio, seekToTime, duration, - bool autoplay = true, + bool? autoplay, }) async { /// 设置/恢复 屏幕亮度 if (brightness != null) { @@ -266,7 +266,7 @@ class VideoDetailController extends GetxController cid: cid.value, enableHeart: enableHeart, isFirstTime: isFirstTime, - autoplay: autoplay, + autoplay: autoplay ?? autoPlay.value, ); /// 开启自动全屏时,在player初始化完成后立即传入headerControl diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 2c35858f..6f751e52 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -176,7 +176,7 @@ class _VideoDetailPageState extends State /// 未开启自动播放时触发播放 Future handlePlay() async { - await vdCtr.playerInit(); + await vdCtr.playerInit(autoplay: true); plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; @@ -266,7 +266,7 @@ class _VideoDetailPageState extends State } vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; - vdCtr.playerInit(autoplay: autoplay); + vdCtr.playerInit(); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 vdCtr.autoPlay.value = !vdCtr.isShowCover.value; From cb7cd0a2f0658ca84d48b213760aee80decff555 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 19 May 2024 19:57:13 +0800 Subject: [PATCH 107/438] feat: videoDetail menu edit --- assets/images/coin.png | Bin 0 -> 7159 bytes lib/models/common/action_type.dart | 94 ++++++++++++++++ lib/pages/setting/pages/action_menu_set.dart | 100 ++++++++++++++++++ lib/pages/setting/style_setting.dart | 5 + lib/pages/video/detail/introduction/view.dart | 28 ++--- .../introduction/widgets/action_item.dart | 54 ++++++---- lib/router/app_pages.dart | 4 + lib/utils/storage.dart | 3 +- 8 files changed, 249 insertions(+), 39 deletions(-) create mode 100644 assets/images/coin.png create mode 100644 lib/models/common/action_type.dart create mode 100644 lib/pages/setting/pages/action_menu_set.dart diff --git a/assets/images/coin.png b/assets/images/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2952a7a5cf519a331b83c0f268eef88769d471 GIT binary patch literal 7159 zcmai3XEa<vI?2oo<*AX$uJ|9 zbE)c;5wTPqLBNLDXyI`F_$sdFW^)=nesPzJh7-d7Tk-$I#4N)}`MRL7DXk^Ce63JJ z$@|x~Dt)%t|F(sSB|<)Jwbk{iJW~8(_(!4PBksrHCfo7KN9jac37>Zv1N(QM&AyBK zr6Q{qIyv=nqi|5paxTn8tu9Ps`T@6=^q%B)@V@A&uz#M=&%bz?S5^*F<32*K3>Lq3 zVXCYAgH*bFr5u6k<-WPOe*5v6)S&0*WP^jaVU`j396F&bw*nD7yp@`KZ zf_Bfei3C%9{_H8dcljj8=IFOlR=nC(1>f%J2`k5Joj9#YKV0weokY&;Ba0H6&ZavJ z-`kJUtqiVj-R64NcxCU+tIcgOuD!4d!s@}{U5PNqxU%EJs~0&lomZ&`ck18=J=A); zb?~oe*?p-K*30XWEGdh}g@sRLjkpq!)vYGro@xZx9t*R_g`PET$k5@)GQTZ9J^Ip3 zhi=jp_$_R~_P|jrsiCnAy?(ms>v?kAYr%H%_Zc&~C#(w6&{!%z0lH5u2k{X5QrdV? z>xSUJRZ<+M1Tb_)0bVO6@f26cL}69`JOaG|*3rf&If+G(*Y-T_=f3;wHgy@fWS)E& zHluGHXednEwVc-1VNG-0^Lk9BHQQqGkTFM?w(8#C9bi$K>jVvL?rb^II?Ez=s94`P z|DpD2@$y-3KQj|!lYV#<|FExhgWWbZY_vAw`%md~ed)g9;CYj2=Oa~3!(Ri{A-X8v zpx$XkRN%?|H7(bPeTDT$U2Rn5Pt08M4%Dr=hUddQ3bO}vgt~_!e--cCO+GX`r6@mF z4)W?sy!%7r`bKCnc77>HZbRWXU4Qf!i{94oamm_O@#VKk*x?6i_IbSE{(qqL@pX>6g6tMuMySM63bXjKKQW`zugF43{dpMj=NYW(uw%8RJ+=#W zI?~pKSmdm9yVR-Bt?)~To7;!c|EzB$#BF(@wND79z8upagtno+y&?wL1Om$oC)Mkz?W>meIH|OtG$Eu09UW5x;^+{3Pb=)_j+O07=RCt<= zvDJ3?+-3010YS=Mw`{sCT}H(Vj8IsLVh$DT(sSUnyxOSFSSV_e;wcryI^ye~*LfKWB*0r%2$vcM19va@SGLQVv5rKjb7b-dKIVwZtba{LV?Nauw z4~NDSM}5AC{0d;OV4IsBZW5tZ;O)rno#Li7l~M2ob00O-f|9b_@h@czYKS^W*^iiF z8Esz>@pwWp6&sLaJH&b$=P@Q_@`O+0$9gxKdvw2I16+{7=P&FjN*)+bsfk0h^`^>Y$s z1Jl{Rze6gqdDR;=?7fNt1SYa`L%)B^8jU_a>8lF|`!Pxs@J@1uy!=`;zD(PFYkhQm zhqhbq(~@455`m}W;==ln7oTFf1hALu97-*24?!-b8%C-e2;|NQ_$GFB(7W9kU59@N z)6IT0UUMUwXIY{111C|1&)!n|fcE;n)ld*K>*r#LNCb zNDed2a>}ny*$^chgGqo-gJcIKPu7@TPaB?3sSNzuW`>pPiJa;N)ml43RU@x~Hp$PD<*cS3_SGnAH+P{(iOgzWiuelm zy;)VB^g6EwzBe=8r-$}bJ#h>aWW=$aC%E=(R+|mAJJx?}+2nS&yHE|}r}<_O$y7J4+{c!?hJv;GL$Qx-qLBx5v2?txK2h=+ z#IJ9zS)3%_ZJg0of3vwC)xJ?KRVM)R#<{Bf{4r!98A36E&b~=zN8rh9apQ~mfI4Q& zl9EWHY{TE+aQnRZ(ZYtnlilpbM`D~A4WP5JcJ8<{ngMj24hFJ2N!k0C=cAl0K_eTZ zt8|s*nLV>nHyCjHRFUN@+2|^Xsh8`ja9RhsWa#$(OkM0^OMr3Y;K>6dp&Ht75<@Zu z!i%bN`?*JQ>S830vo~#l&QomI(bRIh+cRbQtH7p$vyzgKBnH%0<3Uf&ad9=e!JCdq z3GoodNF`M%Vg$Llzj0}f4?8ZoS{E893BBNrRYj*bm2z1af$&Yz8ZC2_;QFcc`ApgfE@m_6@j3NSowGS>vcBjyf(feK zguOKg%X=Epv;?eJ+yLx6jm>F-Z|6@5{qjzVj^P>QWv%w?sg#7s6!o6mOL9(b0!`1eQFzt7 z{wCnv&gTR~k6phrGQZ$|4P~nvHPiM!DZ~n_A z4>-!;5yStQTKT3*2Kz~0@txrO*UwltY>P7MX&8gB&2Mv=Hi}hpSgS2nMz!YE`f5Ef z{To&zQPW?zohc;Mu)k6;!g$Kb=dAW1yDb~x4;J!$>T4B-g1L<;8z3Gtz~?ZVwo}`` z=ZR*?Qcv>#U{5ZNoB%Q-6uz2$(=haqpperZP%$u8q0qYzlW+dzeMb5Y7yMbAqHDPV z(B&ZfhXSTXKyp*q95nLy;jE|on>S!zy+fOTNawa$7Y>dMfN84C=(Sq_a%JpNs?7K` zirCMB2yi#NhlHr9!=s0!G-)VgFvhdPgvzLtSD?9(#7 znZYH~qFt1M0SiOU-+rLWm4ie#d<9N|E^=bIWoiab4f_7G_PYrz?5(ra+nNWJai!Kc z3aAA~oIkx_?jG~w{0cA(e4zp1eZV@bu|{J=jG4VOfemZCpr|~y(d)%EkFFp$ZlJ*z zoLsvDr!_gZTSohkQ^p57p>ok9&S$YJ&5y|noJ*tq%AeBFvt6sx#r{cN`tjxMZ*B}V z{_5IGl{Fr$+g(bV$EEZyw^fc$uNf-4KfC9~T(opPy!jgNsMDM6eRCdt$MBo+Lwm(6 ziUmXZY%ch<kP$X5Z#1YG)4EL>;v@HohmWj$TvEO`G}z} zYVkwBeo&MYQ2CQ&A=sgbW-$xE#U&$-Z@>PagwI9ia<@<66W-Ac(9Vwddl_R5h!CBc zG3K{kL(FVG?M8#RxCzhOZWWJJ04VgU`hks8Ysy(&VTKk<4&!z~2Y_BQSX~)E1!xDw zjZhS5iqhhNJa9`oI76`NxlhZBOVLv^26!eH;Xb15DkKN2{*^+x6nt@)5w3)i&RHwt zQboiRTkG>>88hLm&*R^zDW)<%LjjT^#IZmUj4A#$wo7v>s@Dt)x+uDf{hLTspnLvV6%+(ehY}w; zmDXjAH9@v_To}8w#qmpTPoIYf&1}4u>wMgSLK%m!^H6Hvqp>?#&;ipCmB69~I= zP@-4DjsyVY4qWP|N}u$C6Pa zyr3P!P383j*$5`MBdZ8j*h!B!{YKGMTkJI?Nr;+j0dNJVw(v2(QWOtGChlmb^a6lg zC*Vy`187H|qcuIw&&>zKib3?2@B4d=I3f|tE=TS#Lb6{FoXnnV8(!p+{|LB5$;Az{ z2A+ikYYI^lb@B4v0??(6Z)6L!Nz#a(@Wm~_d`BeR`P(5|EYG6-v%-`dpfFoZx|R)= zhuTCF&VuQe4f!WSUSJRO1L?LT@x~wn`^b_8B1pf-g^J8Y=6W3qP?Ix+MF#y5GYXVs zzKgYC?AM?$J5&V5U}?^{KJMlrn%Mf;XlVM-kfb*+_j1mf#LdW(1kI9glLva(V}Ras zF{)ERm`uhjH$m8G=je1&Wr!^4y>g`>nKHEF2GZ3)d-^%Q6Cjra<`%`BR@x6yDmBy{ zHxkQrZYkWy{uxyWN-Ck&iKNU~( zWp)o*2#?mOWTYj1%yvQZY>#qXKaek|QheO(^CysR0;xjTK}l3c){!iQu@FM}trG|C zqY5YrCR2^jqeeTmTMjzX|*d>wip!(kJ z!p}ZGWEgbn^dT6EXIQ{dF&V4f$)GFc1>o6F*#N?qI-mq9NVB>2W%IJvD((_Ov>zC; zu{ehOwMC3t{exWrr>uB6A3XckT-Ko<(By@ar-Nz~l#rN2z7VMueFfhr{wL{>qc8VT za4)%7;@3;z(18rs?VL0QEcpD3zelnAO%41*Jxptp02w{oQt!yIhddsb>ZGuGBcG;& zCe_KaJv4fb1KcqwV3SW|970g^gl~qV3WF7Ipei19>cs%90ZzXU1|?=Vpp*nBPwXgP8yQTOq*RrTP3P>%daQQbea)PLeks}Xrr+6LA*Ka(u5c}R5qmD52lqO8pTp!r6U29u7)!;n}G=UUpKi0-g@d0#GkcZcR&@3mbP6tml39i)NU^5%gvTlEWZ)^j?U!1zv=LD z!cwU^j(5y)=1!4??g7o-RxJ@awi=BZ4I`dZMR1?u?4Yfu((sKL{p)@z`uM8nN^5c^K%u4`D8*BuHq@?_UtvzAyTb`C1qB-=ha`Nx9t3_oxpo6^h1 zLM2H4wb#om6&YhU*B0U$ZCn{SJPNu9LdmLBv_B-54aox;Io4ZZr9HA#Llq594)q}s z+cQB$`Ze5Z{@TNP4~)m;&mmcs2b5Y(vE&1i%W@XGUhgHH_fpNnD!&_Ng!Rvttj~Ri zNGcPl#OH#pRBRk(9sH(aeY|t8Fn{1(j_%`RG1ESs!f+A&cf!ONSwps)>!a zVZ*@Vw=oUde@rIMA9m=!>^7h6(R}C;9$^*&KBeOG^$(0(%Gjin-dU9vwE021SmV3A z3pQF;IZ#Oc!*hWuKZ1EpRWFD{=m*1t#}vz7#veSv>qt)Ccay~l3?j}M_W^Sb> zanl})m_^+3yBa@tnNLoh!B$qRt9@J-uDM^O)Yw_e5fK!-#Hhf<4~$C1tYDZ+VR*j2 z{AXn_JknafKU@GSfFq;bO$J00#NCOj=YfX{vw@|#%eclfp=l9mVYiTjtw?C3fz@>` z64MaIIvWz}K}O^ZXYc3Z0QPjEN28^=OFvS~3bHRx_2$Opi5)U+-2V1_n7e#)<_Bm& zPx&(X!U8W>knt#XH`GULXw_N{>k=#HTX{r7$qnCfh zx#>2(>CSNv;(8i+@0HuB6L)}((vj_K;ACWkoW_Gkca-~j4<3I0x4&E;JFSuX-cPN1 zPTh(Ela&}+8UF9VvIY8T5A@WKUU-)00i*sUX0gY_>-?{$-IJLMSGW$I2ivT{Oio1U z9K+mo%PCIl%UPt$X72CNc{zD~9%J>anzm~xLt4ovwS|KGIL&hCdKI7CUGl6pHbbD{R_;~c|h%se}} zrD8#cvGbkY-XL9k?27}j;imls*~h=d@@E$;iY=ulsFvNxE(CR+#E=Z4$~mA#@or`07naUm?|czbR&_QxkoW|i^+tLG?WF2;EYGN+Xu zq{gQryB(ta#Js$)LG(M@QD^CaMY$*DJ?*4ZBwR+z1Jj!7e9TGb-_M0z8I+;Gv^lR? z)8teeQF2KBc46zCd*)gEhIaC%ql<(yDSIs+^?_Cn;Kf;qd{u&}$2A5?Bs==_6Q>lD z{wGM17^~LvGEx5({$?LdGm?b%>LS}?NmC1#2Asc6Xn(J@63(7#_mS#17$zM&d#6xK zRZYf(wPmrB);c{Isu~g(iD{%>353^R*Ho(LUkQ;?-yx zwd~$Igjqu3uA7dwQG^J?w=jk!&+A2roChWuZy(m~Q(KKkC8DPWUb}`eKP5Kkiu(Ac ztUt)act5wB=+d~|3OuN7HtL$%QB0$rT6|^H@07(_lJNDBf7^$r+dNc$%~nDcwuQAx zsqVpgo+C)J1?jgwX)jscx&ElNw3f?%S@~`1<8Xo56>eiau0M1B+ZQO?|Gpn1&hT(C l{=<}~mvg`V-P`>?i}iJ^lzFK-(e&S({%sST25l$Qe*pGzPR;-T literal 0 HcmV?d00001 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart new file mode 100644 index 00000000..2284df6c --- /dev/null +++ b/lib/models/common/action_type.dart @@ -0,0 +1,94 @@ +// 操作类型的枚举值:点赞 不喜欢 收藏 投币 稍后再看 下载封面 后台播放 听视频 分享 下载视频 +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +enum ActionType { + like, + coin, + collect, + watchLater, + share, + dislike, + downloadCover, + copyLink, + threeAction, + // backgroundPlay, + // listenVideo, + // downloadVideo, +} + +extension ActionTypeExtension on ActionType { + String get value => [ + 'like', + 'coin', + 'collect', + 'watchLater', + 'share', + 'dislike', + 'downloadCover', + 'copyLink', + // 'backgroundPlay', + // 'listenVideo', + // 'downloadVideo', + ][index]; + String get label => [ + '点赞视频', + '投币', + '收藏视频', + '稍后再看', + '视频分享', + '不喜欢', + '下载封面', + '复制链接', + // '后台播放', + // '听视频', + // '下载视频', + ][index]; +} + +List actionMenuConfig = [ + { + 'icon': const Icon(Icons.thumb_up_alt_outlined), + 'label': '点赞视频', + 'value': ActionType.like, + }, + { + 'icon': Image.asset( + 'assets/images/coin.png', + width: 26, + color: IconTheme.of(Get.context!).color!.withOpacity(0.65), + ), + 'label': '投币', + 'value': ActionType.coin, + }, + { + 'icon': const Icon(Icons.star_border), + 'label': '收藏视频', + 'value': ActionType.collect, + }, + { + 'icon': const Icon(Icons.watch_later_outlined), + 'label': '稍后再看', + 'value': ActionType.watchLater, + }, + { + 'icon': const Icon(Icons.share), + 'label': '视频分享', + 'value': ActionType.share, + }, + { + 'icon': const Icon(Icons.thumb_down_alt_outlined), + 'label': '不喜欢', + 'value': ActionType.dislike, + }, + { + 'icon': const Icon(Icons.image_outlined), + 'label': '下载封面', + 'value': ActionType.downloadCover, + }, + { + 'icon': const Icon(Icons.link_outlined), + 'label': '复制链接', + 'value': ActionType.copyLink, + }, +]; diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart new file mode 100644 index 00000000..f7d92e17 --- /dev/null +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/action_type.dart'; +import '../../../utils/storage.dart'; + +class ActionMenuSetPage extends StatefulWidget { + const ActionMenuSetPage({super.key}); + + @override + State createState() => _ActionMenuSetPageState(); +} + +class _ActionMenuSetPageState extends State { + Box settingStorage = GStrorage.setting; + late List actionTypeSort; + late List allLabels; + + @override + void initState() { + super.initState(); + actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); + allLabels = actionMenuConfig; + allLabels.sort((a, b) { + int indexA = actionTypeSort.indexOf((a['value'] as ActionType).value); + int indexB = actionTypeSort.indexOf((b['value'] as ActionType).value); + if (indexA == -1) indexA = actionTypeSort.length; + if (indexB == -1) indexB = actionTypeSort.length; + return indexA.compareTo(indexB); + }); + } + + void saveEdit() { + List sortedTabbar = allLabels + .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) + .map((i) => (i['value'] as ActionType).value) + .toList(); + settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); + SmartDialog.showToast('保存成功,下次启动时生效'); + } + + void onReorder(int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final tabsItem = allLabels.removeAt(oldIndex); + allLabels.insert(newIndex, tabsItem); + }); + } + + @override + Widget build(BuildContext context) { + final listTiles = [ + for (int i = 0; i < allLabels.length; i++) ...[ + CheckboxListTile( + key: Key((allLabels[i]['value'] as ActionType).value), + value: actionTypeSort + .contains((allLabels[i]['value'] as ActionType).value), + onChanged: (bool? newValue) { + String actionTypeId = (allLabels[i]['value'] as ActionType).value; + if (!newValue!) { + actionTypeSort.remove(actionTypeId); + } else { + actionTypeSort.add(actionTypeId); + } + setState(() {}); + }, + title: Row( + children: [ + allLabels[i]['icon'], + const SizedBox(width: 8), + Text(allLabels[i]['label']), + ], + ), + secondary: const Icon(Icons.drag_indicator_rounded), + ) + ] + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('视频操作菜单'), + actions: [ + TextButton(onPressed: () => saveEdit(), child: const Text('保存')), + const SizedBox(width: 12) + ], + ), + body: ReorderableListView( + onReorder: onReorder, + physics: const NeverScrollableScrollPhysics(), + footer: SizedBox( + height: MediaQuery.of(context).padding.bottom + 30, + ), + children: listTiles, + ), + ); + } +} diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..20fdada0 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,6 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/actionMenuSet'), + title: Text('操作菜单设置', style: titleStyle), + ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..dab81f18 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,7 +1,6 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -539,26 +538,21 @@ class _VideoInfoState extends State with TickerProviderStateMixin { builder: (BuildContext context, BoxConstraints constraints) { return Container( margin: const EdgeInsets.only(top: 6, bottom: 4), - height: constraints.maxWidth / 5 * 0.8, - child: GridView.count( - physics: const NeverScrollableScrollPhysics(), - primary: false, - padding: EdgeInsets.zero, - crossAxisCount: 5, - childAspectRatio: 1.25, - children: [ + height: constraints.maxWidth / 5, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + icon: const Icon(Icons.thumb_up_alt_outlined), + selectIcon: const Icon(Icons.thumb_up), onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), + icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, text: widget.videoDetail!.stat!.coin!.toString(), @@ -566,8 +560,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), onTap: () => showFavBottomSheet(), onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, @@ -575,7 +569,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.clock), + icon: const Icon(Icons.watch_later_outlined), onTap: () async { final res = await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); @@ -585,7 +579,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { text: '稍后看', ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), + icon: const Icon(Icons.share), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, text: '分享', diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 022d9223..2a89afe7 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/utils/feed_back.dart'; class ActionItem extends StatelessWidget { - final Icon? icon; + final dynamic icon; final Icon? selectIcon; final Function? onTap; final Function? onLongPress; @@ -31,26 +32,37 @@ class ActionItem extends StatelessWidget { if (onLongPress != null) {onLongPress!()} }, borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 18, color: Theme.of(context).colorScheme.primary) - : Icon(icon!.icon!, - size: 18, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 6), - Text( - text ?? '', - style: TextStyle( - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - ), - ) - ], + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + icon is Icon + ? Icon( + selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + const SizedBox(height: 6), + Text( + text ?? '', + style: TextStyle( + color: + selectStatus ? Theme.of(context).colorScheme.primary : null, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), ), ); } diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 7fda1bd8..2ca333f8 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -35,6 +35,7 @@ import '../pages/search/index.dart'; import '../pages/search_result/index.dart'; import '../pages/setting/extra_setting.dart'; import '../pages/setting/index.dart'; +import '../pages/setting/pages/action_menu_set.dart'; import '../pages/setting/pages/color_select.dart'; import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/font_size_select.dart'; @@ -174,6 +175,9 @@ class Routes { // navigation bar CustomGetPage( name: '/navbarSetting', page: () => const NavigationBarSetPage()), + // 操作菜单 + CustomGetPage( + name: '/actionMenuSet', page: () => const ActionMenuSetPage()), ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 29cf1846..4a163446 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -149,7 +149,8 @@ class SettingBoxKey { tabbarSort = 'tabbarSort', // 首页tabbar dynamicBadgeMode = 'dynamicBadgeMode', enableGradientBg = 'enableGradientBg', - navBarSort = 'navBarSort'; + navBarSort = 'navBarSort', + actionTypeSort = 'actionTypeSort'; } class LocalCacheKey { From ed28e98bbae2d38971ae2ae16c15871e2f8c5c84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 20 May 2024 23:45:38 +0800 Subject: [PATCH 108/438] =?UTF-8?q?opt:=20=E6=8E=A8=E8=8D=90=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=8B=89=E9=BB=91=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 37 ++++++++++++++++++++++------ lib/http/video.dart | 10 ++++++-- lib/pages/rcmd/controller.dart | 7 ++++++ lib/pages/rcmd/view.dart | 1 + 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 7b56152f..14476cdf 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -20,11 +20,13 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; + final Function? blockUserCb; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, + this.blockUserCb, }) : super(key: key); bool isStringNumeric(String str) { @@ -157,7 +159,11 @@ class VideoCardV extends StatelessWidget { ); }), ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + VideoContent( + videoItem: videoItem, + crossAxisCount: crossAxisCount, + blockUserCb: blockUserCb, + ) ], ), ); @@ -167,9 +173,14 @@ class VideoCardV extends StatelessWidget { class VideoContent extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - const VideoContent( - {Key? key, required this.videoItem, required this.crossAxisCount}) - : super(key: key); + final Function? blockUserCb; + + const VideoContent({ + Key? key, + required this.videoItem, + required this.crossAxisCount, + this.blockUserCb, + }) : super(key: key); Widget _buildBadge(String text, String type, [double fs = 12]) { return PBadge( @@ -241,7 +252,10 @@ class VideoContent extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(videoItem: videoItem); + return MorePanel( + videoItem: videoItem, + blockUserCb: blockUserCb, + ); }, ); }, @@ -297,11 +311,17 @@ class VideoStat extends StatelessWidget { class MorePanel extends StatelessWidget { final dynamic videoItem; - const MorePanel({super.key, required this.videoItem}); + final Function? blockUserCb; + const MorePanel({ + super.key, + required this.videoItem, + this.blockUserCb, + }); Future menuActionHandler(String type) async { switch (type) { case 'block': + Get.back(); blockUser(); break; case 'watchLater': @@ -338,7 +358,10 @@ class MorePanel extends StatelessWidget { reSrc: 11, ); SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); + if (res['status']) { + blockUserCb?.call(videoItem.owner.mid); + } + SmartDialog.showToast(res['msg']); }, child: const Text('确认'), ) diff --git a/lib/http/video.dart b/lib/http/video.dart index bf5921d6..7c1d9ba6 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -387,9 +387,15 @@ class VideoHttp { 'csrf': await Request.getCsrf(), }); if (res.data['code'] == 0) { - return {'status': true, 'data': res.data['data']}; + if (act == 5) { + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); + blackMidsList.add(mid); + setting.put(SettingBoxKey.blackMidsList, blackMidsList); + } + return {'status': true, 'data': res.data['data'], 'msg': '成功'}; } else { - return {'status': false, 'data': []}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 28ff055b..2d606b12 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/video.dart'; @@ -106,4 +107,10 @@ class RcmdController extends GetxController { duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); } } + + void blockUserCb(mid) { + videoList.removeWhere((e) => e.owner.mid == mid); + videoList.refresh(); + SmartDialog.showToast('已移除相关视频'); + } } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 67567870..29a8d469 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -146,6 +146,7 @@ class _RcmdPageState extends State ? VideoCardV( videoItem: videoList[index], crossAxisCount: crossAxisCount, + blockUserCb: (mid) => ctr.blockUserCb(mid), ) : const VideoCardVSkeleton(); }, From 2dfd2d4dc16d42ffbf42d6ed9c112066c8177e2e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 23 May 2024 00:32:59 +0800 Subject: [PATCH 109/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=A0=8F=E5=AE=89=E5=85=A8=E5=8C=BA=E5=9F=9F=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 46 +++++++++---------- lib/plugin/pl_player/widgets/app_bar_ani.dart | 4 ++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index cff4611a..ff0888be 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -768,37 +768,33 @@ class _PLVideoPlayerState extends State ), // 头部、底部控制条 - SafeArea( - top: false, - bottom: false, - child: Obx( - () => Column( - children: [ - if (widget.headerControl != null || _.headerControl != null) - ClipRect( - child: AppBarAni( - controller: animationController, - visible: !_.controlsLock.value && _.showControls.value, - position: 'top', - child: widget.headerControl ?? _.headerControl!, - ), - ), - const Spacer(), + Obx( + () => Column( + children: [ + if (widget.headerControl != null || _.headerControl != null) ClipRect( child: AppBarAni( controller: animationController, visible: !_.controlsLock.value && _.showControls.value, - position: 'bottom', - child: widget.bottomControl ?? - BottomControl( - controller: widget.controller, - triggerFullScreen: _.triggerFullScreen, - buildBottomControl: buildBottomControl(), - ), + position: 'top', + child: widget.headerControl ?? _.headerControl!, ), ), - ], - ), + const Spacer(), + ClipRect( + child: AppBarAni( + controller: animationController, + visible: !_.controlsLock.value && _.showControls.value, + position: 'bottom', + child: widget.bottomControl ?? + BottomControl( + controller: widget.controller, + triggerFullScreen: _.triggerFullScreen, + buildBottomControl: buildBottomControl(), + ), + ), + ), + ], ), ), diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 53eaad16..a1db5807 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -29,6 +29,10 @@ class AppBarAni extends StatelessWidget implements PreferredSizeWidget { curve: Curves.linear, )), child: Container( + padding: EdgeInsets.only( + left: MediaQuery.of(context).padding.left, + right: MediaQuery.of(context).padding.right, + ), decoration: BoxDecoration( gradient: position! == 'top' ? const LinearGradient( From c387cb83e7809c36456c85a82ccb1fa930d2a19f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 24 May 2024 23:54:25 +0800 Subject: [PATCH 110/438] =?UTF-8?q?mod:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/media/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 6541680a..cc413e59 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -105,7 +105,7 @@ class _MediaPageState extends State color: Theme.of(context).dividerColor.withOpacity(0.1), ), ListTile( - onTap: () {}, + onTap: () => Get.toNamed('/fav'), leading: null, dense: true, title: Padding( From 732beb38a71a20cb2980d3c40d473b4b64b68fd1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 15:31:00 +0800 Subject: [PATCH 111/438] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 41 ++++++------------- lib/utils/app_scheme.dart | 24 +++++++++-- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 4ff13065..55c16f7d 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,4 @@ +import 'package:appscheme/appscheme.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,6 +12,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; +import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -643,34 +645,17 @@ InlineSpan buildContent( '', ); } else { - final String pathSegment = Uri.parse(matchStr).path; - Map matchRes = IdUtils.matchAvorBv(input: pathSegment); - List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - UrlUtils.matchUrlPush( - matchRes.containsKey('AV') - ? matchRes['AV']! as int - : matchRes['BV'], - title, - matchStr, - ); - } else { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - // if (redirectUrl == matchStr) { - // Clipboard.setData(ClipboardData(text: matchStr)); - // SmartDialog.showToast('地址可能有误'); - // return; - // } - Get.toNamed( - '/webview', - parameters: { - 'url': redirectUrl, - 'type': 'url', - 'pageTitle': title - }, - ); - } + Uri uri = Uri.parse(matchStr); + SchemeEntity scheme = SchemeEntity( + scheme: uri.scheme, + host: uri.host, + port: uri.port, + path: uri.path, + query: uri.queryParameters, + source: '', + dataString: matchStr, + ); + PiliSchame.fullPathPush(scheme); } } else { if (appUrlSchema.startsWith('bilibili://search')) { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 8502dc7f..a83b7809 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -95,7 +95,7 @@ class PiliSchame { } } if (scheme == 'https') { - _fullPathPush(value); + fullPathPush(value); } } @@ -126,7 +126,7 @@ class PiliSchame { } } - static Future _fullPathPush(SchemeEntity value) async { + static Future fullPathPush(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 // final String scheme = value.scheme!; @@ -135,8 +135,6 @@ class PiliSchame { Map? query = value.query; RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); if (regExp.hasMatch(host)) { - print('bilibili.com host: $host'); - print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; if (path.startsWith('/video')) { Map matchRes = IdUtils.matchAvorBv(input: path); @@ -236,6 +234,24 @@ class PiliSchame { print('个人空间'); Get.toNamed('/member?mid=$area', arguments: {'face': ''}); break; + default: + final Map map = + IdUtils.matchAvorBv(input: area.split('?').first); + if (map.containsKey('AV')) { + _videoPush(map['AV']! as int, null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV'] as String); + } else { + Get.toNamed( + '/webview', + parameters: { + 'url': value.dataString ?? "", + 'type': 'url', + 'pageTitle': '' + }, + ); + } + break; } } } From 95685c4fdf04e8e0c725c7257755b641d7ca169f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 16:16:23 +0800 Subject: [PATCH 112/438] =?UTF-8?q?mod:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=90=9C=E7=B4=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 2 +- lib/pages/history_search/controller.dart | 12 +- lib/pages/history_search/view.dart | 150 ++++++++--------------- 3 files changed, 59 insertions(+), 105 deletions(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 00ebd511..baebfedb 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -244,7 +244,7 @@ class HistoryItem extends StatelessWidget { ), ), ), - videoItem.progress != 0 + videoItem.progress != 0 && videoItem.duration != 0 ? Positioned( left: 3, right: 3, diff --git a/lib/pages/history_search/controller.dart b/lib/pages/history_search/controller.dart index 90ac7a02..a6c79e6a 100644 --- a/lib/pages/history_search/controller.dart +++ b/lib/pages/history_search/controller.dart @@ -10,9 +10,8 @@ class HistorySearchController extends GetxController { final FocusNode searchFocusNode = FocusNode(); RxString searchKeyWord = ''.obs; String hintText = '搜索'; - RxString loadingStatus = 'init'.obs; + RxBool loadingStatus = false.obs; RxString loadingText = '加载中...'.obs; - bool hasRequest = false; late int mid; RxString uname = ''.obs; int pn = 1; @@ -36,8 +35,7 @@ class HistorySearchController extends GetxController { // 提交搜索内容 void submit() { - loadingStatus.value = 'loading'; - if (hasRequest) { + if (!loadingStatus.value) { pn = 1; searchHistories(); } @@ -48,6 +46,7 @@ class HistorySearchController extends GetxController { if (type == 'onLoad' && loadingText.value == '没有更多了') { return; } + loadingStatus.value = true; var res = await UserHttp.searchHistory( pn: pn, keyword: controller.value.text, @@ -63,9 +62,8 @@ class HistorySearchController extends GetxController { loadingText.value = '没有更多了'; } pn += 1; - hasRequest = true; } - loadingStatus.value = 'finish'; + loadingStatus.value = false; return res; } @@ -86,6 +84,6 @@ class HistorySearchController extends GetxController { historyList.removeWhere((e) => e.kid == kid); SmartDialog.showToast(res['msg']); } - loadingStatus.value = 'finish'; + // loadingStatus.value = fasle; } } diff --git a/lib/pages/history_search/view.dart b/lib/pages/history_search/view.dart index 5bde691d..f5bcae64 100644 --- a/lib/pages/history_search/view.dart +++ b/lib/pages/history_search/view.dart @@ -2,7 +2,6 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; -import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/pages/history/widgets/item.dart'; @@ -16,20 +15,19 @@ class HistorySearchPage extends StatefulWidget { } class _HistorySearchPageState extends State { - final HistorySearchController _historySearchCtr = - Get.put(HistorySearchController()); + final HistorySearchController _hisCtr = Get.put(HistorySearchController()); late ScrollController scrollController; @override void initState() { super.initState(); - scrollController = _historySearchCtr.scrollController; + scrollController = _hisCtr.scrollController; scrollController.addListener( () { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 300) { EasyThrottle.throttle('history', const Duration(seconds: 1), () { - _historySearchCtr.onLoad(); + _hisCtr.onLoad(); }); } }, @@ -50,19 +48,19 @@ class _HistorySearchPageState extends State { titleSpacing: 0, actions: [ IconButton( - onPressed: () => _historySearchCtr.submit(), + onPressed: () => _hisCtr.submit(), icon: const Icon(Icons.search_outlined, size: 22)), const SizedBox(width: 10) ], title: Obx( () => TextField( autofocus: true, - focusNode: _historySearchCtr.searchFocusNode, - controller: _historySearchCtr.controller.value, + focusNode: _hisCtr.searchFocusNode, + controller: _hisCtr.controller.value, textInputAction: TextInputAction.search, - onChanged: (value) => _historySearchCtr.onChange(value), + onChanged: (value) => _hisCtr.onChange(value), decoration: InputDecoration( - hintText: _historySearchCtr.hintText, + hintText: _hisCtr.hintText, border: InputBorder.none, suffixIcon: IconButton( icon: Icon( @@ -70,103 +68,61 @@ class _HistorySearchPageState extends State { size: 22, color: Theme.of(context).colorScheme.outline, ), - onPressed: () => _historySearchCtr.onClear(), + onPressed: () => _hisCtr.onClear(), ), ), - onSubmitted: (String value) => _historySearchCtr.submit(), + onSubmitted: (String value) => _hisCtr.submit(), ), ), ), body: Obx( - () => Column( - children: _historySearchCtr.loadingStatus.value == 'init' - ? [const SizedBox()] - : [ - Expanded( - child: FutureBuilder( - future: _historySearchCtr.searchHistories(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => _historySearchCtr.historyList.isNotEmpty - ? ListView.builder( - controller: scrollController, - itemCount: - _historySearchCtr.historyList.length + - 1, - itemBuilder: (context, index) { - if (index == - _historySearchCtr - .historyList.length) { - return Container( - height: MediaQuery.of(context) - .padding - .bottom + - 60, - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - child: Center( - child: Obx( - () => Text( - _historySearchCtr - .loadingText.value, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline, - fontSize: 13), - ), - ), - ), - ); - } else { - return HistoryItem( - videoItem: _historySearchCtr - .historyList[index], - ctr: _historySearchCtr, - onChoose: null, - onUpdateMultiple: () => null, - ); - } - }, - ) - : _historySearchCtr.loadingStatus.value == - 'loading' - ? const SizedBox(child: Text('加载中...')) - : const CustomScrollView( - slivers: [ - NoData(), - ], - ), - ); - } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ) - ], - ); - } + () { + return _hisCtr.loadingStatus.value && _hisCtr.historyList.isEmpty + ? ListView.builder( + itemCount: 10, + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + ) + : _hisCtr.historyList.isNotEmpty + ? ListView.builder( + controller: scrollController, + itemCount: _hisCtr.historyList.length + 1, + itemBuilder: (context, index) { + if (index == _hisCtr.historyList.length) { + return Container( + height: MediaQuery.of(context).padding.bottom + 60, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom), + child: Center( + child: Obx( + () => Text( + _hisCtr.loadingText.value, + style: TextStyle( + color: + Theme.of(context).colorScheme.outline, + fontSize: 13, + ), + ), + ), + ), + ); } else { - // 骨架屏 - return ListView.builder( - itemCount: 10, - itemBuilder: (context, index) { - return const VideoCardHSkeleton(); - }, + return HistoryItem( + videoItem: _hisCtr.historyList[index], + ctr: _hisCtr, + onChoose: null, + onUpdateMultiple: () => null, ); } }, - ), - ), - ], - ), + ) + : const CustomScrollView( + slivers: [ + NoData(), + ], + ); + }, ), ); } From 1cefdb68b7692833acbe561865a8793af21294c0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 16:38:16 +0800 Subject: [PATCH 113/438] =?UTF-8?q?opt:=20=E9=AA=A8=E6=9E=B6=E5=B1=8F?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/view.dart | 8 +++++++- lib/pages/subscription/view.dart | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index b8d37f50..4f48213e 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/fav/widgets/item.dart'; @@ -93,7 +94,12 @@ class _FavPageState extends State { } } else { // 骨架屏 - return const Text('请求中'); + return ListView.builder( + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + itemCount: 10, + ); } }, ), diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index 5e6e4664..bcc03cc3 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/utils/route_push.dart'; import 'controller.dart'; @@ -87,7 +88,12 @@ class _SubPageState extends State { } } else { // 骨架屏 - return const Text('请求中'); + return ListView.builder( + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + itemCount: 10, + ); } }, ), From d6e24096e0197e389122f463eaf31f075306272b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 15:48:28 +0800 Subject: [PATCH 114/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5Hero=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 167 +++++++++++++++---------------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 6f751e52..bf156c71 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -525,11 +525,10 @@ class _VideoDetailPageState extends State Scaffold( resizeToAvoidBottomInset: false, key: vdCtr.scaffoldKey, - backgroundColor: Colors.black, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), child: AppBar( - backgroundColor: Colors.transparent, + backgroundColor: Colors.black, elevation: 0, ), ), @@ -559,8 +558,7 @@ class _VideoDetailPageState extends State } return SliverAppBar( automaticallyImplyLeading: false, - // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: vdCtr.autoPlay.value, + pinned: true, elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, @@ -568,47 +566,42 @@ class _VideoDetailPageState extends State backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( - canPop: plPlayerController?.isFullScreen.value != - true, - onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == - true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - return Stack( - children: [ - if (isShowing) - Padding( - padding: EdgeInsets.only(top: 0), - child: videoPlayerPanel, - ), + canPop: + plPlayerController?.isFullScreen.value != true, + onPopInvoked: (bool didPop) { + if (plPlayerController?.isFullScreen.value == + true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape) { + verticalScreen(); + } + }, + child: Hero( + tag: heroTag, + child: Stack( + children: [ + if (isShowing) videoPlayerPanel, - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: !vdCtr.autoPlay.value && - vdCtr.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: handlePlayPanel(), - ), - ), + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), ), - ], - ); - }, - )), + ), + ), + ], + ), + ), + ), ), ); }, @@ -627,55 +620,51 @@ class _VideoDetailPageState extends State : pinnedHeaderHeight; }, onlyOneScrollInBody: true, - body: ColoredBox( - key: Key(heroTag), - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - tabbarBuild(), - Expanded( - child: TabBarView( - controller: vdCtr.tabCtr, - children: [ - Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: const PageStorageKey('简介'), - slivers: [ - if (vdCtr.videoType == SearchType.video) ...[ - VideoIntroPanel(bvid: vdCtr.bvid), - ] else if (vdCtr.videoType == - SearchType.media_bangumi) ...[ - Obx(() => BangumiIntroPanel( - cid: vdCtr.cid.value)), - ], - SliverToBoxAdapter( - child: Divider( - indent: 12, - endIndent: 12, - color: Theme.of(context) - .dividerColor - .withOpacity(0.06), - ), - ), - if (vdCtr.videoType == SearchType.video && - vdCtr.enableRelatedVideo) - const RelatedVideoPanel(), + body: Column( + children: [ + tabbarBuild(), + Expanded( + child: TabBarView( + controller: vdCtr.tabCtr, + children: [ + Builder( + builder: (BuildContext context) { + return CustomScrollView( + key: const PageStorageKey('简介'), + slivers: [ + if (vdCtr.videoType == SearchType.video) ...[ + VideoIntroPanel(bvid: vdCtr.bvid), + ] else if (vdCtr.videoType == + SearchType.media_bangumi) ...[ + Obx(() => + BangumiIntroPanel(cid: vdCtr.cid.value)), ], - ); - }, + SliverToBoxAdapter( + child: Divider( + indent: 12, + endIndent: 12, + color: Theme.of(context) + .dividerColor + .withOpacity(0.06), + ), + ), + if (vdCtr.videoType == SearchType.video && + vdCtr.enableRelatedVideo) + const RelatedVideoPanel(), + ], + ); + }, + ), + Obx( + () => VideoReplyPanel( + bvid: vdCtr.bvid, + oid: vdCtr.oid.value, ), - Obx( - () => VideoReplyPanel( - bvid: vdCtr.bvid, - oid: vdCtr.oid.value, - ), - ) - ], - ), + ) + ], ), - ], - ), + ), + ], ), ), ), From 5c0edf873b8c1e959041e563c0598bac8c8afa0f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 16:26:39 +0800 Subject: [PATCH 115/438] =?UTF-8?q?fix:=20=E5=A4=96=E9=93=BE=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=20pic=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/url_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index 99886e09..573f9f9d 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -44,7 +44,7 @@ class UrlUtils { final String bv = matchRes['BV']; final Map res = await SearchHttp.ab2cWithPic(bvid: bv); final int cid = res['cid']; - final String pic = res['pic']; + final String? pic = res['pic']; final String heroTag = Utils.makeHeroTag(bv); await Get.toNamed( '/video?bvid=$bv&cid=$cid', From 63d5752d3e797f53b1db7c327952bc94ab9ca06d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 17:30:42 +0800 Subject: [PATCH 116/438] =?UTF-8?q?fix:=20=E8=BF=9E=E7=BB=AD=E5=BF=AB?= =?UTF-8?q?=E8=BF=9B=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 4 ++-- lib/plugin/pl_player/widgets/backward_seek.dart | 7 +++++++ lib/plugin/pl_player/widgets/forward_seek.dart | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index ff0888be..df68da2f 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -935,7 +935,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 500), + duration: const Duration(milliseconds: 200), builder: (BuildContext context, double value, Widget? child) => Opacity( @@ -978,7 +978,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 500), + duration: const Duration(milliseconds: 200), builder: (BuildContext context, double value, Widget? child) => Opacity( diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart index 35de0ae6..8fddf80a 100644 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ b/lib/plugin/pl_player/widgets/backward_seek.dart @@ -20,6 +20,13 @@ class BackwardSeekIndicatorState extends State { Timer? timer; + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + @override void initState() { super.initState(); diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart index 43ddd322..7e3886ce 100644 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ b/lib/plugin/pl_player/widgets/forward_seek.dart @@ -20,6 +20,13 @@ class ForwardSeekIndicatorState extends State { Timer? timer; + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + @override void initState() { super.initState(); From 4aa47f4a7bd31cd3085f9200b692884685bb9b41 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 23:18:37 +0800 Subject: [PATCH 117/438] =?UTF-8?q?fix:=20pip=E8=BF=9B=E5=85=A5=20?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E5=8C=BA=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/controller.dart | 2 -- lib/pages/video/detail/reply/view.dart | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index b718c840..c1929434 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -1,5 +1,4 @@ import 'package:easy_debounce/easy_throttle.dart'; -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/reply.dart'; @@ -15,7 +14,6 @@ class VideoReplyController extends GetxController { this.rpid, this.replyLevel, ); - final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid int? aid; // 层级 2为楼中楼 diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 4c533647..653fe7e0 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -67,13 +67,12 @@ class _VideoReplyPanelState extends State vsync: this, duration: const Duration(milliseconds: 300)); _futureBuilderFuture = _videoReplyController.queryReplyList(); - + scrollController = ScrollController(); fabAnimationCtr.forward(); scrollListener(); } void scrollListener() { - scrollController = _videoReplyController.scrollController; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -185,7 +184,8 @@ class _VideoReplyPanelState extends State builder: (BuildContext context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { var data = snapshot.data; - if (data['status']) { + if (_videoReplyController.replyList.isNotEmpty || + (data && data['status'])) { // 请求成功 return Obx( () => _videoReplyController.isLoadingMore && From 1c6047a3268ab1d01a4a0059912c2b5604aeb267 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 27 May 2024 23:54:33 +0800 Subject: [PATCH 118/438] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 470e9a35..228d17bb 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ QQ频道: https://pd.qq.com/s/365esodk3 - [x] 音质选择(视视频而定) - [x] 解码格式选择(视视频而定) - [x] 弹幕 - - [ ] 字幕 + - [x] 字幕 - [x] 记忆播放 - [x] 视频比例:高度/宽度适应、填充、包含等 From 6b4d24f59566ee12142408b9720809adc23cc86b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 28 May 2024 22:58:51 +0800 Subject: [PATCH 119/438] =?UTF-8?q?opt:=20=E5=8A=A8=E6=80=81=E9=A1=B5up?= =?UTF-8?q?=E4=B8=BB=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/up_panel.dart | 54 +++++++++++++----------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index fd0ae642..ee522cbb 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -1,3 +1,4 @@ +import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -30,6 +31,31 @@ class _UpPanelState extends State { liveList = widget.upData.liveList!; } + void onClickUp(data, i) { + currentMid = data.mid; + Get.find().mid.value = data.mid; + Get.find().upInfo.value = data; + Get.find().onSelectUp(data.mid); + int liveLen = liveList.length; + int upLen = upList.length; + double itemWidth = contentWidth + itemPadding.horizontal; + double screenWidth = MediaQuery.sizeOf(context).width; + double moveDistance = 0.0; + if (itemWidth * (upList.length + liveList.length) <= screenWidth) { + } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { + moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; + } else { + moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; + } + data.hasUpdate = false; + scrollController.animateTo( + moveDistance, + duration: const Duration(milliseconds: 200), + curve: Curves.linear, + ); + setState(() {}); + } + @override Widget build(BuildContext context) { listFormat(); @@ -120,30 +146,10 @@ class _UpPanelState extends State { onTap: () { feedBack(); if (data.type == 'up') { - currentMid = data.mid; - Get.find().mid.value = data.mid; - Get.find().upInfo.value = data; - Get.find().onSelectUp(data.mid); - int liveLen = liveList.length; - int upLen = upList.length; - double itemWidth = contentWidth + itemPadding.horizontal; - double screenWidth = MediaQuery.sizeOf(context).width; - double moveDistance = 0.0; - if (itemWidth * (upList.length + liveList.length) <= screenWidth) { - } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { - moveDistance = - (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; - } else { - moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; - } - data.hasUpdate = false; - scrollController.animateTo( - moveDistance, - duration: const Duration(milliseconds: 500), - curve: Curves.easeInOut, - ); - - setState(() {}); + EasyThrottle.throttle('follow', const Duration(milliseconds: 300), + () { + onClickUp(data, i); + }); } else if (data.type == 'live') { LiveItemModel liveItem = LiveItemModel.fromJson({ 'title': data.title, From eef9e3ae9eb3713a14b55fd04f0880132d623beb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 30 May 2024 23:58:42 +0800 Subject: [PATCH 120/438] opt: loading Ani --- assets/loading.json | 1 + assets/trail_loading.json | 1 + lib/pages/video/detail/introduction/view.dart | 12 ++++++------ lib/plugin/pl_player/view.dart | 7 ++++--- pubspec.lock | 8 ++++++++ pubspec.yaml | 8 +++++--- 6 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 assets/loading.json create mode 100644 assets/trail_loading.json diff --git a/assets/loading.json b/assets/loading.json new file mode 100644 index 00000000..38bccbed --- /dev/null +++ b/assets/loading.json @@ -0,0 +1 @@ +{"v":"5.7.11","fr":60,"ip":0,"op":81,"w":1920,"h":1080,"nm":"Loading Dots","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Dot4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[100]},{"t":55,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[1142,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[1142,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":55,"s":[1142,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":39,"s":[75,75,100]},{"t":55,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Dot3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":31,"s":[100]},{"t":47,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":17,"s":[1022,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[1022,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":47,"s":[1022,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":17,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[75,75,100]},{"t":47,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Dot2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":23,"s":[100]},{"t":39,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[902,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[902,500,0],"to":[0,0,0],"ti":[0,0,0]},{"t":39,"s":[902,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[75,75,100]},{"t":39,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Dot1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"t":30,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[782,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":14,"s":[782,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":30,"s":[782,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":14,"s":[75,75,100]},{"t":30,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/trail_loading.json b/assets/trail_loading.json new file mode 100644 index 00000000..9fb39ea6 --- /dev/null +++ b/assets/trail_loading.json @@ -0,0 +1 @@ +{"v":"4.6.8","fr":60,"ip":0,"op":106,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":20,"s":[0],"e":[360]},{"t":110}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[10,10]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":20,"op":620,"st":20,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[0],"e":[360]},{"t":105}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":15,"op":615,"st":15,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":10,"s":[0],"e":[360]},{"t":100}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":10,"op":610,"st":10,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":5,"s":[0],"e":[360]},{"t":95}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":5,"op":605,"st":5,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[360]},{"t":90}]},"p":{"a":0,"k":[250,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":0,"s":[50,50],"e":[40,40]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":84,"s":[40,40],"e":[50,50]},{"t":100}]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":600,"st":0,"bm":0,"sr":1}]} \ No newline at end of file diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..06a026aa 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/video/detail/index.dart'; @@ -97,11 +98,14 @@ class _VideoIntroPanelState extends State ); } } else { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: SizedBox( height: 100, child: Center( - child: CircularProgressIndicator(), + child: Lottie.asset( + 'assets/loading.json', + width: 200, + ), ), ), ); @@ -595,8 +599,4 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); }); } - - // Widget StaffPanel(BuildContext context, videoIntroController) { - // return - // } } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index df68da2f..6508b624 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -7,6 +7,7 @@ import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:pilipala/models/common/gesture_mode.dart'; @@ -909,9 +910,9 @@ class _PLVideoPlayerState extends State colors: [Colors.black26, Colors.transparent], ), ), - child: Image.asset( - 'assets/images/loading.gif', - height: 25, + child: Lottie.asset( + 'assets/loading.json', + width: 200, ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 8f8a5cae..7ff7ef0d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -846,6 +846,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" + lottie: + dependency: "direct main" + description: + name: lottie + sha256: "6a24ade5d3d918c306bb1c21a6b9a04aab0489d51a2582522eea820b4093b62b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.2" mailer: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a269cd6f..10a0f065 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,7 @@ dependencies: expandable: ^5.0.1 # 投屏 dlna_dart: ^0.0.8 + lottie: ^3.1.2 dev_dependencies: flutter_test: @@ -166,15 +167,15 @@ dev_dependencies: build_runner: ^2.4.8 dependency_overrides: - media_kit: + media_kit: git: url: https://github.com/media-kit/media-kit path: media_kit - media_kit_video: + media_kit_video: git: url: https://github.com/media-kit/media-kit path: media_kit_video - media_kit_libs_video: + media_kit_libs_video: git: url: https://github.com/media-kit/media-kit path: libs/universal/media_kit_libs_video @@ -205,6 +206,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: + - assets/ - assets/images/ - assets/images/lv/ - assets/images/logo/ From e1572c0962b43cf89fb32b02aeb6ceabdb63960f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 31 May 2024 23:37:27 +0800 Subject: [PATCH 121/438] =?UTF-8?q?fix:=20=E9=94=81=E5=AE=9A=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E4=B8=8B=E9=81=BF=E5=85=8D=E6=89=8B=E5=8A=BF=E9=80=80?= =?UTF-8?q?=E5=87=BA=E5=85=A8=E5=B1=8F=20issues=20#753?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index df68da2f..a695e752 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -733,14 +733,18 @@ class _PLVideoPlayerState extends State const double threshold = 7.0; // 滑动阈值 final bool flag = fullScreenGestureMode != FullScreenGestureMode.values.last; - if (dy > _distance.value && dy > threshold) { + if (dy > _distance.value && + dy > threshold && + !_.controlsLock.value) { if (_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 下滑退出全屏 await widget.controller.triggerFullScreen(status: flag); } _distance.value = 0.0; - } else if (dy < _distance.value && dy < -threshold) { + } else if (dy < _distance.value && + dy < -threshold && + !_.controlsLock.value) { if (!_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 上滑进入全屏 From 2358ba42e1867b1aa52c7278e10e2fe811455a33 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 1 Jun 2024 00:21:04 +0800 Subject: [PATCH 122/438] opt: pip issues #752 --- lib/pages/video/detail/view.dart | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index bf156c71..fa531684 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -99,7 +99,6 @@ class _VideoDetailPageState extends State fullScreenStatusListener(); if (Platform.isAndroid) { floating = vdCtr.floating!; - autoEnterPip(); } WidgetsBinding.instance.addObserver(this); lifecycleListener(); @@ -128,8 +127,9 @@ class _VideoDetailPageState extends State } // 播放器状态监听 - void playerListener(PlayerStatus? status) async { - playerStatus.value = status!; + void playerListener(PlayerStatus status) async { + playerStatus.value = status; + autoEnterPip(status: status); if (status == PlayerStatus.completed) { // 结束播放退出全屏 if (autoExitFullcreen) { @@ -181,6 +181,7 @@ class _VideoDetailPageState extends State plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; vdCtr.isShowCover.value = false; + autoEnterPip(status: PlayerStatus.playing); } void fullScreenStatusListener() { @@ -287,10 +288,12 @@ class _VideoDetailPageState extends State .subscribe(this, ModalRoute.of(context)! as PageRoute); } - void autoEnterPip() { + void autoEnterPip({PlayerStatus? status}) { final String routePath = Get.currentRoute; if (autoPiP && routePath.startsWith('/video')) { - floating.toggleAutoPip(autoEnter: autoPiP); + floating.toggleAutoPip( + autoEnter: autoPiP && status == PlayerStatus.playing, + ); } } @@ -314,6 +317,15 @@ class _VideoDetailPageState extends State case 'show' || 'restart': plPlayerController?.danmakuController?.clear(); break; + case 'pause': + vdCtr.hiddenReplyReplyPanel(); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + } + break; } } From bde44af9a0afd283c247c6fac7bcd6eb740eb730 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 1 Jun 2024 22:43:27 +0800 Subject: [PATCH 123/438] opt: main.daat code --- lib/main.dart | 314 ++++++++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 137 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 066bb599..1a2ce989 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,7 +23,7 @@ import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. +import 'package:media_kit/media_kit.dart'; import 'package:pilipala/utils/recommend_filter.dart'; import 'package:catcher_2/catcher_2.dart'; import './services/loggeer.dart'; @@ -31,59 +31,42 @@ import './services/loggeer.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]) - .then((_) async { - await GStrorage.init(); - await setupServiceLocator(); - clearLogs(); - Request(); - await Request.setCookie(); - RecommendFilter(); + await SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); + await GStrorage.init(); + await setupServiceLocator(); + clearLogs(); + Request(); + await Request.setCookie(); - // 异常捕获 logo记录 - final Catcher2Options debugConfig = Catcher2Options( - SilentReportMode(), - [ - FileHandler(await getLogsPath()), - ConsoleHandler( - enableDeviceParameters: false, - enableApplicationParameters: false, - ) - ], - ); + // 异常捕获 logo记录 + final Catcher2Options releaseConfig = Catcher2Options( + SilentReportMode(), + [FileHandler(await getLogsPath())], + ); - final Catcher2Options releaseConfig = Catcher2Options( - SilentReportMode(), - [FileHandler(await getLogsPath())], - ); + Catcher2( + releaseConfig: releaseConfig, + runAppFunction: () { + runApp(const MyApp()); + }, + ); - Catcher2( - debugConfig: debugConfig, - releaseConfig: releaseConfig, - runAppFunction: () { - runApp(const MyApp()); - }, - ); - - // 小白条、导航栏沉浸 - if (Platform.isAndroid) { - final androidInfo = await DeviceInfoPlugin().androidInfo; - if (androidInfo.version.sdkInt >= 29) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - statusBarColor: Colors.transparent, - )); + // 小白条、导航栏沉浸 + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 29) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.transparent, + )); + } - Data.init(); - GlobalData(); - PiliSchame.init(); - DisableBatteryOpt(); - }); + PiliSchame.init(); + DisableBatteryOpt(); } class MyApp extends StatelessWidget { @@ -124,6 +107,39 @@ class MyApp extends StatelessWidget { } catch (_) {} } + if (Platform.isAndroid) { + return AndroidApp( + brandColor: brandColor, + isDynamicColor: isDynamicColor, + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } else { + return OtherApp( + brandColor: brandColor, + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } + } +} + +class AndroidApp extends StatelessWidget { + const AndroidApp({ + super.key, + required this.brandColor, + required this.isDynamicColor, + required this.currentThemeValue, + required this.textScale, + }); + + final Color brandColor; + final bool isDynamicColor; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { return DynamicColorBuilder( builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) { ColorScheme? lightColorScheme; @@ -143,96 +159,120 @@ class MyApp extends StatelessWidget { brightness: Brightness.dark, ); } - - // ThemeData themeData = ThemeData( - // colorScheme: currentThemeValue == ThemeType.dark - // ? darkColorScheme - // : lightColorScheme, - // ); - - // // 小白条、导航栏沉浸 - // if (Platform.isAndroid) { - // List versionParts = Platform.version.split('.'); - // int androidVersion = int.parse(versionParts[0]); - // if (androidVersion >= 29) { - // SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - // } - // SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - // systemNavigationBarColor: GlobalData().enableMYBar - // ? const Color(0x00010000) - // : themeData.canvasColor, - // systemNavigationBarDividerColor: GlobalData().enableMYBar - // ? const Color(0x00010000) - // : themeData.canvasColor, - // systemNavigationBarIconBrightness: - // currentThemeValue == ThemeType.dark - // ? Brightness.light - // : Brightness.dark, - // statusBarColor: Colors.transparent, - // )); - // } - - // 图片缓存 - // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; - return GetMaterialApp( - title: 'PiliPala', - theme: ThemeData( - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - snackBarTheme: SnackBarThemeData( - actionTextColor: lightColorScheme.primary, - backgroundColor: lightColorScheme.secondaryContainer, - closeIconColor: lightColorScheme.secondary, - contentTextStyle: TextStyle(color: lightColorScheme.secondary), - elevation: 20, - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), - ), - darkTheme: ThemeData( - colorScheme: currentThemeValue == ThemeType.light - ? lightColorScheme - : darkColorScheme, - snackBarTheme: SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ), - ), - localizationsDelegates: const [ - GlobalCupertinoLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - locale: const Locale("zh", "CN"), - supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")], - fallbackLocale: const Locale("zh", "CN"), - getPages: Routes.getPages, - home: const MainApp(), - builder: (BuildContext context, Widget? child) { - return FlutterSmartDialog( - toastBuilder: (String msg) => CustomToast(msg: msg), - child: MediaQuery( - data: MediaQuery.of(context) - .copyWith(textScaler: TextScaler.linear(textScale)), - child: child!, - ), - ); - }, - navigatorObservers: [ - VideoDetailPage.routeObserver, - SearchPage.routeObserver, - ], + return BuildMainApp( + lightColorScheme: lightColorScheme, + darkColorScheme: darkColorScheme, + currentThemeValue: currentThemeValue, + textScale: textScale, ); }), ); } } + +class OtherApp extends StatelessWidget { + const OtherApp({ + super.key, + required this.brandColor, + required this.currentThemeValue, + required this.textScale, + }); + + final Color brandColor; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { + return BuildMainApp( + lightColorScheme: ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.light, + ), + darkColorScheme: ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.dark, + ), + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } +} + +class BuildMainApp extends StatelessWidget { + const BuildMainApp({ + super.key, + required this.lightColorScheme, + required this.darkColorScheme, + required this.currentThemeValue, + required this.textScale, + }); + + final ColorScheme lightColorScheme; + final ColorScheme darkColorScheme; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { + final SnackBarThemeData snackBarTheme = SnackBarThemeData( + actionTextColor: lightColorScheme.primary, + backgroundColor: lightColorScheme.secondaryContainer, + closeIconColor: lightColorScheme.secondary, + contentTextStyle: TextStyle(color: lightColorScheme.secondary), + elevation: 20, + ); + + return GetMaterialApp( + title: 'PiliPala', + theme: ThemeData( + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: snackBarTheme, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ), + darkTheme: ThemeData( + colorScheme: currentThemeValue == ThemeType.light + ? lightColorScheme + : darkColorScheme, + snackBarTheme: snackBarTheme, + ), + localizationsDelegates: const [ + GlobalCupertinoLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + locale: const Locale("zh", "CN"), + supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")], + fallbackLocale: const Locale("zh", "CN"), + getPages: Routes.getPages, + home: const MainApp(), + builder: (BuildContext context, Widget? child) { + return FlutterSmartDialog( + toastBuilder: (String msg) => CustomToast(msg: msg), + child: MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaler: TextScaler.linear(textScale)), + child: child!, + ), + ); + }, + navigatorObservers: [ + VideoDetailPage.routeObserver, + SearchPage.routeObserver, + ], + onInit: () { + RecommendFilter(); + Data.init(); + GlobalData(); + }, + ); + } +} From 8f26884164a5a114529b02efc1875c5387e32bfc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 2 Jun 2024 01:04:51 +0800 Subject: [PATCH 124/438] =?UTF-8?q?upgrade:=20=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/build.gradle | 2 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile.lock | 14 +- ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/pages/bangumi/view.dart | 7 +- .../pl_player/widgets/bottom_control.dart | 3 +- pubspec.lock | 160 ++++++++++-------- pubspec.yaml | 32 ++-- 9 files changed, 116 insertions(+), 108 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 713d7f6e..674e96f4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.0' repositories { google() mavenCentral() diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2c1a635b..04fe6670 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,7 +10,6 @@ PODS: - connectivity_plus (0.0.1): - Flutter - FlutterMacOS - - ReachabilitySwift - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -41,7 +40,6 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - - ReachabilitySwift (5.0.0) - saver_gallery (0.0.1): - Flutter - screen_brightness_ios (0.1.0): @@ -101,7 +99,6 @@ SPEC REPOS: trunk: - FMDB - GT3Captcha-iOS - - ReachabilitySwift - Toast EXTERNAL SOURCES: @@ -167,9 +164,9 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d - connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 + connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 @@ -182,7 +179,6 @@ SPEC CHECKSUMS: package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 @@ -190,11 +186,11 @@ SPEC CHECKSUMS: status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446 system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44 Toast: ec33c32b8688982cecc6348adeae667c1b9938da - url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 - wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 + wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 - webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 + webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36 PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index bac856d2..55565d40 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826db..5e31d3d3 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ), ); } else { - return nil; + return const SizedBox(); } } else { - return nil; + return const SizedBox(); } }, ), @@ -216,7 +215,7 @@ class _BangumiPageState extends State (BuildContext context, int index) { return bangumiList!.isNotEmpty ? BangumiCardV(bangumiItem: bangumiList[index]) - : nil; + : const SizedBox(); }, childCount: bangumiList!.isNotEmpty ? bangumiList!.length : 10, ), diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 35e7792a..b3ff37db 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -1,7 +1,6 @@ import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:nil/nil.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/feed_back.dart'; @@ -36,7 +35,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { final int max = _.durationSeconds.value; final int buffer = _.bufferedSeconds.value; if (value > max || max <= 0) { - return nil; + return const SizedBox(); } return Padding( padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6), diff --git a/pubspec.lock b/pubspec.lock index 8f8a5cae..de434f2e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.2.0" - animations: - dependency: "direct main" - description: - name: animations - sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.11" appscheme: dependency: "direct main" description: @@ -101,10 +93,10 @@ packages: dependency: "direct main" description: name: audio_video_progress_bar - sha256: ccc7d7b83d2a16c52d4a7fb332faabd1baa053fb0e4c16815aefd3945ab33b81 + sha256: "552b1f73c56c4c88407999e0a8507176f60c56de3e6d63bc20a0eab48467d4c9" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2" + version: "2.0.3" auto_orientation: dependency: "direct main" description: @@ -213,10 +205,10 @@ packages: dependency: "direct main" description: name: catcher_2 - sha256: "9cf33d2befd10058374e5fc6177577fdd938d73d9c06810de81cf91311a7ce98" + sha256: "2c2c6f8cf8c817730cd1dbb010d55292396930e7a3d42c04c3039e3fd411a2f8" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.3" + version: "1.2.6" characters: dependency: transitive description: @@ -269,10 +261,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9 + sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8 url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.1" + version: "6.0.3" connectivity_plus_platform_interface: dependency: transitive description: @@ -301,10 +293,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.3+8" + version: "0.3.4+1" crypto: dependency: "direct main" description: @@ -333,10 +325,10 @@ packages: dependency: "direct main" description: name: custom_sliding_segmented_control - sha256: "05b73fa48d57218bfdf806bad68a859812b216cd81fe81c6cbefde89f39eb257" + sha256: "53c3e931c3ae1f696085d1ec70ac8e934da836595a9b7d9b88fdd0fcbf2a5574" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.1" + version: "1.8.3" dart_style: dependency: transitive description: @@ -461,10 +453,10 @@ packages: dependency: transitive description: name: extended_image_library - sha256: "9b55fc5ebc65fad984de66b8f177a1bef2a84d79203c9c213f75ff83c2c29edd" + sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6 url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.1" + version: "4.0.4" extended_list: dependency: transitive description: @@ -621,10 +613,10 @@ packages: dependency: "direct main" description: name: flutter_volume_controller - sha256: "0f10cc759499cb6c3e152a8f6ff8e5ce385b99db7e1f586d1a29d8e6c11f4082" + sha256: fa4c36dfe7ef7f423704f34ab8e64e00b4a30a90aa6e56f251e9dba649efcd7f url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.1" + version: "1.3.2" flutter_web_plugins: dependency: transitive description: flutter @@ -722,10 +714,10 @@ packages: dependency: transitive description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.2" + version: "1.2.1" http2: dependency: transitive description: @@ -770,10 +762,10 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.flutter-io.cn" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -798,6 +790,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -834,10 +850,10 @@ packages: dependency: "direct main" description: name: logger - sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" + sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2+1" + version: "2.3.0" logging: dependency: transitive description: @@ -850,26 +866,26 @@ packages: dependency: transitive description: name: mailer - sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" + sha256: d25d89555c1031abacb448f07b801d7c01b4c21d4558e944b12b64394c84a3cb url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.1" + version: "6.1.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0" + version: "0.8.0" media_kit: dependency: "direct main" description: @@ -949,10 +965,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.12.0" mime: dependency: transitive description: @@ -961,14 +977,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.4" - nil: - dependency: "direct main" - description: - name: nil - sha256: ef05770c48942876d843bf6a4822d35e5da0ff893a61f1d5ad96d15c4a659136 - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" nm: dependency: transitive description: @@ -1022,10 +1030,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1435,10 +1443,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.1" + version: "0.7.0" timing: dependency: transitive description: @@ -1467,10 +1475,10 @@ packages: dependency: "direct main" description: name: universal_platform - sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.0+1" + version: "1.1.0" uri_parser: dependency: transitive description: @@ -1483,10 +1491,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" url: "https://pub.flutter-io.cn" source: hosted - version: "6.2.2" + version: "6.2.6" url_launcher_android: dependency: transitive description: @@ -1499,10 +1507,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" url: "https://pub.flutter-io.cn" source: hosted - version: "6.2.1" + version: "6.3.0" url_launcher_linux: dependency: transitive description: @@ -1531,10 +1539,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.2" + version: "2.3.1" url_launcher_windows: dependency: transitive description: @@ -1591,6 +1599,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.4.0+2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.2.1" volume_controller: dependency: transitive description: @@ -1603,18 +1619,18 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d + sha256: "104d94837bb28c735894dcd592877e990149c380e6358b00c04398ca1426eed4" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.4" + version: "1.2.1" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" + sha256: "582f2f7aecc7376332d961a0dd1efa9378ce117657e0ade55d9ff72699a55e82" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.2.0" watcher: dependency: transitive description: @@ -1635,10 +1651,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.0" + version: "0.5.1" web_socket_channel: dependency: transitive description: @@ -1659,34 +1675,34 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 + sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" url: "https://pub.flutter-io.cn" source: hosted - version: "4.5.0" + version: "4.8.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456 + sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc url: "https://pub.flutter-io.cn" source: hosted - version: "3.13.0" + version: "3.16.4" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a" + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d url: "https://pub.flutter-io.cn" source: hosted - version: "2.8.0" + version: "2.10.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c" + sha256: "7affdf9d680c015b11587181171d3cad8093e449db1f7d9f0f08f4f33d24f9a0" url: "https://pub.flutter-io.cn" source: hosted - version: "3.10.2" + version: "3.13.1" win32: dependency: transitive description: @@ -1728,5 +1744,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index a269cd6f..b9390dd9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: dio: ^5.4.1 cookie_jar: ^4.0.8 dio_cookie_manager: ^3.1.1 - connectivity_plus: ^6.0.1 + connectivity_plus: ^6.0.3 dio_http2_adapter: ^2.3.1+1 # 图片 @@ -66,7 +66,7 @@ dependencies: # cookie 管理 webview_cookie_manager: ^2.0.6 # 浏览器 - webview_flutter: ^4.5.0 + webview_flutter: ^4.8.0 # 解决sliver滑动不同步 extended_nested_scroll_view: ^6.2.1 # 上拉加载 @@ -79,7 +79,7 @@ dependencies: flutter_smart_dialog: ^4.9.4 # 下滑关闭 dismissible_page: ^1.0.2 - custom_sliding_segmented_control: ^1.7.5 + custom_sliding_segmented_control: ^1.8.3 # 加密 crypto: ^3.0.3 encrypt: ^5.0.3 @@ -94,19 +94,18 @@ dependencies: audio_session: ^0.1.18 # 音量、亮度、屏幕控制 - flutter_volume_controller: ^1.3.1 + flutter_volume_controller: ^1.3.2 screen_brightness: ^0.2.2+1 - wakelock_plus: ^1.1.1 - universal_platform: ^1.0.0+1 + wakelock_plus: ^1.1.6 + universal_platform: ^1.1.0 # 进度条 - audio_video_progress_bar: ^2.0.2 + audio_video_progress_bar: ^2.0.3 auto_orientation: ^2.3.1 protobuf: ^3.0.0 - animations: ^2.0.11 # 获取appx信息 - package_info_plus: ^4.1.0 - url_launcher: ^6.1.14 + package_info_plus: ^4.2.0 + url_launcher: ^6.2.6 flutter_svg: ^2.0.10+1 # 防抖节流 easy_debounce: ^2.0.3 @@ -136,10 +135,9 @@ dependencies: gt3_flutter_plugin: ^0.0.8 uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 - nil: ^1.1.1 - catcher_2: ^1.2.3 - logger: ^2.0.2+1 - path: 1.8.3 + catcher_2: ^1.2.6 + logger: ^2.3.0 + path: ^1.9.0 # 电池优化 disable_battery_optimization: ^1.1.1 # 展开/收起 @@ -166,15 +164,15 @@ dev_dependencies: build_runner: ^2.4.8 dependency_overrides: - media_kit: + media_kit: git: url: https://github.com/media-kit/media-kit path: media_kit - media_kit_video: + media_kit_video: git: url: https://github.com/media-kit/media-kit path: media_kit_video - media_kit_libs_video: + media_kit_libs_video: git: url: https://github.com/media-kit/media-kit path: libs/universal/media_kit_libs_video From 3b1ad133db0a9e8bdec5d47ec44e030dc4ee8367 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 2 Jun 2024 22:32:00 +0800 Subject: [PATCH 125/438] mod: logs replace# --- lib/pages/setting/pages/logs.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 0958edb8..f497aee5 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -41,7 +41,8 @@ class _LogsPageState extends State { .replaceAll('DEVICE INFO', '设备信息') .replaceAll('APP INFO', '应用信息') .replaceAll('ERROR', '错误信息') - .replaceAll('STACK TRACE', '错误堆栈'); + .replaceAll('STACK TRACE', '错误堆栈') + .replaceAll('#', 'Line'); }).toList(); List> result = []; for (String i in contentList) { @@ -50,7 +51,7 @@ class _LogsPageState extends State { .split("\n") .map((l) { if (l.startsWith("Crash occurred on")) { - date = DateTime.parse( + date = DateTime.tryParse( l.split("Crash occurred on")[1].trim().split('.')[0], ); return ""; From e0e1c6b08af706f79856074ba4b3ea5a53806fc4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 4 Jun 2024 23:59:23 +0800 Subject: [PATCH 126/438] upgrade: color --- lib/common/pages_bottom_sheet.dart | 2 +- lib/common/skeleton/skeleton.dart | 4 ++-- lib/common/widgets/stat/danmu.dart | 2 +- lib/common/widgets/stat/view.dart | 2 +- .../introduction/widgets/intro_detail.dart | 4 ++-- lib/pages/dynamics/view.dart | 5 ++--- lib/pages/dynamics/widgets/additional_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/dynamics/widgets/up_panel.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 5 ++++- lib/pages/setting/pages/font_size_select.dart | 2 +- .../detail/introduction/widgets/fav_panel.dart | 2 +- .../detail/introduction/widgets/group_panel.dart | 2 +- .../detail/introduction/widgets/menu_row.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/reply_reply/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/app_bar.dart | 2 +- .../video/detail/widgets/header_control.dart | 16 ++++++++-------- lib/utils/image_save.dart | 2 +- 20 files changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 2a56eb41..49e9b4d8 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -124,7 +124,7 @@ class EpisodeBottomSheet { }); return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ buildTitle(), diff --git a/lib/common/skeleton/skeleton.dart b/lib/common/skeleton/skeleton.dart index 34e87f55..b17a55fc 100644 --- a/lib/common/skeleton/skeleton.dart +++ b/lib/common/skeleton/skeleton.dart @@ -13,8 +13,8 @@ class Skeleton extends StatelessWidget { var shimmerGradient = LinearGradient( colors: [ Colors.transparent, - Theme.of(context).colorScheme.background.withAlpha(10), - Theme.of(context).colorScheme.background.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), Colors.transparent, ], stops: const [ diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index c1c439db..511839a0 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -14,7 +14,7 @@ class StatDanMu extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 2665e2d4..5359c979 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -14,7 +14,7 @@ class StatView extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index c5bbd566..07684a86 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -20,10 +20,10 @@ class IntroDetail extends StatelessWidget { sheetHeight = localCache.get('sheetHeight'); TextStyle smallTitle = TextStyle( fontSize: 12, - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index da15239d..258ad531 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,13 +162,12 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceVariant + .surfaceContainerHighest .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), thumbDecoration: BoxDecoration( - color: - Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), ), duration: const Duration(milliseconds: 300), diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index fa11f217..50e1b6d3 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -19,7 +19,7 @@ Widget addWidget(item, context, type, {floor = 1}) { }; Color bgColor = floor == 1 ? Theme.of(context).dividerColor.withOpacity(0.08) - : Theme.of(context).colorScheme.background; + : Theme.of(context).colorScheme.surface; switch (type) { case 'ADDITIONAL_TYPE_UGC': // 转发的投稿 diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 0d3baecd..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -52,7 +52,7 @@ class AuthorPanel extends StatelessWidget { color: item.modules.moduleAuthor!.vip != null && item.modules.moduleAuthor!.vip['status'] > 0 ? const Color.fromARGB(255, 251, 100, 163) - : Theme.of(context).colorScheme.onBackground, + : Theme.of(context).colorScheme.onSurface, fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, ), ), diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index ee522cbb..f8c973a0 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -69,7 +69,7 @@ class _UpPanelState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 16, right: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -95,7 +95,7 @@ class _UpPanelState extends State { ), Container( height: 90, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Row( children: [ Flexible( diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..1b96d412 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -16,7 +16,10 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart index 4985c83f..f5ca6be3 100644 --- a/lib/pages/setting/pages/font_size_select.dart +++ b/lib/pages/setting/pages/font_size_select.dart @@ -66,7 +66,7 @@ class _FontSizeSelectPageState extends State { .colorScheme .primary .withOpacity(0.3))), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Row( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 517caeaa..5ef78967 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -29,7 +29,7 @@ class _FavPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 64ff913d..dcdaf9c5 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -57,7 +57,7 @@ class _GroupPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart index c175aff1..a26c86f0 100644 --- a/lib/pages/video/detail/introduction/widgets/menu_row.dart +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -12,7 +12,7 @@ class MenuRow extends StatelessWidget { Widget build(BuildContext context) { return Container( width: double.infinity, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -84,7 +84,7 @@ class MenuRow extends StatelessWidget { style: TextStyle( fontSize: 13, color: selectStatus - ? Theme.of(context).colorScheme.onBackground + ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.outline), ), ), diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 3da15f64..d7355d81 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -170,7 +170,7 @@ class _VideoReplyNewDialogState extends State topLeft: Radius.circular(12), topRight: Radius.circular(12), ), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 3fe84c71..6dda9512 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -78,7 +78,7 @@ class _VideoReplyReplyPanelState extends State { Widget build(BuildContext context) { return Container( height: widget.source == 'videoDetail' ? widget.sheetHeight : null, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ if (widget.source == 'videoDetail') diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 882a9a8b..197d6124 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -23,7 +23,7 @@ class AiDetail extends StatelessWidget { Widget build(BuildContext context) { sheetHeight = localCache.get('sheetHeight'); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index efc0b593..b16623ad 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -29,7 +29,7 @@ class ScrollAppBar extends StatelessWidget { opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: EdgeInsets.only(top: statusBarHeight), child: AppBar( primary: false, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 544d93e0..3ff74b44 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -93,7 +93,7 @@ class _HeaderControlState extends State { height: 460, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -317,7 +317,7 @@ class _HeaderControlState extends State { height: 500, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -377,7 +377,7 @@ class _HeaderControlState extends State { inactiveThumbColor: Theme.of(context).colorScheme.primaryContainer, inactiveTrackColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, splashRadius: 10.0, // boolean variable value value: shutdownTimerService.waitForPlayingCompleted, @@ -570,7 +570,7 @@ class _HeaderControlState extends State { height: 310, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -660,7 +660,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -734,7 +734,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -828,7 +828,7 @@ class _HeaderControlState extends State { height: 580, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -1084,7 +1084,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0cd6915c..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -12,7 +12,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { builder: (context) => Container( margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(10.0), ), child: Column( From 7fc4f3fd4145acacdf116a82ff04ce8aa68002f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 6 Jun 2024 00:28:46 +0800 Subject: [PATCH 127/438] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5app=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=85=B3=E6=B3=A8=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 78747d1a..0098fe95 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -69,9 +69,10 @@ class RecVideoItemAppModel { : null; // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 + RegExp regex = RegExp(r'已关注|新关注'); isFollowed = rcmdReason != null && rcmdReason!.content != null && - rcmdReason!.content!.contains('关注') + regex.hasMatch(rcmdReason!.content!) ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 From f8897f74bfae9b2d86d8d30625f3816ab9797462 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 7 Jun 2024 23:48:48 +0800 Subject: [PATCH 128/438] =?UTF-8?q?feat:=20web=E7=AB=AF=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 +- lib/http/login.dart | 99 ++++++++++++++++++++++--------- lib/pages/login/controller.dart | 80 ++++++++++++++++++++++--- lib/pages/login/view.dart | 37 +++++++----- lib/pages/mine/controller.dart | 5 +- lib/pages/webview/controller.dart | 52 +--------------- lib/pages/webview/view.dart | 4 +- lib/utils/login.dart | 58 ++++++++++++++++++ 8 files changed, 231 insertions(+), 108 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..8a72479b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -400,10 +400,12 @@ class Api { '${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web'; // web端短信验证码 - static const String smsCode = + static const String webSmsCode = '${HttpString.passBaseUrl}/x/passport-login/web/sms/send'; // web端验证码登录 + static const String webSmsLogin = + "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 diff --git a/lib/http/login.dart b/lib/http/login.dart index ff3fee23..13236623 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; import 'package:encrypt/encrypt.dart'; +import 'package:pilipala/http/constants.dart'; import 'package:uuid/uuid.dart'; import '../models/login/index.dart'; import '../utils/login.dart'; @@ -21,32 +22,32 @@ class LoginHttp { } } - static Future sendSmsCode({ - int? cid, - required int tel, - required String token, - required String challenge, - required String validate, - required String seccode, - }) async { - var res = await Request().post( - Api.appSmsCode, - data: { - 'cid': cid, - 'tel': tel, - "source": "main_web", - 'token': token, - 'challenge': challenge, - 'validate': validate, - 'seccode': seccode, - }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - // headers: {'user-agent': ApiConstants.userAgent} - ), - ); - print(res); - } + // static Future sendSmsCode({ + // int? cid, + // required int tel, + // required String token, + // required String challenge, + // required String validate, + // required String seccode, + // }) async { + // var res = await Request().post( + // Api.appSmsCode, + // data: { + // 'cid': cid, + // 'tel': tel, + // "source": "main_web", + // 'token': token, + // 'challenge': challenge, + // 'validate': validate, + // 'seccode': seccode, + // }, + // options: Options( + // contentType: Headers.formUrlEncodedContentType, + // // headers: {'user-agent': ApiConstants.userAgent} + // ), + // ); + // print(res); + // } // web端验证码 static Future sendWebSmsCode({ @@ -60,6 +61,7 @@ class LoginHttp { Map data = { 'cid': cid, 'tel': tel, + "source": "main_web", 'token': token, 'challenge': challenge, 'validate': validate, @@ -67,17 +69,56 @@ class LoginHttp { }; FormData formData = FormData.fromMap({...data}); var res = await Request().post( - Api.smsCode, + Api.webSmsCode, data: formData, options: Options( contentType: Headers.formUrlEncodedContentType, ), ); - print(res); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } } // web端验证码登录 - static Future loginInByWebSmsCode() async {} + static Future loginInByWebSmsCode({ + int? cid, + required int tel, + required int code, + required String captchaKey, + }) async { + // webSmsLogin + Map data = { + "cid": cid, + "tel": tel, + "code": code, + "source": "main_mini", + "keep": 0, + "captcha_key": captchaKey, + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.webSmsLogin, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } // web端密码登录 static Future liginInByWebPwd() async {} diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index c002fdf9..e47653e2 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,11 +1,16 @@ +import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; +import 'package:pilipala/pages/webview/index.dart'; +import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { final GlobalKey mobFormKey = GlobalKey(); @@ -26,9 +31,19 @@ class LoginPageController extends GetxController { final Gt3FlutterPlugin captcha = Gt3FlutterPlugin(); + // 倒计时60s + RxInt seconds = 60.obs; + late Timer timer; + RxBool smsCodeSendStatus = false.obs; + // 默认密码登录 RxInt loginType = 0.obs; + late String captchaLey; + + late int tel; + late int webSmsCode; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -43,6 +58,7 @@ class LoginPageController extends GetxController { curve: Curves.easeInOut, ); passwordTextFieldNode.requestFocus(); + (mobFormKey.currentState as FormState).save(); } } @@ -86,18 +102,32 @@ class LoginPageController extends GetxController { } } - // 验证码登录 - void loginInByCode() { - if ((msgCodeFormKey.currentState as FormState).validate()) {} + // web端验证码登录 + void loginInByCode() async { + if ((msgCodeFormKey.currentState as FormState).validate()) { + (msgCodeFormKey.currentState as FormState).save(); + var res = await LoginHttp.loginInByWebSmsCode( + cid: 86, + tel: tel, + code: webSmsCode, + captchaKey: captchaLey, + ); + if (res['status']) { + log(res.toString()); + LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } } - // app端验证码 - void getMsgCode() async { + // 获取app端验证码 + void getAppMsgCode() async { getCaptcha((data) async { CaptchaDataModel captchaData = data; var res = await LoginHttp.sendAppSmsCode( cid: 86, - tel: 13734077064, + tel: tel, token: captchaData.token!, challenge: captchaData.geetest!.challenge!, validate: captchaData.validate!, @@ -121,7 +151,7 @@ class LoginPageController extends GetxController { captcha.addEventHandler(onShow: (Map message) async { SmartDialog.dismiss(); }, onClose: (Map message) async { - SmartDialog.showToast('关闭验证'); + SmartDialog.showToast('取消验证'); }, onResult: (Map message) async { debugPrint("Captcha result: $message"); String code = message["code"]; @@ -201,4 +231,40 @@ class LoginPageController extends GetxController { captcha.startCaptcha(registerData); } else {} } + + // 获取web端验证码 + void getWebMsgCode() async { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var res = await LoginHttp.sendWebSmsCode( + cid: 86, + tel: tel, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + captchaLey = res['data']['captcha_key']; + SmartDialog.showToast('验证码已发送'); + // 倒计时60s + smsCodeSendStatus.value = true; + startTimer(); + } else { + SmartDialog.showToast(res['msg']); + } + }); + } + + void startTimer() { + timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (seconds.value > 0) { + seconds.value--; + } else { + seconds.value = 60; + smsCodeSendStatus.value = false; + timer.cancel(); + } + }); + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 6521e9d9..ae0a60b9 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -93,9 +93,7 @@ class _LoginPageState extends State { validator: (v) { return v!.trim().isNotEmpty ? null : "手机号码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.tel = int.parse(val!), onEditingComplete: () { _loginPageCtr.nextStep(); }, @@ -308,21 +306,28 @@ class _LoginPageState extends State { ? null : "验证码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.webSmsCode = + int.parse(val!), ), - Positioned( - right: 8, - top: 4, - child: Center( - child: TextButton( - onPressed: () => - _loginPageCtr.getMsgCode(), - child: const Text('获取验证码'), + Obx(() { + return Positioned( + right: 8, + top: 0, + child: Center( + child: TextButton( + onPressed: _loginPageCtr + .smsCodeSendStatus.value + ? null + : () => + _loginPageCtr.getWebMsgCode(), + child: _loginPageCtr + .smsCodeSendStatus.value + ? Text( + '重新获取(${_loginPageCtr.seconds.value}s)') + : const Text('获取验证码')), ), - ), - ), + ); + }) ], ), ), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index a61bb820..153a7162 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -6,7 +6,6 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; -import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { @@ -34,8 +33,8 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - RoutePush.loginPush(); - // Get.toNamed('/loginPage'); + // RoutePush.loginPush(); + Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index bdacc652..e0ff113c 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -76,7 +76,7 @@ class WebviewController extends GetxController { (url.startsWith( 'https://passport.bilibili.com/web/sso/exchange_cookie') || url.startsWith('https://m.bilibili.com/'))) { - confirmLogin(url); + LoginUtils.confirmLogin(url, controller); } }, onWebResourceError: (WebResourceError error) {}, @@ -97,54 +97,4 @@ class WebviewController extends GetxController { ) ..loadRequest(Uri.parse(url)); } - - confirmLogin(url) async { - var content = ''; - if (url != null) { - content = '${content + url}; \n'; - } - try { - await SetCookie.onSet(); - final result = await UserHttp.userInfo(); - if (result['status'] && result['data'].isLogin) { - SmartDialog.showToast('登录成功'); - try { - Box userInfoCache = GStrorage.userInfo; - if (!userInfoCache.isOpen) { - userInfoCache = await Hive.openBox('userInfo'); - } - await userInfoCache.put('userInfoCache', result['data']); - - final HomeController homeCtr = Get.find(); - homeCtr.updateLoginStatus(true); - homeCtr.userFace.value = result['data'].face; - final MediaController mediaCtr = Get.find(); - mediaCtr.mid = result['data'].mid; - await LoginUtils.refreshLoginStatus(true); - } catch (err) { - SmartDialog.show(builder: (BuildContext context) { - return AlertDialog( - title: const Text('登录遇到问题'), - content: Text(err.toString()), - actions: [ - TextButton( - onPressed: () => controller.reload(), - child: const Text('确认'), - ) - ], - ); - }); - } - Get.back(); - } else { - // 获取用户信息失败 - SmartDialog.showToast(result['msg']); - Clipboard.setData(ClipboardData(text: result['msg'])); - } - } catch (e) { - SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); - content = content + e.toString(); - Clipboard.setData(ClipboardData(text: content)); - } - } } diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 8edd2189..cba40ad1 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/login.dart'; import 'package:url_launcher/url_launcher.dart'; import 'controller.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -43,7 +44,8 @@ class _WebviewPageState extends State { Obx( () => _webviewController.type.value == 'login' ? TextButton( - onPressed: () => _webviewController.confirmLogin(null), + onPressed: () => + LoginUtils.confirmLogin(null, _webviewController), child: const Text('刷新登录状态'), ) : const SizedBox(), diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 59c53027..2687a8c2 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -2,12 +2,18 @@ import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/mine/index.dart'; +import 'package:pilipala/utils/cookie.dart'; +import 'package:pilipala/utils/storage.dart'; import 'package:uuid/uuid.dart'; class LoginUtils { @@ -57,4 +63,56 @@ class LoginUtils { String uuid = getUUID() + getUUID(); return 'XY${uuid.substring(0, 35).toUpperCase()}'; } + + static confirmLogin(url, controller) async { + var content = ''; + if (url != null) { + content = '${content + url}; \n'; + } + try { + await SetCookie.onSet(); + final result = await UserHttp.userInfo(); + if (result['status'] && result['data'].isLogin) { + SmartDialog.showToast('登录成功'); + try { + Box userInfoCache = GStrorage.userInfo; + if (!userInfoCache.isOpen) { + userInfoCache = await Hive.openBox('userInfo'); + } + await userInfoCache.put('userInfoCache', result['data']); + + final HomeController homeCtr = Get.find(); + homeCtr.updateLoginStatus(true); + homeCtr.userFace.value = result['data'].face; + final MediaController mediaCtr = Get.find(); + mediaCtr.mid = result['data'].mid; + await LoginUtils.refreshLoginStatus(true); + } catch (err) { + SmartDialog.show(builder: (BuildContext context) { + return AlertDialog( + title: const Text('登录遇到问题'), + content: Text(err.toString()), + actions: [ + TextButton( + onPressed: controller != null + ? () => controller.reload() + : SmartDialog.dismiss, + child: const Text('确认'), + ) + ], + ); + }); + } + Get.back(); + } else { + // 获取用户信息失败 + SmartDialog.showToast(result['msg']); + Clipboard.setData(ClipboardData(text: result['msg'])); + } + } catch (e) { + SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); + content = content + e.toString(); + Clipboard.setData(ClipboardData(text: content)); + } + } } From 9888aba62e93b87c75832c543697302930c588e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:26:08 +0800 Subject: [PATCH 129/438] =?UTF-8?q?feat:=20web=E7=AB=AF=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/login.dart | 38 ++++++++++++++++++++++++++ lib/pages/login/controller.dart | 47 +++++++++++++++++++++++++++------ lib/pages/login/view.dart | 2 +- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 8a72479b..e0b92c02 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -408,6 +408,8 @@ class Api { "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 + static const String loginInByWebPwd = + "${HttpString.passBaseUrl}/x/passport-login/web/login"; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index 13236623..a97360c7 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -214,4 +214,42 @@ class LoginHttp { ); print(res); } + + // web端密码登录 + static Future loginInByWebPwd({ + required int username, + required String password, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + Map data = { + 'username': username, + 'password': password, + 'keep': 0, + 'token': token, + 'challenge': challenge, + 'validate': validate, + 'seccode': seccode, + 'source': 'main-fe-header', + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.loginInByWebPwd, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index e47653e2..354076f9 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,15 +1,13 @@ import 'dart:async'; -import 'dart:developer'; import 'dart:io'; +import 'package:encrypt/encrypt.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; -import 'package:pilipala/pages/webview/index.dart'; import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { @@ -39,7 +37,7 @@ class LoginPageController extends GetxController { // 默认密码登录 RxInt loginType = 0.obs; - late String captchaLey; + late String captchaKey; late int tel; late int webSmsCode; @@ -102,6 +100,39 @@ class LoginPageController extends GetxController { } } + // web端密码登录 + void loginInByWebPassword() async { + if ((passwordFormKey.currentState as FormState).validate()) { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var webKeyRes = await LoginHttp.getWebKey(); + if (webKeyRes['status']) { + String rhash = webKeyRes['data']['hash']; + String key = webKeyRes['data']['key']; + dynamic publicKey = RSAKeyParser().parse(key); + String passwordEncryptyed = Encrypter(RSA(publicKey: publicKey)) + .encrypt(rhash + passwordTextController.text) + .base64; + var res = await LoginHttp.loginInByWebPwd( + username: tel, + password: passwordEncryptyed, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } else { + SmartDialog.showToast(webKeyRes['msg']); + } + }); + } + } + // web端验证码登录 void loginInByCode() async { if ((msgCodeFormKey.currentState as FormState).validate()) { @@ -110,11 +141,10 @@ class LoginPageController extends GetxController { cid: 86, tel: tel, code: webSmsCode, - captchaKey: captchaLey, + captchaKey: captchaKey, ); if (res['status']) { - log(res.toString()); - LoginUtils.confirmLogin('', null); + await LoginUtils.confirmLogin('', null); } else { SmartDialog.showToast(res['msg']); } @@ -245,7 +275,7 @@ class LoginPageController extends GetxController { seccode: captchaData.seccode!, ); if (res['status']) { - captchaLey = res['data']['captcha_key']; + captchaKey = res['data']['captcha_key']; SmartDialog.showToast('验证码已发送'); // 倒计时60s smsCodeSendStatus.value = true; @@ -256,6 +286,7 @@ class LoginPageController extends GetxController { }); } + // 验证码倒计时 void startTimer() { timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (seconds.value > 0) { diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index ae0a60b9..2f406706 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -234,7 +234,7 @@ class _LoginPageState extends State { .primary, // 设置按钮背景色 ), onPressed: () => - _loginPageCtr.loginInByAppPassword(), + _loginPageCtr.loginInByWebPassword(), child: const Text('确认登录'), ) ], From 4fe8366c2fe6013f8f9f941ebc6b33437709975e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:42:45 +0800 Subject: [PATCH 130/438] upgrade: gt3 plugin --- pubspec.lock | 20 ++++++++++---------- pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index fdcd517c..406fbca4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -674,10 +674,10 @@ packages: dependency: "direct main" description: name: gt3_flutter_plugin - sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1 + sha256: "08f35692e937770ad6b3e2017eb8ef81839a82b8a63f5acf3abab14b688fc36c" url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.8" + version: "0.1.0" hive: dependency: "direct main" description: @@ -981,10 +981,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "1.0.5" nm: dependency: transitive description: @@ -1054,10 +1054,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: @@ -1070,10 +1070,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1238,10 +1238,10 @@ packages: dependency: "direct main" description: name: saver_gallery - sha256: "2657953427ebe5a3b2d08157d41587c01923ccce3f1a616d55082be7470f8530" + sha256: "0f740608072053a0da3b19cc5812a87e36f5c3c0b959d2475c4eb3d697f4a782" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "3.0.3" screen_brightness: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index f758d308..f02c2ead 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -132,7 +132,7 @@ dependencies: # html渲染 flutter_html: ^3.0.0-beta.2 # 极验 - gt3_flutter_plugin: ^0.0.8 + gt3_flutter_plugin: ^0.1.0 uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 catcher_2: ^1.2.6 From 3a94281310ecf86ccdd0a5d21853b9ed38dceff6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 03:14:37 +0800 Subject: [PATCH 131/438] =?UTF-8?q?feat:=20web=E7=AB=AF=E6=89=AB=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 12 +++- lib/http/login.dart | 27 +++++++ lib/pages/login/controller.dart | 35 +++++++++ lib/pages/login/view.dart | 121 ++++++++++++++++++++++++++------ pubspec.lock | 16 +++++ pubspec.yaml | 2 + 6 files changed, 190 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e0b92c02..b0fc0556 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -405,11 +405,19 @@ class Api { // web端验证码登录 static const String webSmsLogin = - "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; + '${HttpString.passBaseUrl}/x/passport-login/web/login/sms'; // web端密码登录 static const String loginInByWebPwd = - "${HttpString.passBaseUrl}/x/passport-login/web/login"; + '${HttpString.passBaseUrl}/x/passport-login/web/login'; + + // web端二维码 + static const String qrCodeApi = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate'; + + // 扫码登录 + static const String loginInByQrcode = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll'; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index a97360c7..2437b72a 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -252,4 +252,31 @@ class LoginHttp { return {'status': false, 'data': [], 'msg': res.data['message']}; } } + + // web端登录二维码 + static Future getWebQrcode() async { + var res = await Request().get(Api.qrCodeApi); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } + + // web端二维码轮询登录状态 + static Future queryWebQrcodeStatus(String qrcodeKey) async { + var res = await Request() + .get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey}); + if (res.data['data']['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 354076f9..b5ddba16 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -42,6 +42,10 @@ class LoginPageController extends GetxController { late int tel; late int webSmsCode; + RxInt validSeconds = 180.obs; + late Timer validTimer; + late String qrcodeKey; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -298,4 +302,35 @@ class LoginPageController extends GetxController { } }); } + + // 获取登录二维码 + Future getWebQrcode() async { + var res = await LoginHttp.getWebQrcode(); + validSeconds.value = 180; + if (res['status']) { + qrcodeKey = res['data']['qrcode_key']; + validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) { + if (validSeconds.value > 0) { + validSeconds.value--; + queryWebQrcodeStatus(); + } else { + getWebQrcode(); + validTimer.cancel(); + } + }); + return res; + } else { + SmartDialog.showToast(res['msg']); + } + } + + // 轮询二维码登录状态 + Future queryWebQrcodeStatus() async { + var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + validTimer.cancel(); + Get.back(); + } + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 2f406706..cd91ea26 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'controller.dart'; @@ -37,6 +38,105 @@ class _LoginPageState extends State { icon: const Icon(Icons.arrow_back), ), ), + actions: [ + IconButton( + tooltip: '浏览器打开', + onPressed: () { + Get.offNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + }, + icon: const Icon(Icons.language), + ), + IconButton( + tooltip: '二维码登录', + onPressed: () { + showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return AlertDialog( + title: Row( + children: [ + const Text('扫码登录'), + IconButton( + onPressed: () { + setState(() {}); + }, + icon: const Icon(Icons.refresh), + ), + ], + ), + contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4), + content: AspectRatio( + aspectRatio: 1, + child: Container( + width: 200, + padding: const EdgeInsets.all(12), + child: FutureBuilder( + future: _loginPageCtr.getWebQrcode(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map data = snapshot.data as Map; + return QrImageView( + data: data['data']['url'], + backgroundColor: Colors.transparent, + ); + } else { + return const Center( + child: SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () {}, + child: Obx(() { + return Text( + '有效期: ${_loginPageCtr.validSeconds.value}s', + style: Theme.of(context).textTheme.titleMedium, + ); + }), + ), + TextButton( + onPressed: () {}, + child: Text( + '检查登录状态', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + ), + ), + ) + ], + ); + }); + }, + ); + }, + icon: const Icon(Icons.qr_code), + ), + const SizedBox(width: 22), + ], ), body: PageView( physics: const NeverScrollableScrollPhysics(), @@ -99,27 +199,6 @@ class _LoginPageState extends State { }, ), ), - GestureDetector( - onTap: () { - Get.offNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); - }, - child: Padding( - padding: const EdgeInsets.only(left: 2), - child: Text( - '使用网页端登录', - style: TextStyle( - color: Theme.of(context).colorScheme.primary), - ), - ), - ), const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/pubspec.lock b/pubspec.lock index 406fbca4..5c6f2b43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1218,6 +1218,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f02c2ead..66187abf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -145,6 +145,8 @@ dependencies: # 投屏 dlna_dart: ^0.0.8 lottie: ^3.1.2 + # 二维码 + qr_flutter: ^4.1.0 dev_dependencies: flutter_test: From aee52b02470f91db723b24a9ce2b9a6dbc573c10 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 04:09:00 +0800 Subject: [PATCH 132/438] =?UTF-8?q?opt:=20=E6=B6=88=E6=81=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E9=AA=A8=E6=9E=B6=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 270 +++++++++++++++++++----------------- 1 file changed, 142 insertions(+), 128 deletions(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa7ad60b..fa95463b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -102,134 +103,83 @@ class _WhisperPageState extends State { }, child: SingleChildScrollView( controller: _scrollController, - child: Column( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return ListTile( - onTap: () { - sessionList[i].unreadCount = 0; - sessionList.refresh(); - Get.toNamed( - '/whisperDetail', - parameters: { - 'talkerId': sessionList[i] - .talkerId - .toString(), - 'name': sessionList[i] - .accountInfo - .name, - 'face': sessionList[i] - .accountInfo - .face, - 'mid': sessionList[i] - .accountInfo - .mid - .toString(), - }, - ); - }, - leading: Badge( - isLabelVisible: - sessionList[i].unreadCount > 0, - label: Text(sessionList[i] - .unreadCount - .toString()), - alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionList[i] - .accountInfo - .face, - ), - ), - title: Text( - sessionList[i].accountInfo.name), - subtitle: Text( - sessionList[i].lastMsg.content != - null && - sessionList[i] - .lastMsg - .content != - '' - ? (sessionList[i] - .lastMsg - .content['text'] ?? - sessionList[i] - .lastMsg - .content['content'] ?? - sessionList[i] - .lastMsg - .content['title'] ?? - sessionList[i] - .lastMsg - .content[ - 'reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline)), - trailing: Text( - Utils.dateFormat(sessionList[i] - .lastMsg - .timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, - ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ) - ], + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => + sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), + ); + } + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), ), ), @@ -239,3 +189,67 @@ class _WhisperPageState extends State { ); } } + +class SessionItem extends StatelessWidget { + final dynamic sessionItem; + final Function changeFucCall; + + const SessionItem({ + super.key, + required this.sessionItem, + required this.changeFucCall, + }); + + @override + Widget build(BuildContext context) { + final content = sessionItem.lastMsg.content; + return ListTile( + onTap: () { + sessionItem.unreadCount = 0; + changeFucCall.call(); + Get.toNamed( + '/whisperDetail', + parameters: { + 'talkerId': sessionItem.talkerId.toString(), + 'name': sessionItem.accountInfo.name, + 'face': sessionItem.accountInfo.face, + 'mid': sessionItem.accountInfo.mid.toString(), + }, + ); + }, + leading: Badge( + isLabelVisible: sessionItem.unreadCount > 0, + label: Text(sessionItem.unreadCount.toString()), + alignment: Alignment.topRight, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), + ), + title: Text(sessionItem.accountInfo.name), + subtitle: Text( + content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline)), + trailing: Text( + Utils.dateFormat(sessionItem.lastMsg.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + ); + } +} From f4b7f77e440cbd8d51558f74312a4e4439fc1f8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 13:29:36 +0800 Subject: [PATCH 133/438] upgrade: new version syntax --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++++---- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 ++--- lib/pages/home/view.dart | 8 ++++---- lib/pages/live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++---- lib/pages/media/view.dart | 4 ++-- lib/pages/member/view.dart | 5 ++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 +++--- lib/pages/setting/style_setting.dart | 6 +++--- lib/pages/setting/widgets/switch_item.dart | 6 +++--- .../video/detail/introduction/widgets/page_panel.dart | 2 +- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/toolbar_icon_button.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/header_control.dart | 8 ++++---- lib/pages/whisper_detail/view.dart | 6 +++--- lib/plugin/pl_player/view.dart | 6 +++--- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- lib/plugin/pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- 28 files changed, 52 insertions(+), 56 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index cbc6659b..b130faae 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 1265477f..c674b223 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 95d4d898..bb85be1a 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), + padding: + WidgetStateProperty.all(EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) { + WidgetStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3df7ce25..345a47b6 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 8acdc26a..e66e2a91 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 79e5c073..72d7b4a0 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index baebfedb..8e71df6f 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,11 +217,10 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty - .resolveWith( + WidgetStateProperty.resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a25389bd..0c45a262 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: MaterialStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: WidgetStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 4dd7c538..6abb1260 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..ecc53af2 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -253,8 +253,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary @@ -344,8 +343,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index cc413e59..f6a033e5 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( + WidgetStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index b6648647..bb0d92be 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -90,9 +90,8 @@ class _MemberPageState extends State () => Text( _memberController.memberInfo.value.name ?? '', style: TextStyle( - color: Theme.of(context) - .colorScheme - .onBackground, + color: + Theme.of(context).colorScheme.onSurface, fontSize: 14), ), ), diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..3207a8ea 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -92,7 +92,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index aaaa8b84..885a831c 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..e07bef66 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index d0c2bbf2..36c4433e 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { - if (states.isNotEmpty && states.first == MaterialState.selected) { + thumbIcon: + WidgetStateProperty.resolveWith((Set states) { + if (states.isNotEmpty && states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 266f5566..c8111847 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 55c16f7d..08e4d405 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -97,7 +97,7 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: colorScheme.background, + color: colorScheme.surface, ), child: Icon( Icons.offline_bolt, diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index c4390796..bf810ce2 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d7355d81..a136f623 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -259,7 +259,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( + foregroundColor: WidgetStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index fa531684..a0f5d6bc 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 197d6124..37d51106 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -108,7 +108,7 @@ class AiDetail extends StatelessWidget { fontSize: 13, color: Theme.of(context) .colorScheme - .onBackground, + .onSurface, height: 1.5, ), children: [ diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 3ff74b44..d839a1eb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1248,7 +1248,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1263,7 +1263,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1286,7 +1286,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1330,7 +1330,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..042afca1 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..0e405884 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index 5f33311c..bf9467a8 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 6cbe31f0..7547a1cb 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0b77b7cc..0727ad68 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( From 897551bf237c65ec364507aa1c3d1cff865c6851 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 15:35:45 +0800 Subject: [PATCH 134/438] =?UTF-8?q?fix:=20=E7=A7=81=E4=BF=A1=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 203 +++++++++--------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index ad11e4c3..0d37e8b3 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -259,115 +259,114 @@ class ChatItem extends StatelessWidget { ); case MsgType.auto_reply_push: return Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .secondaryContainer + .withOpacity(0.4), + borderRadius: const BorderRadius.all( + Radius.circular(16), ), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withOpacity(0.4), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - bottomLeft: Radius.circular(6), - bottomRight: Radius.circular(16), - ), - ), - margin: const EdgeInsets.all(12), - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - content['main_title'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), + ), + margin: const EdgeInsets.all(12), + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + content['main_title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, ), - for (var i in content['sub_cards']) ...[ - const SizedBox(height: 6), - GestureDetector( - onTap: () async { - RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', - caseSensitive: false); - Iterable matches = - bvRegex.allMatches(i['jump_url']); - if (matches.isNotEmpty) { - Match match = matches.first; - String bvid = match.group(0)!; - try { - SmartDialog.showLoading(); - final int cid = await SearchHttp.ab2c(bvid: bvid); - final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': i['cover_url'], - 'heroTag': heroTag, - }), - ); - } catch (err) { - SmartDialog.dismiss(); - SmartDialog.showToast(err.toString()); - } - } else { - SmartDialog.showToast('未匹配到 BV 号'); - Get.toNamed('/webview', - arguments: {'url': i['jump_url']}); - } - }, - child: Row( + ), + for (var i in content['sub_cards']) ...[ + const SizedBox(height: 6), + GestureDetector( + onTap: () async { + RegExp bvRegex = + RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false); + Iterable matches = + bvRegex.allMatches(i['jump_url']); + if (matches.isNotEmpty) { + Match match = matches.first; + String bvid = match.group(0)!; + try { + SmartDialog.showLoading(); + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + SmartDialog.dismiss().then( + (e) => Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': i['cover_url'], + 'heroTag': heroTag, + }), + ); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + } else { + SmartDialog.showToast('未匹配到 BV 号'); + Get.toNamed('/webview', + arguments: {'url': i['jump_url']}); + } + }, + child: Row( + children: [ + NetworkImgLayer( + width: 130, + height: 130 * 9 / 16, + src: i['cover_url'], + ), + const SizedBox(width: 6), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NetworkImgLayer( - width: 130, - height: 130 * 9 / 16, - src: i['cover_url'], + Text( + i['field1'], + maxLines: 2, + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), + ), + Text( + i['field2'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + Text( + i['field3'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), ), - const SizedBox(width: 6), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - i['field1'], - maxLines: 2, - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), - ), - Text( - i['field2'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - Text( - Utils.timeFormat(int.parse(i['field3'])), - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - ], - )), ], )), - ], + ], + ), + ), ], - )); + ], + ), + ); default: return Text( content != null && content != '' From a03d159a86b338c6ca6e7cc145cb83b86e603870 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:36:55 +0800 Subject: [PATCH 135/438] =?UTF-8?q?mod:=20=E5=86=85=E5=AE=B9=E5=88=86?= =?UTF-8?q?=E5=8C=BA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 10 ++++++---- lib/pages/search_panel/controller.dart | 6 +++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 70980547..075defc7 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,7 +75,7 @@ class SearchHttp { required page, String? order, int? duration, - int? tid, + int? tids, }) async { var reqData = { 'search_type': searchType.type, @@ -85,7 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, - if (tid != null) 'tid': tid, + if (tids != null && tids != -1) 'tids': tids, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index fbd1cfc6..d3ffafea 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -18,10 +18,12 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + color: isSelect + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 56d83601..35113198 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,8 +16,8 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; - // 视频分区筛选 仅用于搜索视频 - RxInt tid = (-1).obs; + // 视频分区筛选 仅用于搜索视频 -1时不传 + RxInt tids = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( @@ -26,7 +26,7 @@ class SearchPanelController extends GetxController { page: page.value, order: searchType!.type != 'video' ? null : order.value, duration: searchType!.type != 'video' ? null : duration.value, - tid: searchType!.type != 'video' ? null : tid.value, + tids: searchType!.type != 'video' ? null : tids.value, ); if (result['status']) { if (type == 'onRefresh') { diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 06ee5d4b..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -323,7 +323,7 @@ class VideoPanelController extends GetxController { SearchPanelController ctr = Get.find( tag: 'video${searchPanelCtr.keyword!}'); - ctr.tid.value = i['value']; + ctr.tids.value = i['value']; Get.back(); SmartDialog.showLoading(msg: '获取中'); await ctr.onRefresh(); From b37232931bc0c838b37af1cd2d6620afb0ca293b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:45:27 +0800 Subject: [PATCH 136/438] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=94=B5=E6=B1=A0=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 1a2ce989..05c0476c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,6 @@ import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/pages/main/view.dart'; -import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; @@ -66,7 +65,6 @@ void main() async { } PiliSchame.init(); - DisableBatteryOpt(); } class MyApp extends StatelessWidget { From be3bffa33cee1d142d78bb7917c288ef9502ed37 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:54:40 +0800 Subject: [PATCH 137/438] =?UTF-8?q?opt:=20=E6=88=91=E7=9A=84=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/subscription/view.dart | 31 +++++++++++++++--------- lib/pages/subscription/widgets/item.dart | 19 +++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index bcc03cc3..e1d1820d 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -53,18 +53,25 @@ class _SubPageState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { - return Obx( - () => ListView.builder( - controller: scrollController, - itemCount: _subController.subFolderData.value.list!.length, - itemBuilder: (context, index) { - return SubItem( - subFolderItem: - _subController.subFolderData.value.list![index], - cancelSub: _subController.cancelSub); - }, - ), - ); + if (_subController.subFolderData.value.list!.isNotEmpty) { + return Obx( + () => ListView.builder( + controller: scrollController, + itemCount: _subController.subFolderData.value.list!.length, + itemBuilder: (context, index) { + return SubItem( + subFolderItem: + _subController.subFolderData.value.list![index], + cancelSub: _subController.cancelSub); + }, + ), + ); + } else { + return const CustomScrollView( + physics: NeverScrollableScrollPhysics(), + slivers: [HttpError(errMsg: '', btnText: '没有数据', fn: null)], + ); + } } else { return CustomScrollView( physics: const NeverScrollableScrollPhysics(), diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 0d424611..6c1cc2b9 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -114,16 +114,15 @@ class VideoContent extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - SizedBox( - height: 35, - width: 35, - child: IconButton( - onPressed: () => cancelSub?.call(subFolderItem), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.outline, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - ), - icon: const Icon(Icons.delete_outline, size: 18), + IconButton( + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => cancelSub?.call(subFolderItem), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, ), ) ], From 006ba8bc59f1898f77d7c95c4a8013a861455514 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 9 Jun 2024 14:51:59 +0800 Subject: [PATCH 138/438] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 6 +- lib/models/common/action_type.dart | 1 - lib/pages/setting/pages/action_menu_set.dart | 10 +- lib/pages/setting/style_setting.dart | 10 +- .../video/detail/introduction/controller.dart | 55 ++-- lib/pages/video/detail/introduction/view.dart | 234 ++++++++++++++---- .../introduction/widgets/action_item.dart | 39 +-- lib/utils/global_data.dart | 3 +- 8 files changed, 246 insertions(+), 112 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 04fe6670..a400600f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - gt3_flutter_plugin (0.0.8): + - gt3_flutter_plugin (0.0.9): - Flutter - GT3Captcha-iOS - GT3Captcha-iOS (0.15.8.3) @@ -171,13 +171,13 @@ SPEC CHECKSUMS: flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 + gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart index 2284df6c..b888be17 100644 --- a/lib/models/common/action_type.dart +++ b/lib/models/common/action_type.dart @@ -11,7 +11,6 @@ enum ActionType { dislike, downloadCover, copyLink, - threeAction, // backgroundPlay, // listenVideo, // downloadVideo, diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart index f7d92e17..7a4fd9ba 100644 --- a/lib/pages/setting/pages/action_menu_set.dart +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/action_type.dart'; +import 'package:pilipala/utils/global_data.dart'; import '../../../utils/storage.dart'; class ActionMenuSetPage extends StatefulWidget { @@ -12,14 +13,14 @@ class ActionMenuSetPage extends StatefulWidget { } class _ActionMenuSetPageState extends State { - Box settingStorage = GStrorage.setting; + Box setting = GStrorage.setting; late List actionTypeSort; late List allLabels; @override void initState() { super.initState(); - actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); allLabels = actionMenuConfig; allLabels.sort((a, b) { @@ -36,8 +37,9 @@ class _ActionMenuSetPageState extends State { .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) .map((i) => (i['value'] as ActionType).value) .toList(); - settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); - SmartDialog.showToast('保存成功,下次启动时生效'); + setting.put(SettingBoxKey.actionTypeSort, sortedTabbar); + GlobalData().actionTypeSort = sortedTabbar; + SmartDialog.showToast('操作成功'); } void onReorder(int oldIndex, int newIndex) { diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 20fdada0..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,11 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), - ListTile( - dense: false, - onTap: () => Get.toNamed('/actionMenuSet'), - title: Text('操作菜单设置', style: titleStyle), - ), + // ListTile( + // dense: false, + // onTap: () => Get.toNamed('/actionMenuSet'), + // title: Text('操作菜单设置', style: titleStyle), + // ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9ed04870..9c542f21 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -38,6 +38,8 @@ class VideoIntroController extends GetxController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + // 是否不喜欢 + RxBool hasDisLike = false.obs; Box userInfoCache = GStrorage.userInfo; bool userLogin = false; Rx favFolderData = FavFolderData().obs; @@ -153,36 +155,16 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('🙏 UP已经收到了~'); return false; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('一键三连 给UP送温暖'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('点错了')), - TextButton( - onPressed: () async { - var result = await VideoHttp.oneThree(bvid: bvid); - if (result['status']) { - hasLike.value = result["data"]["like"]; - hasCoin.value = result["data"]["coin"]; - hasFav.value = result["data"]["fav"]; - SmartDialog.showToast('三连成功 🎉'); - } else { - SmartDialog.showToast(result['msg']); - } - SmartDialog.dismiss(); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); + var result = await VideoHttp.oneThree(bvid: bvid); + print('🤣🦴:${result["data"]}'); + if (result['status']) { + hasLike.value = result["data"]["like"]; + hasCoin.value = result["data"]["coin"]; + hasFav.value = result["data"]["fav"]; + SmartDialog.showToast('三连成功'); + } else { + SmartDialog.showToast(result['msg']); + } } // (取消)点赞 @@ -193,9 +175,8 @@ class VideoIntroController extends GetxController { } var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value); if (result['status']) { - // hasLike.value = result["data"] == 1 ? true : false; if (!hasLike.value) { - SmartDialog.showToast('点赞成功 👍'); + SmartDialog.showToast('点赞成功'); hasLike.value = true; videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1; } else if (hasLike.value) { @@ -215,6 +196,10 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } + if (hasCoin.value) { + SmartDialog.showToast('已投过币了'); + return; + } showDialog( context: Get.context!, builder: (context) { @@ -236,7 +221,7 @@ class VideoIntroController extends GetxController { var res = await VideoHttp.coinVideo( bvid: bvid, multiply: _tempThemeValue); if (res['status']) { - SmartDialog.showToast('投币成功 👏'); + SmartDialog.showToast('投币成功'); hasCoin.value = true; videoDetail.value.stat!.coin = videoDetail.value.stat!.coin! + _tempThemeValue; @@ -269,7 +254,7 @@ class VideoIntroController extends GetxController { if (result['status']) { // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } @@ -299,7 +284,7 @@ class VideoIntroController extends GetxController { Get.back(); // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index ceec8fa4..91287c59 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -15,6 +18,7 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; @@ -146,13 +150,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - void Function()? handleState(Future Function() action) { + // 一键三连动画 + late AnimationController _controller; + late Animation _scaleTransition; + final RxDouble _progress = 0.0.obs; + + void Function()? handleState(Future Function() action) { return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); + isProcessing = true; + await action.call(); + isProcessing = false; }; } @@ -169,6 +178,25 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); + + /// 一键三连动画 + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + reverseDuration: const Duration(milliseconds: 300), + vsync: this, + ); + _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) + ..addListener(() async { + _progress.value = _scaleTransition.value - 0.5; + if (_progress.value == 1) { + if (_controller.status == AnimationStatus.completed) { + await videoIntroController.actionOneThree(); + } + _progress.value = 0; + _scaleTransition.removeListener(() {}); + _controller.stop(); + } + }); } // 收藏 @@ -249,6 +277,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); + _controller.dispose(); + _scaleTransition.removeListener(() {}); super.dispose(); } @@ -538,6 +568,157 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } Widget actionGrid(BuildContext context, videoIntroController) { + final actionTypeSort = GlobalData().actionTypeSort; + + Widget progressWidget(progress) { + return SizedBox( + width: 68, + height: 68, + child: CircularProgressIndicator( + value: progress.value, + strokeWidth: 4, + ), + ); + } + + Map menuListWidgets = { + 'like': Obx( + () { + bool likeStatus = videoIntroController.hasLike.value; + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + InkWell( + onTapDown: (details) { + feedBack(); + _controller.forward(); + }, + onTapUp: (TapUpDetails details) { + if (_progress.value == 0) { + feedBack(); + EasyThrottle.throttle( + 'my-throttler', const Duration(milliseconds: 200), () { + videoIntroController.actionLikeVideo(); + }); + } + _controller.reverse(); + }, + borderRadius: StyleString.mdRadius, + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Icon( + key: ValueKey(likeStatus), + likeStatus + ? Icons.thumb_up + : Icons.thumb_up_alt_outlined, + color: likeStatus + ? colorScheme.primary + : colorScheme.outline, + ), + ), + const SizedBox(height: 6), + Text( + widget.videoDetail!.stat!.like!.toString(), + style: TextStyle( + color: likeStatus ? colorScheme.primary : null, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), + ), + ), + ], + ); + }, + ), + 'coin': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: Image.asset('assets/images/coin.png', width: 30), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), + ), + ], + ), + ), + 'collect': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), + ), + ], + ), + ), + 'watchLater': ActionItem( + icon: const Icon(Icons.watch_later_outlined), + onTap: () async { + final res = + await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); + SmartDialog.showToast(res['msg']); + }, + selectStatus: false, + text: '稍后看', + ), + 'share': ActionItem( + icon: const Icon(Icons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + text: '分享', + ), + 'dislike': Obx( + () => ActionItem( + icon: const Icon(Icons.thumb_down_alt_outlined), + selectIcon: const Icon(Icons.thumb_down), + onTap: () {}, + selectStatus: videoIntroController.hasDisLike.value, + text: '不喜欢', + ), + ), + 'downloadCover': ActionItem( + icon: const Icon(Icons.image_outlined), + onTap: () {}, + selectStatus: false, + text: '下载封面', + ), + 'copyLink': ActionItem( + icon: const Icon(Icons.link_outlined), + onTap: () {}, + selectStatus: false, + text: '复制链接', + ), + }; + final List list = []; + for (var i = 0; i < actionTypeSort.length; i++) { + list.add(menuListWidgets[actionTypeSort[i]]!); + } + return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Container( @@ -545,50 +726,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: constraints.maxWidth / 5, child: ListView( scrollDirection: Axis.horizontal, - children: [ - Obx( - () => ActionItem( - icon: const Icon(Icons.thumb_up_alt_outlined), - selectIcon: const Icon(Icons.thumb_up), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - text: widget.videoDetail!.stat!.like!.toString()), - ), - Obx( - () => ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ), - Obx( - () => ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ), - ActionItem( - icon: const Icon(Icons.watch_later_outlined), - onTap: () async { - final res = - await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); - SmartDialog.showToast(res['msg']); - }, - selectStatus: false, - text: '稍后看', - ), - ActionItem( - icon: const Icon(Icons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - text: '分享', - ), - ], + children: list, ), ); }); diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 2a89afe7..af409dfc 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -38,20 +38,29 @@ class ActionItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 4), - icon is Icon - ? Icon( - selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ) - : Image.asset( - 'assets/images/coin.png', - width: 25, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: icon is Icon + ? Icon( + selectStatus + ? selectIcon!.icon ?? icon!.icon + : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + key: ValueKey(selectStatus), + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), const SizedBox(height: 6), Text( text ?? '', @@ -60,7 +69,7 @@ class ActionItem extends StatelessWidget { selectStatus ? Theme.of(context).colorScheme.primary : null, fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, ), - ) + ), ], ), ), diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 29791210..97bff5a5 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -11,7 +11,8 @@ class GlobalData { bool enablePlayerControlAnimation = true; final bool enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - + List actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); // 私有构造函数 GlobalData._(); From d94ddca9056f1f00f8b2a7412a3cdcf86e07edfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 00:58:04 +0800 Subject: [PATCH 139/438] =?UTF-8?q?opt:=20=E4=B8=89=E8=BF=9E=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E6=A0=B7=E5=BC=8F&=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 91287c59..22f2dfe2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; @@ -576,7 +574,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: 68, child: CircularProgressIndicator( value: progress.value, - strokeWidth: 4, + strokeWidth: 2, ), ); } @@ -593,6 +591,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { InkWell( onTapDown: (details) { feedBack(); + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { From 61c29d30829a59d189225abfc1eae830fc0d1ff4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 01:09:28 +0800 Subject: [PATCH 140/438] =?UTF-8?q?fix:=20=E8=B7=B3=E8=BD=AC=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/login/controller.dart | 6 +++--- lib/pages/login/view.dart | 30 +++++++++++++----------------- lib/pages/mine/controller.dart | 3 +-- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index b5ddba16..fbb06e2f 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -31,7 +31,7 @@ class LoginPageController extends GetxController { // 倒计时60s RxInt seconds = 60.obs; - late Timer timer; + Timer? timer; RxBool smsCodeSendStatus = false.obs; // 默认密码登录 @@ -43,7 +43,7 @@ class LoginPageController extends GetxController { late int webSmsCode; RxInt validSeconds = 180.obs; - late Timer validTimer; + Timer? validTimer; late String qrcodeKey; // 监听pageView切换 @@ -329,7 +329,7 @@ class LoginPageController extends GetxController { var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); if (res['status']) { await LoginUtils.confirmLogin('', null); - validTimer.cancel(); + validTimer?.cancel(); Get.back(); } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -15,8 +15,10 @@ class _LoginPageState extends State { final LoginPageController _loginPageCtr = Get.put(LoginPageController()); @override - void initState() { - super.initState(); + void dispose() { + _loginPageCtr.validTimer?.cancel(); + _loginPageCtr.timer?.cancel(); + super.dispose(); } @override @@ -51,7 +53,7 @@ class _LoginPageState extends State { }, ); }, - icon: const Icon(Icons.language), + icon: const Icon(Icons.language, size: 20), ), IconButton( tooltip: '二维码登录', @@ -90,7 +92,7 @@ class _LoginPageState extends State { Map data = snapshot.data as Map; return QrImageView( data: data['data']['url'], - backgroundColor: Colors.transparent, + backgroundColor: Colors.white, ); } else { return const Center( @@ -131,9 +133,11 @@ class _LoginPageState extends State { ); }); }, - ); + ).then((value) { + _loginPageCtr.validTimer!.cancel(); + }); }, - icon: const Icon(Icons.qr_code), + icon: const Icon(Icons.qr_code, size: 20), ), const SizedBox(width: 22), ], @@ -164,17 +168,9 @@ class _LoginPageState extends State { fontSize: 34, fontWeight: FontWeight.w500), ), - Row( - children: [ - Text( - '请使用您的 BiliBili 账号登录。', - style: Theme.of(context).textTheme.titleSmall!, - ), - GestureDetector( - onTap: () {}, - child: const Icon(Icons.info_outline, size: 16), - ) - ], + Text( + '请使用您的 BiliBili 账号登录。', + style: Theme.of(context).textTheme.titleSmall!, ), Container( margin: const EdgeInsets.only(top: 38, bottom: 15), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 153a7162..75c50d82 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -33,8 +33,7 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - // RoutePush.loginPush(); - Get.toNamed('/loginPage'); + Get.toNamed('/loginPage', preventDuplicates: false); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; From 51be62cd26f06364d9a8ef923ed13b874ce6c88d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 12:50:15 +0800 Subject: [PATCH 141/438] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 22f2dfe2..3b89d1de 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -185,7 +185,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) ..addListener(() async { - _progress.value = _scaleTransition.value - 0.5; + _progress.value = + double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3)); if (_progress.value == 1) { if (_controller.status == AnimationStatus.completed) { await videoIntroController.actionOneThree(); @@ -570,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 68, - height: 68, + width: 33, + height: 33, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -585,16 +586,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { bool likeStatus = videoIntroController.hasLike.value; ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), InkWell( onTapDown: (details) { feedBack(); - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } + // if (videoIntroController.userInfo == null) { + // SmartDialog.showToast('账号未登录'); + // return; + // } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -650,9 +650,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'coin': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,9 +663,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'collect': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), From 128c9201bda0efae89cc627bdd841a5313e921dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 13:36:23 +0800 Subject: [PATCH 142/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E8=B7=9D=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a136f623..d55e2c1c 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -214,7 +214,15 @@ class _VideoReplyNewDialogState extends State ), Container( height: 52, - padding: const EdgeInsets.only(left: 12, right: 12), + padding: const EdgeInsets.only( + left: 12, + right: 12, + ), + margin: EdgeInsets.only( + bottom: toolbarType == 'input' && keyboardHeight == 0.0 + ? MediaQuery.of(context).padding.bottom + : 0, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ From e47816d0c8f748411779bde717de2e2d72c51250 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 14:18:59 +0800 Subject: [PATCH 143/438] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++++++++++++++----- .../introduction/widgets/action_item.dart | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 3b89d1de..93cc26c9 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -571,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 33, - height: 33, + width: const IconThemeData.fallback().size! + 5, + height: const IconThemeData.fallback().size! + 5, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -587,14 +587,19 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), InkWell( onTapDown: (details) { feedBack(); - // if (videoIntroController.userInfo == null) { - // SmartDialog.showToast('账号未登录'); - // return; - // } + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -651,7 +656,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'coin': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,7 +674,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'collect': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index af409dfc..3288b2fa 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -55,7 +55,7 @@ class ActionItem extends StatelessWidget { : Image.asset( key: ValueKey(selectStatus), 'assets/images/coin.png', - width: 25, + width: const IconThemeData.fallback().size, color: selectStatus ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, From beb5945135209ce70a20f48b37f18745b1200298 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 00:06:49 +0800 Subject: [PATCH 144/438] =?UTF-8?q?fix:=20=E7=BD=91=E7=BB=9C=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=97=B6=E7=99=BB=E5=BD=95=E7=8A=B6=E6=80=81=E9=87=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/mine/controller.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 75c50d82..e682a7e5 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -57,8 +57,6 @@ class MineController extends GetxController { } else { resetUserInfo(); } - } else { - resetUserInfo(); } await queryUserStatOwner(); return res; From 7af1ebd3d5fd63d4dab2b5ec8dc0356dd02046d7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 22:59:07 +0800 Subject: [PATCH 145/438] =?UTF-8?q?fix:=20=E5=BC=B9=E5=B9=95=E6=98=BE?= =?UTF-8?q?=E7=A4=BA&=E5=BC=B9=E5=B9=95=E8=AE=BE=E7=BD=AE=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 41 +++++++++++-------- .../video/detail/widgets/header_control.dart | 4 +- lib/plugin/pl_player/controller.dart | 17 +++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a0f5d6bc..a99ee936 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -591,27 +591,32 @@ class _VideoDetailPageState extends State verticalScreen(); } }, - child: Hero( - tag: heroTag, - child: Stack( - children: [ - if (isShowing) videoPlayerPanel, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints constraints) { + return Hero( + tag: heroTag, + child: Stack( + children: [ + if (isShowing) videoPlayerPanel, - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: !vdCtr.autoPlay.value && - vdCtr.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: handlePlayPanel(), + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), + ), + ), ), - ), + ], ), - ], - ), + ); + }, ), ), ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index d839a1eb..b597a060 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1069,7 +1069,9 @@ class _HeaderControlState extends State { ); }); }, - ); + ).then((value) { + widget.controller!.cacheDanmakuOption(); + }); } /// 播放顺序 diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 03c2efbe..2865a117 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1089,6 +1089,16 @@ class PlPlayerController { videoStorage.put(VideoBoxKey.playRepeat, type.value); } + /// 缓存本次弹幕选项 + cacheDanmakuOption() { + localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); + localCache.put(LocalCacheKey.danmakuShowArea, showArea); + localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); + localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); + localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); + localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + } + Future dispose({String type = 'single'}) async { // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { @@ -1118,12 +1128,7 @@ class PlPlayerController { // dataStatus.status.close(); /// 缓存本次弹幕选项 - localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); - localCache.put(LocalCacheKey.danmakuShowArea, showArea); - localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); - localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); - localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); - localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + cacheDanmakuOption(); if (_videoPlayerController != null) { var pp = _videoPlayerController!.platform as NativePlayer; await pp.setProperty('audio-files', ''); From 5c3d3cc957e037a699a7f8a19a32c375227d35f5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 12 Jun 2024 23:37:29 +0800 Subject: [PATCH 146/438] undo: flutter version 3.22.1 -> 3.19.1 --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++--- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/view.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- .../fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 +-- lib/pages/home/view.dart | 8 ++--- .../live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++-- lib/pages/media/view.dart | 4 +-- lib/pages/search/widgets/search_text.dart | 5 +-- .../search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 ++-- lib/pages/setting/style_setting.dart | 6 ++-- lib/pages/setting/widgets/switch_item.dart | 6 ++-- lib/pages/subscription/widgets/item.dart | 2 +- .../introduction/widgets/page_panel.dart | 2 +- .../detail/reply_new/toolbar_icon_button.dart | 4 +-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- .../video/detail/widgets/header_control.dart | 8 ++--- lib/pages/whisper_detail/view.dart | 6 ++-- lib/plugin/pl_player/view.dart | 6 ++-- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- .../pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- pubspec.lock | 36 +++++++++---------- 29 files changed, 72 insertions(+), 72 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index b130faae..cbc6659b 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: WidgetStateProperty.resolveWith((states) { + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c674b223..1265477f 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index bb85be1a..95d4d898 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: - WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all( + EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( - (Set states) { + MaterialStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 345a47b6..3df7ce25 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 258ad531..1775798e 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,7 +162,7 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceContainerHighest + .surfaceVariant .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index e66e2a91..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 72d7b4a0..79e5c073 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 8e71df6f..baebfedb 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,10 +217,11 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty + .resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 0c45a262..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: WidgetStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: MaterialStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 6abb1260..4dd7c538 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 04d5a1a8..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -249,7 +249,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary @@ -339,7 +340,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index f6a033e5..cc413e59 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index d3ffafea..bcc8c17e 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -20,10 +20,7 @@ class SearchText extends StatelessWidget { return Material( color: isSelect ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + : Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index bc9b48c9..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -93,7 +93,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 885a831c..aaaa8b84 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index b6ee5041..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index 36c4433e..d0c2bbf2 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: - WidgetStateProperty.resolveWith((Set states) { - if (states.isNotEmpty && states.first == WidgetState.selected) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 6c1cc2b9..b244d3c7 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -116,7 +116,7 @@ class VideoContent extends StatelessWidget { children: [ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => cancelSub?.call(subFolderItem), icon: Icon( diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index c8111847..266f5566 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index bf810ce2..c4390796 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d55e2c1c..40267559 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -267,7 +267,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all( + foregroundColor: MaterialStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a99ee936..22271c2b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index b597a060..f51edff1 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1250,7 +1250,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1265,7 +1265,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1288,7 +1288,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1332,7 +1332,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 042afca1..1701be33 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith( - (Set states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0e405884..34140be8 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index bf9467a8..5f33311c 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 7547a1cb..6cbe31f0 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0727ad68..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( diff --git a/pubspec.lock b/pubspec.lock index 5c6f2b43..c73ce395 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -762,10 +762,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.flutter-io.cn" source: hosted - version: "0.19.0" + version: "0.18.1" io: dependency: transitive description: @@ -794,26 +794,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.flutter-io.cn" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -973,10 +973,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.flutter-io.cn" source: hosted - version: "1.12.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1467,10 +1467,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.0" + version: "0.6.1" timing: dependency: transitive description: @@ -1627,10 +1627,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.flutter-io.cn" source: hosted - version: "14.2.1" + version: "13.0.0" volume_controller: dependency: transitive description: @@ -1707,10 +1707,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc + sha256: "0d21cfc3bfdd2e30ab2ebeced66512b91134b39e72e97b43db2d47dda1c4e53a" url: "https://pub.flutter-io.cn" source: hosted - version: "3.16.4" + version: "3.16.3" webview_flutter_platform_interface: dependency: transitive description: @@ -1768,5 +1768,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" From 5afb50cac990d41ea5f69001fa637e3a59dee38f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 22:55:54 +0800 Subject: [PATCH 147/438] =?UTF-8?q?fix:=20=E5=BF=AB=E8=BF=9B=E5=BF=AB?= =?UTF-8?q?=E9=80=80=E8=A7=A6=E5=8F=91=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 4 ++-- lib/plugin/pl_player/widgets/backward_seek.dart | 4 ++-- lib/plugin/pl_player/widgets/forward_seek.dart | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..fa0dc444 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -940,7 +940,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( @@ -983,7 +983,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart index 8fddf80a..8289d77c 100644 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ b/lib/plugin/pl_player/widgets/backward_seek.dart @@ -30,14 +30,14 @@ class BackwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart index 7e3886ce..3f68fe0d 100644 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ b/lib/plugin/pl_player/widgets/forward_seek.dart @@ -30,14 +30,14 @@ class ForwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); From 28737543174976d65a0a91e560c21bbc870367f7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 23:13:47 +0800 Subject: [PATCH 148/438] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 7 +++++++ lib/plugin/pl_player/view.dart | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 22271c2b..ef067d1b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -514,6 +514,13 @@ class _VideoDetailPageState extends State showEposideCb: () => vdCtr.videoType == SearchType.video ? videoIntroController.showEposideHandler() : bangumiIntroController.showEposideHandler(), + fullScreenCb: (bool status) { + if (status) { + videoHeight.value = Get.size.height; + } else { + videoHeight.value = defaultVideoHeight; + } + }, ), ); }, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index fa0dc444..f3e0946b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -39,6 +39,7 @@ class PLVideoPlayer extends StatefulWidget { this.customWidget, this.customWidgets, this.showEposideCb, + this.fullScreenCb, super.key, }); @@ -52,6 +53,7 @@ class PLVideoPlayer extends StatefulWidget { final Widget? customWidget; final List? customWidgets; final Function? showEposideCb; + final Function? fullScreenCb; @override State createState() => _PLVideoPlayerState(); @@ -335,7 +337,10 @@ class _PLVideoPlayerState extends State color: Colors.white, ), ), - fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value), + fuc: () { + _.triggerFullScreen(status: !_.isFullScreen.value); + widget.fullScreenCb?.call(!_.isFullScreen.value); + }, ), }; final List list = []; From 5e63f0da7b93eb3e2d2b27ed0ba8b52d24518f50 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 16:03:45 +0800 Subject: [PATCH 149/438] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/msg.dart | 66 ++-- lib/pages/member/widgets/profile.dart | 12 +- lib/pages/whisper/controller.dart | 9 + lib/pages/whisper/view.dart | 23 +- lib/pages/whisper_detail/controller.dart | 37 +- lib/pages/whisper_detail/view.dart | 342 ++++++++++-------- .../whisper_detail/widget/chat_item.dart | 162 +++++---- 7 files changed, 373 insertions(+), 278 deletions(-) diff --git a/lib/http/msg.dart b/lib/http/msg.dart index d1d31958..7ac30bc1 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:math'; +import 'package:dio/dio.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -134,56 +136,42 @@ class MsgHttp { // 发送私信 static Future sendMsg({ - int? senderUid, - int? receiverId, + required int senderUid, + required int receiverId, int? receiverType, int? msgType, dynamic content, }) async { String csrf = await Request.getCsrf(); - Map params = await WbiSign().makSign({ - 'msg[sender_uid]': senderUid, - 'msg[receiver_id]': receiverId, - 'msg[receiver_type]': receiverType ?? 1, - 'msg[msg_type]': msgType ?? 1, - 'msg[msg_status]': 0, - 'msg[dev_id]': getDevId(), - 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, - 'msg[new_face_version]': 0, - 'msg[content]': content, - 'from_firework': 0, - 'build': 0, - 'mobi_app': 'web', - 'csrf_token': csrf, - 'csrf': csrf, - }); - var res = - await Request().post(Api.sendMsg, queryParameters: { - ...params, - 'csrf_token': csrf, - 'csrf': csrf, - }, data: { - 'w_sender_uid': params['msg[sender_uid]'], - 'w_receiver_id': params['msg[receiver_id]'], - 'w_dev_id': params['msg[dev_id]'], - 'w_rid': params['w_rid'], - 'wts': params['wts'], - 'csrf_token': csrf, - 'csrf': csrf, - }); + var res = await Request().post( + Api.sendMsg, + data: { + 'msg[sender_uid]': senderUid, + 'msg[receiver_id]': receiverId, + 'msg[receiver_type]': 1, + 'msg[msg_type]': 1, + 'msg[msg_status]': 0, + 'msg[content]': jsonEncode(content), + 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, + 'msg[new_face_version]': 0, + 'msg[dev_id]': getDevId(), + 'from_firework': 0, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); if (res.data['code'] == 0) { return { 'status': true, 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index a708a35e..8c6385db 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -208,7 +208,17 @@ class ProfilePanel extends StatelessWidget { const SizedBox(width: 8), Expanded( child: TextButton( - onPressed: () {}, + onPressed: () { + Get.toNamed( + '/whisperDetail', + parameters: { + 'name': memberInfo.name!, + 'face': memberInfo.face!, + 'mid': memberInfo.mid.toString(), + 'heroTag': ctr.heroTag!, + }, + ); + }, style: TextButton.styleFrom( backgroundColor: Theme.of(context) .colorScheme diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index c82cab5b..b7c52d2e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -62,4 +62,13 @@ class WhisperController extends GetxController { Future onRefresh() async { querySessionList('onRefresh'); } + + void refreshLastMsg(int talkerId, String content) { + final SessionList currentItem = + sessionList.where((p0) => p0.talkerId == talkerId).first; + currentItem.lastMsg!.content['content'] = content; + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.insert(0, currentItem); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa95463b..6d2f281b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -202,6 +202,7 @@ class SessionItem extends StatelessWidget { @override Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; return ListTile( onTap: () { @@ -214,6 +215,7 @@ class SessionItem extends StatelessWidget { 'name': sessionItem.accountInfo.name, 'face': sessionItem.accountInfo.face, 'mid': sessionItem.accountInfo.mid.toString(), + 'heroTag': heroTag, }, ); }, @@ -221,11 +223,14 @@ class SessionItem extends StatelessWidget { isLabelVisible: sessionItem.unreadCount > 0, label: Text(sessionItem.unreadCount.toString()), alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionItem.accountInfo.face, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), ), ), title: Text(sessionItem.accountInfo.name), @@ -245,10 +250,10 @@ class SessionItem extends StatelessWidget { .copyWith(color: Theme.of(context).colorScheme.outline)), trailing: Text( Utils.dateFormat(sessionItem.lastMsg.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith(color: Theme.of(context).colorScheme.outline), + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), ), ); } diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 6e950f81..3c7e0837 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,30 +1,39 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/session.dart'; +import 'package:pilipala/pages/whisper/index.dart'; import '../../utils/feed_back.dart'; import '../../utils/storage.dart'; class WhisperDetailController extends GetxController { - late int talkerId; + int? talkerId; late String name; late String face; late String mid; + late String heroTag; RxList messageList = [].obs; //表情转换图片规则 - List? eInfos; + RxList eInfos = [].obs; final TextEditingController replyContentController = TextEditingController(); Box userInfoCache = GStrorage.userInfo; + List emoteList = []; @override void onInit() { super.onInit(); - talkerId = int.parse(Get.parameters['talkerId']!); + if (Get.parameters.containsKey('talkerId')) { + talkerId = int.parse(Get.parameters['talkerId']!); + } else { + talkerId = int.parse(Get.parameters['mid']!); + } name = Get.parameters['name']!; face = Get.parameters['face']!; mid = Get.parameters['mid']!; + heroTag = Get.parameters['heroTag']!; } Future querySessionMsg() async { @@ -34,7 +43,7 @@ class WhisperDetailController extends GetxController { if (messageList.isNotEmpty) { ackSessionMsg(); if (res['data'].eInfos != null) { - eInfos = res['data'].eInfos; + eInfos.value = res['data'].eInfos; } } } else { @@ -73,7 +82,25 @@ class WhisperDetailController extends GetxController { msgType: 1, ); if (result['status']) { - SmartDialog.showToast('发送成功'); + String content = jsonDecode(result['data']['msg_content'])['content']; + messageList.insert( + 0, + MessageItem( + msgSeqno: result['data']['msg_key'], + senderUid: userInfo.mid, + receiverId: int.parse(mid), + content: {'content': content}, + msgType: 1, + timestamp: DateTime.now().millisecondsSinceEpoch, + ), + ); + eInfos.addAll(emoteList); + replyContentController.clear(); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.refreshLastMsg(talkerId!, message); + } catch (_) {} } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..7c5762d9 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -1,9 +1,12 @@ import 'dart:async'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/video/reply/emote.dart'; import 'package:pilipala/pages/emote/index.dart'; +import 'package:pilipala/pages/video/detail/reply_new/toolbar_icon_button.dart'; import 'package:pilipala/pages/whisper_detail/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; import '../../utils/storage.dart'; @@ -24,9 +27,9 @@ class _WhisperDetailPageState extends State late TextEditingController _replyContentController; final FocusNode replyContentFocusNode = FocusNode(); final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 - late double emoteHeight = 0.0; + late double emoteHeight = 230.0; double keyboardHeight = 0.0; // 键盘高度 - String toolbarType = 'input'; + RxString toolbarType = ''.obs; Box userInfoCache = GStrorage.userInfo; @override @@ -41,9 +44,7 @@ class _WhisperDetailPageState extends State _focuslistener() { replyContentFocusNode.addListener(() { if (replyContentFocusNode.hasFocus) { - setState(() { - toolbarType = 'input'; - }); + toolbarType.value = 'input'; } }); } @@ -52,7 +53,7 @@ class _WhisperDetailPageState extends State void didChangeMetrics() { super.didChangeMetrics(); final String routePath = Get.currentRoute; - if (mounted && routePath.startsWith('/whisper_detail')) { + if (mounted && routePath.startsWith('/whisperDetail')) { WidgetsBinding.instance.addPostFrameCallback((_) { // 键盘高度 final viewInsets = EdgeInsets.fromViewPadding( @@ -61,8 +62,11 @@ class _WhisperDetailPageState extends State if (mounted) { if (keyboardHeight == 0) { setState(() { - emoteHeight = keyboardHeight = + keyboardHeight = keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight; + if (keyboardHeight != 0) { + emoteHeight = keyboardHeight; + } }); } } @@ -79,6 +83,23 @@ class _WhisperDetailPageState extends State super.dispose(); } + void onChooseEmote(PackageItem package, Emote emote) { + _whisperDetailController.emoteList.add( + {'text': emote.text, 'url': emote.url}, + ); + final int cursorPosition = + max(_replyContentController.selection.baseOffset, 0); + final String currentText = _replyContentController.text; + final String newText = currentText.substring(0, cursorPosition) + + emote.text! + + currentText.substring(cursorPosition); + _replyContentController.value = TextEditingValue( + text: newText, + selection: + TextSelection.collapsed(offset: cursorPosition + emote.text!.length), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -88,30 +109,20 @@ class _WhisperDetailPageState extends State width: double.infinity, height: 50, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( width: 34, height: 34, child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { - return Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.6); - }), - ), onPressed: () => Get.back(), icon: Icon( - Icons.arrow_back_outlined, + Icons.arrow_back_ios, size: 18, color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), + const SizedBox(width: 10), GestureDetector( onTap: () { feedBack(); @@ -125,11 +136,14 @@ class _WhisperDetailPageState extends State }, child: Row( children: [ - NetworkImgLayer( - width: 34, - height: 34, - type: 'avatar', - src: _whisperDetailController.face, + Hero( + tag: _whisperDetailController.heroTag, + child: NetworkImgLayer( + width: 34, + height: 34, + type: 'avatar', + src: _whisperDetailController.face, + ), ), const SizedBox(width: 6), Text( @@ -143,155 +157,169 @@ class _WhisperDetailPageState extends State ], ), ), - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); - setState(() { - keyboardHeight = 0; - }); - }, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - final Map data = snapshot.data as Map; - if (data['status']) { - List messageList = _whisperDetailController.messageList; - return Obx( - () => messageList.isEmpty - ? const SizedBox() - : ListView.builder( - itemCount: messageList.length, - shrinkWrap: true, - reverse: true, - itemBuilder: (_, int i) { - if (i == 0) { - return Column( - children: [ - ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos), - const SizedBox(height: 12), - ], - ); - } else { - return ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos); - } - }, - ), - ); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ), - ), - // resizeToAvoidBottomInset: true, - bottomNavigationBar: Container( - width: double.infinity, - height: MediaQuery.of(context).padding.bottom + 70 + keyboardHeight, - padding: EdgeInsets.only( - left: 8, - right: 12, - top: 12, - bottom: MediaQuery.of(context).padding.bottom, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: 4, - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert_outlined, + size: 20, ), ), - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // IconButton( - // onPressed: () {}, - // icon: Icon( - // Icons.add_circle_outline, - // color: Theme.of(context).colorScheme.outline, - // ), - // ), - IconButton( - onPressed: () { - // if (toolbarType == 'input') { - // setState(() { - // toolbarType = 'emote'; - // }); - // } - // FocusScope.of(context).unfocus(); - }, - icon: Icon( - Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.outline, + const SizedBox(width: 14) + ], + ), + body: Column( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + toolbarType.value = ''; + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + final Map data = snapshot.data as Map; + if (data['status']) { + List messageList = _whisperDetailController.messageList; + return Obx( + () => messageList.isEmpty + ? const SizedBox() + : Align( + alignment: Alignment.topCenter, + child: ListView.builder( + itemCount: messageList.length, + shrinkWrap: true, + reverse: true, + itemBuilder: (_, int i) { + if (i == 0) { + return Column( + children: [ + ChatItem( + item: messageList[i], + e_infos: _whisperDetailController + .eInfos), + const SizedBox(height: 20), + ], + ); + } else { + return ChatItem( + item: messageList[i], + e_infos: + _whisperDetailController.eInfos); + } + }, + ), + ), + ); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + // 骨架屏 + return const SizedBox(); + } + }, + ), + ), + ), + Obx( + () => Container( + padding: EdgeInsets.fromLTRB( + 8, + 12, + 12, + toolbarType.value == '' + ? MediaQuery.of(context).padding.bottom + 6 + : 6, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: 1, + color: Colors.grey.withOpacity(0.15), ), ), - Expanded( - child: Container( - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.08), - borderRadius: BorderRadius.circular(40.0), - ), - child: TextField( - readOnly: true, - style: Theme.of(context).textTheme.titleMedium, - controller: _replyContentController, - autofocus: false, - focusNode: replyContentFocusNode, - decoration: const InputDecoration( - border: InputBorder.none, // 移除默认边框 - hintText: '开发中 ...', // 提示文本 - contentPadding: EdgeInsets.symmetric( - horizontal: 16.0, vertical: 12.0), // 内边距 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ToolbarIconButton( + onPressed: () { + if (toolbarType.value == '') { + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'input') { + FocusScope.of(context).unfocus(); + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'emote') { + FocusScope.of(context).requestFocus(); + } + }, + icon: const Icon(Icons.emoji_emotions_outlined, size: 22), + toolbarType: toolbarType.value, + selected: false, + ), + const SizedBox(width: 4), + Expanded( + child: Container( + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.05), + borderRadius: BorderRadius.circular(40.0), + ), + child: TextField( + style: Theme.of(context).textTheme.titleMedium, + controller: _replyContentController, + autofocus: false, + focusNode: replyContentFocusNode, + decoration: const InputDecoration( + border: InputBorder.none, // 移除默认边框 + hintText: '文明发言 ~', // 提示文本 + contentPadding: EdgeInsets.symmetric( + horizontal: 16.0, vertical: 12.0), // 内边距 + ), ), ), ), - ), - IconButton( - // onPressed: _whisperDetailController.sendMsg, - onPressed: null, - icon: Icon( - Icons.send, - color: Theme.of(context).colorScheme.outline, + IconButton( + onPressed: _whisperDetailController.sendMsg, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.outline, + ), ), - ), - // const SizedBox(width: 16), - ], + ], + ), ), - AnimatedSize( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 300), + ), + Obx( + () => AnimatedSize( + curve: Curves.linear, + duration: const Duration(milliseconds: 200), child: SizedBox( width: double.infinity, - height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + height: toolbarType.value == 'input' + ? keyboardHeight + : toolbarType.value == 'emote' + ? emoteHeight + : 0, child: EmotePanel( - onChoose: (package, emote) => {}, + onChoose: (package, emote) => onChooseEmote(package, emote), ), ), ), - ], - ), + ), + ], ), + resizeToAvoidBottomInset: false, ); } } diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 0d37e8b3..c0d87221 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,7 +1,6 @@ // ignore_for_file: must_be_immutable import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -69,9 +68,13 @@ class ChatItem extends StatelessWidget { Color textColor(BuildContext context) { return isOwner ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onSecondaryContainer; + : Theme.of(context).colorScheme.onBackground; } + const double safeDistanceval = 6; + const double borderRadiusVal = 12; + const double paddingVal = 10; + Widget richTextMessage(BuildContext context) { var text = content['content']; if (e_infos != null) { @@ -386,73 +389,98 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Row( - children: [ - if (!isOwner) const SizedBox(width: 12), - if (isOwner) const Spacer(), - Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 - ), - decoration: BoxDecoration( - color: isOwner - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.secondaryContainer, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(16), - topRight: const Radius.circular(16), - bottomLeft: Radius.circular(isOwner ? 16 : 6), - bottomRight: Radius.circular(isOwner ? 6 : 16), + : Padding( + padding: const EdgeInsets.only(top: 12), + child: Row( + mainAxisAlignment: !isOwner + ? MainAxisAlignment.start + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: safeDistanceval), + if (isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), ), + Container( + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: isOwner + ? Theme.of(context) + .colorScheme + .primary + .withAlpha(180) + : Theme.of(context) + .colorScheme + .outlineVariant + .withOpacity(0.6) + .withAlpha(125), + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(borderRadiusVal), + topRight: const Radius.circular(borderRadiusVal), + bottomLeft: + Radius.circular(isOwner ? borderRadiusVal : 2), + bottomRight: + Radius.circular(isOwner ? 2 : borderRadiusVal), + ), + ), + margin: const EdgeInsets.only( + left: 8, + right: 8, + ), + padding: const EdgeInsets.all(paddingVal), + child: messageContent(context), + // child: Column( + // crossAxisAlignment: isOwner + // ? CrossAxisAlignment.end + // : CrossAxisAlignment.start, + // children: [ + // messageContent(context), + // SizedBox(height: isPic ? 7 : 2), + // Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // Utils.dateFormat(item.timestamp), + // style: Theme.of(context) + // .textTheme + // .labelSmall! + // .copyWith( + // color: isOwner + // ? Theme.of(context) + // .colorScheme + // .onPrimary + // .withOpacity(0.8) + // : Theme.of(context) + // .colorScheme + // .onSecondaryContainer + // .withOpacity(0.8)), + // ), + // item.msgStatus == 1 + // ? Text( + // ' 已撤回', + // style: + // Theme.of(context).textTheme.labelSmall!, + // ) + // : const SizedBox() + // ], + // ) + // ], + // ), ), - margin: const EdgeInsets.only(top: 12), - padding: EdgeInsets.only( - top: 8, - bottom: 6, - left: isPic ? 8 : 12, - right: isPic ? 8 : 12, - ), - child: Column( - crossAxisAlignment: isOwner - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - messageContent(context), - SizedBox(height: isPic ? 7 : 2), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: isOwner - ? Theme.of(context) - .colorScheme - .onPrimary - .withOpacity(0.8) - : Theme.of(context) - .colorScheme - .onSecondaryContainer - .withOpacity(0.8)), - ), - item.msgStatus == 1 - ? Text( - ' 已撤回', - style: - Theme.of(context).textTheme.labelSmall!, - ) - : const SizedBox() - ], - ) - ], - ), - ), - if (!isOwner) const Spacer(), - if (isOwner) const SizedBox(width: 12), - ], + if (!isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: safeDistanceval), + ], + ), ); } } From 46cef5e55b41adc0c9448255afc79051c3c44622 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 17:03:52 +0800 Subject: [PATCH 150/438] =?UTF-8?q?feat:=20=E4=BC=9A=E8=AF=9D=E7=A7=BB?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++ lib/http/msg.dart | 31 +++++++++++---- lib/pages/whisper/controller.dart | 6 +++ lib/pages/whisper/view.dart | 18 +++++---- lib/pages/whisper_detail/controller.dart | 38 +++++++++++++++++++ lib/pages/whisper_detail/view.dart | 16 ++++---- .../whisper_detail/widget/chat_item.dart | 15 +++++++- 7 files changed, 105 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e519d91c..aaa804fa 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -535,4 +535,8 @@ class Api { /// 搜索结果计数 static const String searchCount = '/x/web-interface/wbi/search/all/v2'; + + /// 关闭会话 + static const String removeSession = + '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7ac30bc1..9cdc9160 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -124,13 +124,7 @@ class MsgHttp { 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } @@ -208,4 +202,27 @@ class MsgHttp { } return s.join(); } + + static Future removeSession({ + int? talkerId, + }) async { + String csrf = await Request.getCsrf(); + Map params = await WbiSign().makSign({ + 'talker_id': talkerId, + 'session_type': 1, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf + }); + var res = await Request().get(Api.removeSession, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index b7c52d2e..195b238b 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -71,4 +71,10 @@ class WhisperController extends GetxController { sessionList.insert(0, currentItem); sessionList.refresh(); } + + // 移除会话 + void removeSessionMsg(int talkerId) { + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 6d2f281b..bbe17048 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -204,6 +204,8 @@ class SessionItem extends StatelessWidget { Widget build(BuildContext context) { final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; + final msgStatus = sessionItem.lastMsg.msgStatus; + return ListTile( onTap: () { sessionItem.unreadCount = 0; @@ -235,13 +237,15 @@ class SessionItem extends StatelessWidget { ), title: Text(sessionItem.accountInfo.name), subtitle: Text( - content != null && content != '' - ? (content['text'] ?? - content['content'] ?? - content['title'] ?? - content['reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', + msgStatus == 1 + ? '你撤回了一条消息' + : content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context) diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 3c7e0837..32e0ceb0 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -105,4 +106,41 @@ class WhisperDetailController extends GetxController { SmartDialog.showToast(result['msg']); } } + + void removeSession(context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + clipBehavior: Clip.hardEdge, + title: const Text('提示'), + content: const Text('确认清空会话内容并移除会话?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await MsgHttp.removeSession(talkerId: talkerId); + if (res['status']) { + SmartDialog.showToast('操作成功'); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.removeSessionMsg(talkerId!); + Get.back(); + } catch (_) {} + } + }, + child: const Text('确认'), + ), + ], + ); + }, + ); + } } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 7c5762d9..912b5dc5 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -145,7 +145,7 @@ class _WhisperDetailPageState extends State src: _whisperDetailController.face, ), ), - const SizedBox(width: 6), + const SizedBox(width: 10), Text( _whisperDetailController.name, style: Theme.of(context).textTheme.titleMedium, @@ -158,12 +158,14 @@ class _WhisperDetailPageState extends State ), ), actions: [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert_outlined, - size: 20, - ), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined, size: 20), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: () => _whisperDetailController.removeSession(context), + child: const Text('关闭会话'), + ) + ], ), const SizedBox(width: 14) ], diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index c0d87221..f64cf223 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -389,8 +389,19 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Padding( - padding: const EdgeInsets.only(top: 12), + : Container( + padding: const EdgeInsets.only(top: 6, bottom: 6), + decoration: BoxDecoration( + border: Border( + left: item.msgStatus == 1 && !isOwner + ? BorderSide( + width: 4, color: Theme.of(context).dividerColor) + : BorderSide.none, + right: item.msgStatus == 1 && isOwner + ? BorderSide( + width: 4, color: Theme.of(context).primaryColor) + : BorderSide.none, + )), child: Row( mainAxisAlignment: !isOwner ? MainAxisAlignment.start From bfdd996b0833f13ad7b4de6b1ee11850ba8e4fbd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 20:34:28 +0800 Subject: [PATCH 151/438] =?UTF-8?q?feat:=20=E6=9C=AA=E8=AF=BB=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 12 ++ lib/pages/whisper/controller.dart | 45 +++++ lib/pages/whisper/view.dart | 269 +++++++++++++++--------------- 4 files changed, 194 insertions(+), 135 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index aaa804fa..198d6174 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -539,4 +539,7 @@ class Api { /// 关闭会话 static const String removeSession = '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; + + /// 消息未读数 + static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9cdc9160..9a6e878b 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -225,4 +225,16 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + static Future unread() async { + var res = await Request().get(Api.unread); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 195b238b..e00c990e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/account.dart'; @@ -7,6 +8,38 @@ class WhisperController extends GetxController { RxList sessionList = [].obs; RxList accountList = [].obs; bool isLoading = false; + RxList noticesList = [ + { + 'icon': Icons.message_outlined, + 'title': '回复我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.alternate_email, + 'title': '@ 我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.thumb_up_outlined, + 'title': '收到的赞', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.notifications_none_outlined, + 'title': '系统通知', + 'path': '', + 'count': 0, + } + ].obs; + + @override + void onInit() { + unread(); + super.onInit(); + } Future querySessionList(String? type) async { if (isLoading) return; @@ -77,4 +110,16 @@ class WhisperController extends GetxController { sessionList.removeWhere((p0) => p0.talkerId == talkerId); sessionList.refresh(); } + + // 消息未读数 + void unread() async { + var res = await MsgHttp.unread(); + if (res['status']) { + noticesList[0]['count'] = res['data']['reply']; + noticesList[1]['count'] = res['data']['at']; + noticesList[2]['count'] = res['data']['like']; + noticesList[3]['count'] = res['data']['sys_msg']; + noticesList.refresh(); + } + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index bbe17048..8070498e 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -44,147 +45,145 @@ class _WhisperPageState extends State { appBar: AppBar( title: const Text('消息'), ), - body: Column( - children: [ - // LayoutBuilder( - // builder: (BuildContext context, BoxConstraints constraints) { - // // 在这里根据父级容器的约束条件构建小部件树 - // return Padding( - // padding: const EdgeInsets.only(left: 20, right: 20), - // child: SizedBox( - // height: constraints.maxWidth / 5, - // child: GridView.count( - // primary: false, - // crossAxisCount: 4, - // padding: const EdgeInsets.all(0), - // childAspectRatio: 1.25, - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // SizedBox( - // width: 36, - // height: 36, - // child: IconButton( - // style: ButtonStyle( - // padding: - // MaterialStateProperty.all(EdgeInsets.zero), - // backgroundColor: - // MaterialStateProperty.resolveWith((states) { - // return Theme.of(context) - // .colorScheme - // .primary - // .withOpacity(0.1); - // }), - // ), - // onPressed: () {}, - // icon: Icon( - // Icons.message_outlined, - // size: 18, - // color: Theme.of(context).colorScheme.primary, - // ), - // ), - // ), - // const SizedBox(height: 6), - // const Text('回复我的', style: TextStyle(fontSize: 13)) - // ], - // ), - // ], - // ), - // ), - // ); - // }, - // ), - Expanded( - child: RefreshIndicator( - onRefresh: () async { - await _whisperController.onRefresh(); - }, - child: SingleChildScrollView( - controller: _scrollController, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return SessionItem( - sessionItem: sessionList[i], - changeFucCall: () => - sessionList.refresh(), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, + body: RefreshIndicator( + onRefresh: () async { + _whisperController.unread(); + await _whisperController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: Column( + children: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // 在这里根据父级容器的约束条件构建小部件树 + return Padding( + padding: const EdgeInsets.only(left: 20, right: 20), + child: SizedBox( + height: constraints.maxWidth / 4, + child: Obx( + () => GridView.count( + primary: false, + crossAxisCount: 4, + padding: const EdgeInsets.all(0), + children: [ + ..._whisperController.noticesList.map((element) { + return InkWell( + onTap: () => {}, + onLongPress: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Badge( + isLabelVisible: element['count'] > 0, + label: Text(element['count'] > 99 + ? '99+' + : element['count'].toString()), + child: Padding( + padding: const EdgeInsets.all(10), + child: Icon( + element['icon'], + size: 21, + color: Theme.of(context) + .colorScheme + .primary, + ), + ), + ), + const SizedBox(height: 4), + Text(element['title']) + ], ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } + ); + }).toList(), + ], + ), + ), + ), + ); + }, + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); } else { - // 骨架屏 - return ListView.builder( - itemCount: 15, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, int i) { - return Skeleton( - child: ListTile( - leading: Container( - width: 45, - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onInverseSurface, - borderRadius: BorderRadius.circular(25), - ), - ), - title: Container( - width: 100, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - subtitle: Container( - width: 80, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - ), - ); - }, + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), ); } - }, - ), + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), - ), + ], ), - ], + ), ), ); } From 569f7c23fd2e64072fe905dbd0d098b1ebc66ec9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 13:58:43 +0800 Subject: [PATCH 152/438] =?UTF-8?q?feat:=20=E5=9B=9E=E5=A4=8D=E6=88=91?= =?UTF-8?q?=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +++ lib/models/msg/reply.dart | 168 ++++++++++++++ lib/pages/message/reply/controller.dart | 25 +++ lib/pages/message/reply/index.dart | 4 + lib/pages/message/reply/view.dart | 285 ++++++++++++++++++++++++ lib/pages/whisper/controller.dart | 2 +- lib/pages/whisper/view.dart | 2 +- lib/router/app_pages.dart | 3 + 9 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 lib/models/msg/reply.dart create mode 100644 lib/pages/message/reply/controller.dart create mode 100644 lib/pages/message/reply/index.dart create mode 100644 lib/pages/message/reply/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 198d6174..4db5994d 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -542,4 +542,7 @@ class Api { /// 消息未读数 static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; + + /// 回复我的 + static const String messageReplyAPi = '/x/msgfeed/reply'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9a6e878b..20905386 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -237,4 +238,29 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + // 回复我的 + static Future messageReply({ + int? id, + int? replyTime, + }) async { + var params = { + if (id != null) 'id': id, + if (replyTime != null) 'reply_time': replyTime, + }; + var res = await Request().get(Api.messageReplyAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageReplyModel.fromJson(res.data['data']), + }; + } catch (err) { + print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/models/msg/reply.dart b/lib/models/msg/reply.dart new file mode 100644 index 00000000..be617088 --- /dev/null +++ b/lib/models/msg/reply.dart @@ -0,0 +1,168 @@ +class MessageReplyModel { + MessageReplyModel({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + MessageReplyModel.fromJson(Map json) { + cursor = Cursor.fromJson(json['cursor']); + items = json["items"] != null + ? json["items"].map((e) { + return MessageReplyItem.fromJson(e); + }).toList() + : []; + } +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + Cursor.fromJson(Map json) { + id = json['id']; + isEnd = json['is_end']; + time = json['time']; + } +} + +class MessageReplyItem { + MessageReplyItem({ + this.count, + this.id, + this.isMulti, + this.item, + this.replyTime, + this.user, + }); + + int? count; + int? id; + int? isMulti; + ReplyContentItem? item; + int? replyTime; + ReplyUser? user; + + MessageReplyItem.fromJson(Map json) { + count = json['count']; + id = json['id']; + isMulti = json['is_multi']; + item = ReplyContentItem.fromJson(json["item"]); + replyTime = json['reply_time']; + user = ReplyUser.fromJson(json['user']); + } +} + +class ReplyContentItem { + ReplyContentItem({ + this.subjectId, + this.rootId, + this.sourceId, + this.targetId, + this.type, + this.businessId, + this.business, + this.title, + this.desc, + this.image, + this.uri, + this.nativeUri, + this.detailTitle, + this.rootReplyContent, + this.sourceContent, + this.targetReplyContent, + this.atDetails, + this.topicDetails, + this.hideReplyButton, + this.hideLikeButton, + this.likeState, + this.danmu, + this.message, + }); + + int? subjectId; + int? rootId; + int? sourceId; + int? targetId; + String? type; + int? businessId; + String? business; + String? title; + String? desc; + String? image; + String? uri; + String? nativeUri; + String? detailTitle; + String? rootReplyContent; + String? sourceContent; + String? targetReplyContent; + List? atDetails; + List? topicDetails; + bool? hideReplyButton; + bool? hideLikeButton; + int? likeState; + String? danmu; + String? message; + + ReplyContentItem.fromJson(Map json) { + subjectId = json['subject_id']; + rootId = json['root_id']; + sourceId = json['source_id']; + targetId = json['target_id']; + type = json['type']; + businessId = json['business_id']; + business = json['business']; + title = json['title']; + desc = json['desc']; + image = json['image']; + uri = json['uri']; + nativeUri = json['native_uri']; + detailTitle = json['detail_title']; + rootReplyContent = json['root_reply_content']; + sourceContent = json['source_content']; + targetReplyContent = json['target_reply_content']; + atDetails = json['at_details']; + topicDetails = json['topic_details']; + hideReplyButton = json['hide_reply_button']; + hideLikeButton = json['hide_like_button']; + likeState = json['like_state']; + danmu = json['danmu']; + message = json['message']; + } +} + +class ReplyUser { + ReplyUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + ReplyUser.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } +} diff --git a/lib/pages/message/reply/controller.dart b/lib/pages/message/reply/controller.dart new file mode 100644 index 00000000..d62662df --- /dev/null +++ b/lib/pages/message/reply/controller.dart @@ -0,0 +1,25 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/reply.dart'; + +class MessageReplyController extends GetxController { + Cursor? cursor; + RxList replyItems = [].obs; + + Future queryMessageReply({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'replyTime': cursor!.time, + }; + var res = await MsgHttp.messageReply( + id: params['id'], replyTime: params['replyTime']); + if (res['status']) { + cursor = res['data'].cursor; + replyItems.addAll(res['data'].items); + } + return res; + } +} diff --git a/lib/pages/message/reply/index.dart b/lib/pages/message/reply/index.dart new file mode 100644 index 00000000..969d03dd --- /dev/null +++ b/lib/pages/message/reply/index.dart @@ -0,0 +1,4 @@ +library message_reply; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart new file mode 100644 index 00000000..c4c02f6c --- /dev/null +++ b/lib/pages/message/reply/view.dart @@ -0,0 +1,285 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/reply.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageReplyPage extends StatefulWidget { + const MessageReplyPage({super.key}); + + @override + State createState() => _MessageReplyPageState(); +} + +class _MessageReplyPageState extends State { + final MessageReplyController _messageReplyCtr = + Get.put(MessageReplyController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageReplyCtr.queryMessageReply(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageReplyCtr.queryMessageReply(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('回复我的'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageReplyCtr.queryMessageReply(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final replyItems = _messageReplyCtr.replyItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => + ReplyItem(item: replyItems[index]), + itemCount: replyItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageReplyCtr.queryMessageReply(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class ReplyItem extends StatelessWidget { + final MessageReplyItem item; + + const ReplyItem({super.key, required this.item}); + + @override + Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(item.user!.mid); + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + }, + child: Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Get.toNamed('/member?mid=${item.user!.mid}', + arguments: {'face': item.user!.avatar, 'heroTag': heroTag}); + }, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.user!.avatar, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: item.user!.nickname!), + const TextSpan(text: ' '), + if (item.item!.type! == 'video') + TextSpan( + text: '对我的视频发表了评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + if (item.item!.type! == 'reply') + TextSpan( + text: '回复了我的评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ])), + const SizedBox(height: 6), + Text.rich( + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle(letterSpacing: 0.3), + buildContent(context, item.item)), + if (item.item!.targetReplyContent != '') ...[ + const SizedBox(height: 2), + Text( + item.item!.targetReplyContent!, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + const SizedBox(height: 4), + Row( + children: [ + Text( + Utils.dateFormat(item.replyTime!, formatType: 'detail'), + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: 16), + Text( + '回复', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + ) + ], + ), + ), + // Spacer(), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 80, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.rootReplyContent!, + maxLines: 4, + style: const TextStyle( + fontSize: 12, + letterSpacing: 0.3, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + ); + } +} + +InlineSpan buildContent(BuildContext context, item) { + List? atDetails = item!.atDetails; + final List spanChilds = []; + if (atDetails!.isNotEmpty) { + final String patternStr = + atDetails.map((e) => '@${e['nickname']}').toList().join('|'); + final RegExp regExp = RegExp(patternStr); + item.sourceContent!.splitMapJoin( + regExp, + onMatch: (Match match) { + spanChilds.add( + TextSpan( + text: match.group(0), + recognizer: TapGestureRecognizer() + ..onTap = () { + var currentUser = atDetails + .where((e) => e['nickname'] == match.group(0)!.substring(1)) + .first; + Get.toNamed('/member?mid=${currentUser['mid']}', arguments: { + 'face': currentUser['avatar'], + }); + }, + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + ); + return ''; + }, + onNonMatch: (String nonMatch) { + spanChilds.add( + TextSpan(text: nonMatch), + ); + return ''; + }, + ); + } else { + spanChilds.add( + TextSpan(text: item.sourceContent), + ); + } + + return TextSpan(children: spanChilds); +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index e00c990e..f3cc47d6 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -12,7 +12,7 @@ class WhisperController extends GetxController { { 'icon': Icons.message_outlined, 'title': '回复我的', - 'path': '', + 'path': '/messageReply', 'count': 0, }, { diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 8070498e..fccdd844 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -69,7 +69,7 @@ class _WhisperPageState extends State { children: [ ..._whisperController.noticesList.map((element) { return InkWell( - onTap: () => {}, + onTap: () => Get.toNamed(element['path']), onLongPress: () {}, borderRadius: StyleString.mdRadius, child: Column( diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 2ca333f8..bb036c6e 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/reply/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -178,6 +179,8 @@ class Routes { // 操作菜单 CustomGetPage( name: '/actionMenuSet', page: () => const ActionMenuSetPage()), + // 回复我的 + CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), ]; } From 779f7e76662fbadc562bff5f13c3b5c0f4858386 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 17:53:46 +0800 Subject: [PATCH 153/438] =?UTF-8?q?feat:=20=E6=94=B6=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +- lib/models/msg/like.dart | 183 +++++++++++++ lib/pages/message/at/controller.dart | 3 + lib/pages/message/at/index.dart | 4 + lib/pages/message/at/view.dart | 19 ++ lib/pages/message/like/controller.dart | 30 +++ lib/pages/message/like/index.dart | 4 + lib/pages/message/like/view.dart | 319 +++++++++++++++++++++++ lib/pages/message/reply/view.dart | 27 +- lib/pages/message/system/controller.dart | 3 + lib/pages/message/system/index.dart | 4 + lib/pages/message/system/view.dart | 19 ++ lib/pages/whisper/controller.dart | 6 +- lib/router/app_pages.dart | 10 + 15 files changed, 636 insertions(+), 24 deletions(-) create mode 100644 lib/models/msg/like.dart create mode 100644 lib/pages/message/at/controller.dart create mode 100644 lib/pages/message/at/index.dart create mode 100644 lib/pages/message/at/view.dart create mode 100644 lib/pages/message/like/controller.dart create mode 100644 lib/pages/message/like/index.dart create mode 100644 lib/pages/message/like/view.dart create mode 100644 lib/pages/message/system/controller.dart create mode 100644 lib/pages/message/system/index.dart create mode 100644 lib/pages/message/system/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 4db5994d..f20b8bcf 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -545,4 +545,7 @@ class Api { /// 回复我的 static const String messageReplyAPi = '/x/msgfeed/reply'; + + /// 收到的赞 + static const String messageLikeAPi = '/x/msgfeed/like'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 20905386..7c168230 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; @@ -256,7 +257,30 @@ class MsgHttp { 'data': MessageReplyModel.fromJson(res.data['data']), }; } catch (err) { - print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } + + // 收到的赞 + static Future messageLike({ + int? id, + int? likeTime, + }) async { + var params = { + if (id != null) 'id': id, + if (likeTime != null) 'like_time': likeTime, + }; + var res = await Request().get(Api.messageLikeAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageLikeModel.fromJson(res.data['data']), + }; + } catch (err) { return {'status': false, 'date': [], 'msg': err.toString()}; } } else { diff --git a/lib/models/msg/like.dart b/lib/models/msg/like.dart new file mode 100644 index 00000000..b279131b --- /dev/null +++ b/lib/models/msg/like.dart @@ -0,0 +1,183 @@ +class MessageLikeModel { + MessageLikeModel({ + this.latest, + this.total, + }); + + Latest? latest; + Total? total; + + factory MessageLikeModel.fromJson(Map json) => + MessageLikeModel( + latest: json["latest"] == null ? null : Latest.fromJson(json["latest"]), + total: json["total"] == null ? null : Total.fromJson(json["total"]), + ); +} + +class Latest { + Latest({ + this.items, + this.lastViewAt, + }); + + List? items; + int? lastViewAt; + + factory Latest.fromJson(Map json) => Latest( + items: json["items"], + lastViewAt: json["last_view_at"], + ); +} + +class Total { + Total({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + factory Total.fromJson(Map json) => Total( + cursor: Cursor.fromJson(json['cursor']), + items: json["items"] == null + ? [] + : json["items"].map((e) { + return MessageLikeItem.fromJson(e); + }).toList(), + ); +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + factory Cursor.fromJson(Map json) => Cursor( + id: json['id'], + isEnd: json['is_end'], + time: json['time'], + ); +} + +class MessageLikeItem { + MessageLikeItem({ + this.id, + this.users, + this.item, + this.counts, + this.likeTime, + this.noticeState, + this.isExpand = false, + }); + + int? id; + List? users; + MessageLikeItemItem? item; + int? counts; + int? likeTime; + int? noticeState; + bool isExpand; + + factory MessageLikeItem.fromJson(Map json) => + MessageLikeItem( + id: json["id"], + users: json["users"] == null + ? [] + : json["users"].map((e) { + return MessageLikeUser.fromJson(e); + }).toList(), + item: json["item"] == null + ? null + : MessageLikeItemItem.fromJson(json["item"]), + counts: json["counts"], + likeTime: json["like_time"], + noticeState: json["notice_state"], + ); +} + +class MessageLikeUser { + MessageLikeUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + factory MessageLikeUser.fromJson(Map json) => + MessageLikeUser( + mid: json["mid"], + fans: json["fans"], + nickname: json["nickname"], + avatar: json["avatar"], + midLink: json["mid_link"], + follow: json["follow"], + ); +} + +class MessageLikeItemItem { + MessageLikeItemItem({ + this.itemId, + this.pid, + this.type, + this.business, + this.businessId, + this.replyBusinessId, + this.likeBusinessId, + this.title, + this.desc, + this.image, + this.uri, + this.detailName, + this.nativeUri, + this.ctime, + }); + + int? itemId; + int? pid; + String? type; + String? business; + int? businessId; + int? replyBusinessId; + int? likeBusinessId; + String? title; + String? desc; + String? image; + String? uri; + String? detailName; + String? nativeUri; + int? ctime; + + factory MessageLikeItemItem.fromJson(Map json) => + MessageLikeItemItem( + itemId: json["item_id"], + pid: json["pid"], + type: json["type"], + business: json["business"], + businessId: json["business_id"], + replyBusinessId: json["reply_business_id"], + likeBusinessId: json["like_business_id"], + title: json["title"], + desc: json["desc"], + image: json["image"], + uri: json["uri"], + detailName: json["detail_name"], + nativeUri: json["native_uri"], + ctime: json["ctime"], + ); +} diff --git a/lib/pages/message/at/controller.dart b/lib/pages/message/at/controller.dart new file mode 100644 index 00000000..af08987f --- /dev/null +++ b/lib/pages/message/at/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageAtController extends GetxController {} diff --git a/lib/pages/message/at/index.dart b/lib/pages/message/at/index.dart new file mode 100644 index 00000000..b2c573e6 --- /dev/null +++ b/lib/pages/message/at/index.dart @@ -0,0 +1,4 @@ +library message_at; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/at/view.dart b/lib/pages/message/at/view.dart new file mode 100644 index 00000000..9c48ec99 --- /dev/null +++ b/lib/pages/message/at/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageAtPage extends StatefulWidget { + const MessageAtPage({super.key}); + + @override + State createState() => _MessageAtPageState(); +} + +class _MessageAtPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('@我的'), + ), + ); + } +} diff --git a/lib/pages/message/like/controller.dart b/lib/pages/message/like/controller.dart new file mode 100644 index 00000000..9b09f89a --- /dev/null +++ b/lib/pages/message/like/controller.dart @@ -0,0 +1,30 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/like.dart'; + +class MessageLikeController extends GetxController { + Cursor? cursor; + RxList likeItems = [].obs; + + Future queryMessageLike({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'likeTime': cursor!.time, + }; + var res = await MsgHttp.messageLike( + id: params['id'], likeTime: params['likeTime']); + if (res['status']) { + cursor = res['data'].total.cursor; + likeItems.addAll(res['data'].total.items); + } + return res; + } + + Future expandedUsersAvatar(i) async { + likeItems[i].isExpand = !likeItems[i].isExpand; + likeItems.refresh(); + } +} diff --git a/lib/pages/message/like/index.dart b/lib/pages/message/like/index.dart new file mode 100644 index 00000000..4f9c143a --- /dev/null +++ b/lib/pages/message/like/index.dart @@ -0,0 +1,4 @@ +library message_like; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart new file mode 100644 index 00000000..e677fb25 --- /dev/null +++ b/lib/pages/message/like/view.dart @@ -0,0 +1,319 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/like.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageLikePage extends StatefulWidget { + const MessageLikePage({super.key}); + + @override + State createState() => _MessageLikePageState(); +} + +class _MessageLikePageState extends State { + final MessageLikeController _messageLikeCtr = + Get.put(MessageLikeController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageLikeCtr.queryMessageLike(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageLikeCtr.queryMessageLike(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('收到的赞'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageLikeCtr.queryMessageLike(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final likeItems = _messageLikeCtr.likeItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => LikeItem( + item: likeItems[index], + index: index, + messageLikeCtr: _messageLikeCtr, + ), + itemCount: likeItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageLikeCtr.queryMessageLike(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class LikeItem extends StatelessWidget { + final MessageLikeItem item; + final int index; + final MessageLikeController messageLikeCtr; + + const LikeItem( + {super.key, + required this.item, + required this.index, + required this.messageLikeCtr}); + + @override + Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; + final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); + int usersLen = item.users!.length > 3 ? 3 : item.users!.length; + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + try { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + } catch (_) { + SmartDialog.showToast('视频可能失效了'); + } + }, + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (usersLen == 1) { + final String heroTag = + Utils.makeHeroTag(item.users!.first.mid); + Get.toNamed('/member?mid=${item.users!.first.mid}', + arguments: { + 'face': item.users!.first.avatar, + 'heroTag': heroTag + }); + } else { + messageLikeCtr.expandedUsersAvatar(index); + } + }, + // 多个头像层叠 + child: SizedBox( + width: 50, + height: 50, + child: Stack( + children: [ + for (var i = 0; i < usersLen; i++) + Positioned( + top: i % 2 * (50 / (usersLen >= 2 ? 2 : 1)), + left: i / 2 * (50 / (usersLen >= 2 ? 2 : 1)), + child: NetworkImgLayer( + width: 50 / (usersLen >= 2 ? 2 : 1), + height: 50 / (usersLen >= 2 ? 2 : 1), + type: 'avatar', + src: item.users![i].avatar, + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: nickNameList.join('、')), + const TextSpan(text: ' '), + if (item.users!.length > 1) + TextSpan( + text: '等总计${item.users!.length}人', + style: TextStyle(color: outline), + ), + TextSpan( + text: '赞了我的评论', + style: TextStyle(color: outline), + ), + ])), + const SizedBox(height: 4), + Text( + Utils.dateFormat(item.likeTime!, formatType: 'detail'), + style: TextStyle(color: outline), + ), + ], + ), + ), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 60, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.title!, + maxLines: 4, + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + Positioned( + top: 0, + right: 0, + bottom: 0, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? Get.size.width - 74 : 0, + color: Theme.of(context).colorScheme.secondaryContainer, + child: ListView.builder( + itemCount: item.users!.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int i) { + return Padding( + padding: EdgeInsets.fromLTRB(i == 0 ? 12 : 4, 8, 4, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + final String heroTag = + Utils.makeHeroTag(item.users![i].mid); + Get.toNamed( + '/member?mid=${item.users![i].mid}', + arguments: { + 'face': item.users![i].avatar, + 'heroTag': heroTag + }, + ); + }, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.users![i].avatar, + ), + ), + const SizedBox(height: 6), + SizedBox( + width: 68, + child: Text( + textAlign: TextAlign.center, + item.users![i].nickname!, + maxLines: 1, + overflow: TextOverflow.clip, + style: TextStyle(color: outline), + ), + ), + ], + ), + ); + }, + ), + ), + ), + Positioned( + top: 0, + left: 0, + bottom: 0, + child: GestureDetector( + onTap: () { + messageLikeCtr.expandedUsersAvatar(index); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? 74 : 0, + color: Colors.black.withOpacity(0.3), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index c4c02f6c..881f8650 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -113,6 +113,7 @@ class ReplyItem extends StatelessWidget { @override Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; final String heroTag = Utils.makeHeroTag(item.user!.mid); final String bvid = item.item!.uri!.split('/').last; // 页码 @@ -167,15 +168,11 @@ class ReplyItem extends StatelessWidget { const TextSpan(text: ' '), if (item.item!.type! == 'video') TextSpan( - text: '对我的视频发表了评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + text: '对我的视频发表了评论', style: TextStyle(color: outline)), if (item.item!.type! == 'reply') TextSpan( text: '回复了我的评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ])), const SizedBox(height: 6), @@ -188,8 +185,7 @@ class ReplyItem extends StatelessWidget { const SizedBox(height: 2), Text( item.item!.targetReplyContent!, - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ], const SizedBox(height: 4), @@ -197,21 +193,15 @@ class ReplyItem extends StatelessWidget { children: [ Text( Utils.dateFormat(item.replyTime!, formatType: 'detail'), - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), const SizedBox(width: 16), - Text( - '回复', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + Text('回复', style: TextStyle(color: outline)), ], ) ], ), ), - // Spacer(), const SizedBox(width: 25), if (item.item!.type! == 'reply') Container( @@ -221,10 +211,7 @@ class ReplyItem extends StatelessWidget { child: Text( item.item!.rootReplyContent!, maxLines: 4, - style: const TextStyle( - fontSize: 12, - letterSpacing: 0.3, - ), + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), overflow: TextOverflow.ellipsis, ), ), diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart new file mode 100644 index 00000000..ad28af56 --- /dev/null +++ b/lib/pages/message/system/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageSystemController extends GetxController {} diff --git a/lib/pages/message/system/index.dart b/lib/pages/message/system/index.dart new file mode 100644 index 00000000..70b9fc6d --- /dev/null +++ b/lib/pages/message/system/index.dart @@ -0,0 +1,4 @@ +library message_system; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart new file mode 100644 index 00000000..da0f1219 --- /dev/null +++ b/lib/pages/message/system/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageSystemPage extends StatefulWidget { + const MessageSystemPage({super.key}); + + @override + State createState() => _MessageSystemPageState(); +} + +class _MessageSystemPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('系统通知'), + ), + ); + } +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index f3cc47d6..2614bf5a 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -18,19 +18,19 @@ class WhisperController extends GetxController { { 'icon': Icons.alternate_email, 'title': '@ 我的', - 'path': '', + 'path': '/messageAt', 'count': 0, }, { 'icon': Icons.thumb_up_outlined, 'title': '收到的赞', - 'path': '', + 'path': '/messageLike', 'count': 0, }, { 'icon': Icons.notifications_none_outlined, 'title': '系统通知', - 'path': '', + 'path': '/messageSystem', 'count': 0, } ].obs; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index bb036c6e..a6b48f0d 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,7 +4,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/at/index.dart'; +import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; +import 'package:pilipala/pages/message/system/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -181,6 +184,13 @@ class Routes { name: '/actionMenuSet', page: () => const ActionMenuSetPage()), // 回复我的 CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), + // @我的 + CustomGetPage(name: '/messageAt', page: () => const MessageAtPage()), + // 收到的赞 + CustomGetPage(name: '/messageLike', page: () => const MessageLikePage()), + // 系统通知 + CustomGetPage( + name: '/messageSystem', page: () => const MessageSystemPage()), ]; } From a2210f17f64b65008481d05503f8c8219d4c2dfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:24:31 +0800 Subject: [PATCH 154/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E9=87=8D=E5=A4=8D=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a83b7809..023e52be 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -196,9 +196,7 @@ class PiliSchame { parameters: {'url': redirectUrl, 'type': 'url', 'pageTitle': ''}, ); } - } - - if (path != null) { + } else if (path != null) { final String area = path.split('/').last; switch (area) { case 'bangumi': From 4591593bb56a36539700383404823355410244c5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:34:32 +0800 Subject: [PATCH 155/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 40267559..0f04455f 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -117,6 +117,7 @@ class _VideoReplyNewDialogState extends State final String newText = currentText.substring(0, cursorPosition) + emote.text! + currentText.substring(cursorPosition); + message.value = newText; _replyContentController.value = TextEditingValue( text: newText, selection: From 2503d5cbb4833ad19d63eb71919b097d10052026 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 19:09:05 +0800 Subject: [PATCH 156/438] =?UTF-8?q?opt:=20=E6=90=9C=E7=B4=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/http_error.dart | 38 +++++++++------ lib/http/search.dart | 16 +++++-- lib/models/search/result.dart | 10 ++-- lib/pages/search_panel/controller.dart | 4 +- .../search_panel/widgets/video_panel.dart | 47 ++++++++++++------- 5 files changed, 72 insertions(+), 43 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index cbc6659b..0381319e 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -2,12 +2,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; class HttpError extends StatelessWidget { - const HttpError( - {required this.errMsg, required this.fn, this.btnText, super.key}); + const HttpError({ + required this.errMsg, + required this.fn, + this.btnText, + this.isShowBtn = true, + super.key, + }); final String? errMsg; final Function()? fn; final String? btnText; + final bool isShowBtn; @override Widget build(BuildContext context) { @@ -29,20 +35,22 @@ class HttpError extends StatelessWidget { style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 20), - FilledButton.tonal( - onPressed: () { - fn!(); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { - return Theme.of(context).colorScheme.primary.withAlpha(20); - }), + if (isShowBtn) + FilledButton.tonal( + onPressed: () { + fn!(); + }, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + return Theme.of(context).colorScheme.primary.withAlpha(20); + }), + ), + child: Text( + btnText ?? '点击重试', + style: + TextStyle(color: Theme.of(context).colorScheme.primary), + ), ), - child: Text( - btnText ?? '点击重试', - style: TextStyle(color: Theme.of(context).colorScheme.primary), - ), - ), ], ), ), diff --git a/lib/http/search.dart b/lib/http/search.dart index 075defc7..403e6a37 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -88,7 +88,11 @@ class SearchHttp { if (tids != null && tids != -1) 'tids': tids, }; var res = await Request().get(Api.searchByType, data: reqData); - if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { + if (res.data['code'] == 0) { + if (res.data['data']['numPages'] == 0) { + // 我想返回数据,使得可以通过data.list 取值,结果为[] + return {'status': true, 'data': Data()}; + } Object data; try { switch (searchType) { @@ -125,9 +129,7 @@ class SearchHttp { return { 'status': false, 'data': [], - 'msg': res.data['data'] != null && res.data['data']['numPages'] == 0 - ? '没有相关数据' - : res.data['message'], + 'msg': res.data['message'], }; } } @@ -206,3 +208,9 @@ class SearchHttp { } } } + +class Data { + List list; + + Data({this.list = const []}); +} diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 81917b72..b903c873 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -5,10 +5,12 @@ class SearchVideoModel { SearchVideoModel({this.list}); List? list; SearchVideoModel.fromJson(Map json) { - list = json['result'] - .where((e) => e['available'] == true) - .map((e) => SearchVideoItemModel.fromJson(e)) - .toList(); + list = json['result'] == null + ? [] + : json['result'] + .where((e) => e['available'] == true) + .map((e) => SearchVideoItemModel.fromJson(e)) + .toList(); } } diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 35113198..dc0b2bac 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -30,9 +30,9 @@ class SearchPanelController extends GetxController { ); if (result['status']) { if (type == 'onRefresh') { - resultList.value = result['data'].list; + resultList.value = result['data'].list ?? []; } else { - resultList.addAll(result['data'].list); + resultList.addAll(result['data'].list ?? []); } page.value++; onPushDetail(keyword, resultList); diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 15745bde..f43e2eec 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/pages/search/widgets/search_text.dart'; @@ -25,25 +26,35 @@ class SearchVideoPanel extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(top: 36), - child: ListView.builder( - controller: ctr!.scrollController, - addAutomaticKeepAlives: false, - addRepaintBoundaries: false, - itemCount: list!.length, - itemBuilder: (context, index) { - var i = list![index]; - return Padding( - padding: index == 0 - ? const EdgeInsets.only(top: 2) - : EdgeInsets.zero, - child: VideoCardH( - videoItem: i, - showPubdate: true, - source: 'search', + child: list!.isNotEmpty + ? ListView.builder( + controller: ctr!.scrollController, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: list!.length, + itemBuilder: (context, index) { + var i = list![index]; + return Padding( + padding: index == 0 + ? const EdgeInsets.only(top: 2) + : EdgeInsets.zero, + child: VideoCardH( + videoItem: i, + showPubdate: true, + source: 'search', + ), + ); + }, + ) + : CustomScrollView( + slivers: [ + HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + ) + ], ), - ); - }, - ), ), // 分类筛选 Container( From 75525595c6258a9561d1b88845babb626aaf07ad Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 20:59:55 +0800 Subject: [PATCH 157/438] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5tab=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a25389bd..e485fe41 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -357,25 +357,29 @@ class CustomChip extends StatelessWidget { Widget build(BuildContext context) { final ColorScheme colorTheme = Theme.of(context).colorScheme; final Color secondaryContainer = colorTheme.secondaryContainer; + final Color onPrimary = colorTheme.onPrimary; + final Color primary = colorTheme.primary; final TextStyle chipTextStyle = selected - ? const TextStyle(fontWeight: FontWeight.bold, fontSize: 13) - : const TextStyle(fontSize: 13); - final ColorScheme colorScheme = Theme.of(context).colorScheme; + ? TextStyle(fontSize: 13, color: onPrimary) + : TextStyle(fontSize: 13, color: colorTheme.onSecondaryContainer); const VisualDensity visualDensity = VisualDensity(horizontal: -4.0, vertical: -2.0); return InputChip( - side: BorderSide( - color: selected - ? colorScheme.onSecondaryContainer.withOpacity(0.2) - : Colors.transparent, - ), + side: BorderSide.none, backgroundColor: secondaryContainer, - selectedColor: secondaryContainer, - color: MaterialStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), - padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), + color: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected) || + states.contains(MaterialState.hovered)) { + return primary; + } + return colorTheme.secondaryContainer; + }), + padding: const EdgeInsets.fromLTRB(6, 1, 6, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), selected: selected, showCheckmark: false, visualDensity: visualDensity, From b61d2305a93cbd12d910f83e7f7ed263fe938b47 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 17 Jun 2024 23:51:01 +0800 Subject: [PATCH 158/438] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 54b52e8e..3db43dd2 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -9,6 +9,8 @@ enum SubtitleType { zhHans, // 英文(美国) enUS, + // 中文繁体 + zhTW, } extension SubtitleTypeExtension on SubtitleType { @@ -24,6 +26,8 @@ extension SubtitleTypeExtension on SubtitleType { return '中文(简体)'; case SubtitleType.enUS: return '英文(美国)'; + case SubtitleType.zhTW: + return '中文(繁体)'; } } } @@ -41,6 +45,8 @@ extension SubtitleIdExtension on SubtitleType { return 'zh-Hans'; case SubtitleType.enUS: return 'en-US'; + case SubtitleType.zhTW: + return 'zh-TW'; } } } @@ -58,6 +64,8 @@ extension SubtitleCodeExtension on SubtitleType { return 4; case SubtitleType.enUS: return 5; + case SubtitleType.zhTW: + return 6; } } } From 35d2d92480be22636e80b091ba381f237df9ff0b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 18 Jun 2024 23:35:38 +0800 Subject: [PATCH 159/438] =?UTF-8?q?fix:=20=E5=88=86=E9=9B=86=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=AD=97=E5=B9=95=E6=9C=AA=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/controller.dart | 2 ++ lib/pages/video/detail/introduction/controller.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 65cd5dd8..208a85e4 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -225,6 +225,8 @@ class BangumiIntroController extends GetxController { videoDetailCtr.oid.value = aid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); + videoDetailCtr.getSubtitle(); + videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9c542f21..1a1b1b74 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -436,6 +436,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); videoDetailCtr.getSubtitle(); + videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From 54c38d8683acee6e6c81c75afaf0e4ec7573bfd6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 19 Jun 2024 23:17:36 +0800 Subject: [PATCH 160/438] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 24 ++++++ .../video/detail/widgets/header_control.dart | 75 ++++++++++--------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 3db43dd2..ac3ee3e0 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -11,6 +11,12 @@ enum SubtitleType { enUS, // 中文繁体 zhTW, + // + en, + // + pt, + // + es, } extension SubtitleTypeExtension on SubtitleType { @@ -28,6 +34,12 @@ extension SubtitleTypeExtension on SubtitleType { return '英文(美国)'; case SubtitleType.zhTW: return '中文(繁体)'; + case SubtitleType.en: + return '英文'; + case SubtitleType.pt: + return '葡萄牙语'; + case SubtitleType.es: + return '西班牙语'; } } } @@ -47,6 +59,12 @@ extension SubtitleIdExtension on SubtitleType { return 'en-US'; case SubtitleType.zhTW: return 'zh-TW'; + case SubtitleType.en: + return 'en'; + case SubtitleType.pt: + return 'pt'; + case SubtitleType.es: + return 'es'; } } } @@ -66,6 +84,12 @@ extension SubtitleCodeExtension on SubtitleType { return 5; case SubtitleType.zhTW: return 6; + case SubtitleType.en: + return 7; + case SubtitleType.pt: + return 8; + case SubtitleType.es: + return 9; } } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f51edff1..5072377a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -433,42 +433,47 @@ class _HeaderControlState extends State { return AlertDialog( title: const Text('选择字幕'), contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 18), - content: StatefulBuilder(builder: (context, StateSetter setState) { - return len == 0 - ? const SizedBox( - height: 60, - child: Center( - child: Text('没有字幕'), - ), - ) - : Column( - mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: -1, - title: const Text('关闭字幕'), - groupValue: tempThemeValue, - onChanged: (value) { - tempThemeValue = value!; - widget.controller?.toggleSubtitle(value); - Get.back(); - }, + content: StatefulBuilder( + builder: (context, StateSetter setState) { + return len == 0 + ? const SizedBox( + height: 60, + child: Center( + child: Text('没有字幕'), ), - ...widget.videoDetailCtr!.subtitles - .map((e) => RadioListTile( - value: e.code, - title: Text(e.title), - groupValue: tempThemeValue, - onChanged: (value) { - tempThemeValue = value!; - widget.controller?.toggleSubtitle(value); - Get.back(); - }, - )) - .toList(), - ], - ); - }), + ) + : SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + value: -1, + title: const Text('关闭字幕'), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller?.toggleSubtitle(value); + Get.back(); + }, + ), + ...widget.videoDetailCtr!.subtitles + .map((e) => RadioListTile( + value: e.code, + title: Text(e.title), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller + ?.toggleSubtitle(value); + Get.back(); + }, + )) + .toList(), + ], + ), + ); + }, + ), ); }); } From 56c063538964c5119c10572b8e606118a87ea31b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 19 Jun 2024 23:39:19 +0800 Subject: [PATCH 161/438] =?UTF-8?q?mod:=20scheme=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a83b7809..02c0084f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -19,6 +19,7 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { + print('getLatestScheme value: $value'); if (value != null) { _routePush(value); } @@ -26,6 +27,8 @@ class PiliSchame { /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { + print('registerSchemeListener event: $event'); + if (event != null) { _routePush(event); } @@ -92,6 +95,12 @@ class PiliSchame { 'id': 'cv$id', 'dynamicType': 'read' }); + } else if (host == 'pgc') { + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } } } if (scheme == 'https') { From 4de15ad6b3282b6b2e2354509e92bc6648d93dbe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 20 Jun 2024 00:06:45 +0800 Subject: [PATCH 162/438] =?UTF-8?q?opt:=20=20=5FroutePush=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 138 +++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 62 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 39a36cad..17d20bcd 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -1,5 +1,6 @@ import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/route_push.dart'; @@ -19,7 +20,6 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { - print('getLatestScheme value: $value'); if (value != null) { _routePush(value); } @@ -27,8 +27,6 @@ class PiliSchame { /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { - print('registerSchemeListener event: $event'); - if (event != null) { _routePush(event); } @@ -41,66 +39,82 @@ class PiliSchame { final String host = value.host; final String path = value.path; if (scheme == 'bilibili') { - if (host == 'root') { - Navigator.popUntil( - Get.context!, (Route route) => route.isFirst); - } else if (host == 'space') { - final String mid = path.split('/').last; - Get.toNamed( - '/member?mid=$mid', - arguments: {'face': null}, - ); - } else if (host == 'video') { - String pathQuery = path.split('/').last; - final numericRegex = RegExp(r'^[0-9]+$'); - if (numericRegex.hasMatch(pathQuery)) { - pathQuery = 'AV$pathQuery'; - } - Map map = IdUtils.matchAvorBv(input: pathQuery); - if (map.containsKey('AV')) { - _videoPush(map['AV'], null); - } else if (map.containsKey('BV')) { - _videoPush(null, map['BV']); - } else { - SmartDialog.showToast('投稿匹配失败'); - } - } else if (host == 'live') { - final String roomId = path.split('/').last; - Get.toNamed('/liveRoom?roomid=$roomId', - arguments: {'liveItem': null, 'heroTag': roomId}); - } else if (host == 'bangumi') { - if (path.startsWith('/season')) { - final String seasonId = path.split('/').last; - RoutePush.bangumiPush(int.parse(seasonId), null); - } - } else if (host == 'opus') { - if (path.startsWith('/detail')) { - var opusId = path.split('/').last; - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://www.bilibili.com/opus/$opusId', - 'type': 'url', - 'pageTitle': '', - }, + switch (host) { + case 'root': + Navigator.popUntil( + Get.context!, (Route route) => route.isFirst); + break; + case 'space': + final String mid = path.split('/').last; + Get.toNamed( + '/member?mid=$mid', + arguments: {'face': null}, ); - } - } else if (host == 'search') { - Get.toNamed('/searchResult', parameters: {'keyword': ''}); - } else if (host == 'article') { - final String id = path.split('/').last.split('?').first; - Get.toNamed('/htmlRender', parameters: { - 'url': 'https://www.bilibili.com/read/cv$id', - 'title': 'cv$id', - 'id': 'cv$id', - 'dynamicType': 'read' - }); - } else if (host == 'pgc') { - if (path.contains('ep')) { - final String lastPathSegment = path.split('/').last; - RoutePush.bangumiPush( - null, int.parse(lastPathSegment.split('?').first)); - } + break; + case 'video': + String pathQuery = path.split('/').last; + final numericRegex = RegExp(r'^[0-9]+$'); + if (numericRegex.hasMatch(pathQuery)) { + pathQuery = 'AV$pathQuery'; + } + Map map = IdUtils.matchAvorBv(input: pathQuery); + if (map.containsKey('AV')) { + _videoPush(map['AV'], null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV']); + } else { + SmartDialog.showToast('投稿匹配失败'); + } + break; + case 'live': + final String roomId = path.split('/').last; + Get.toNamed( + '/liveRoom?roomid=$roomId', + arguments: {'liveItem': null, 'heroTag': roomId}, + ); + break; + case 'bangumi': + if (path.startsWith('/season')) { + final String seasonId = path.split('/').last; + RoutePush.bangumiPush(int.parse(seasonId), null); + } + break; + case 'opus': + if (path.startsWith('/detail')) { + var opusId = path.split('/').last; + Get.toNamed( + '/webview', + parameters: { + 'url': 'https://www.bilibili.com/opus/$opusId', + 'type': 'url', + 'pageTitle': '', + }, + ); + } + break; + case 'search': + Get.toNamed('/searchResult', parameters: {'keyword': ''}); + break; + case 'article': + final String id = path.split('/').last.split('?').first; + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/cv$id', + 'title': 'cv$id', + 'id': 'cv$id', + 'dynamicType': 'read' + }); + break; + case 'pgc': + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } + break; + default: + SmartDialog.showToast('未匹配地址,请联系开发者'); + Clipboard.setData(ClipboardData(text: value.toJson().toString())); + break; } } if (scheme == 'https') { From 1db1d8f598ac4b7236f2abe8e9fc7eab3074602c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 20 Jun 2024 23:38:49 +0800 Subject: [PATCH 163/438] =?UTF-8?q?feat:=20=E7=82=B9=E5=87=BBtab=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E9=A1=B6=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 12 ++++++++++++ lib/pages/video/detail/reply/view.dart | 3 +++ lib/pages/video/detail/view.dart | 2 ++ 3 files changed, 17 insertions(+) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 38c62d7e..ea85a5b9 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -109,6 +109,7 @@ class VideoDetailController extends GetxController ].obs; RxDouble sheetHeight = 0.0.obs; RxString archiveSourceType = 'dash'.obs; + ScrollController? replyScrillController; @override void onInit() { @@ -551,4 +552,15 @@ class VideoDetailController extends GetxController cover.value = videoItem['pic'] = pic; } } + + void onControllerCreated(ScrollController controller) { + replyScrillController = controller; + } + + void onTapTabbar(int index) { + if (index == 1 && tabCtr.index == 1) { + replyScrillController?.animateTo(0, + duration: const Duration(milliseconds: 300), curve: Curves.ease); + } + } } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 653fe7e0..be1bd331 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -19,12 +19,14 @@ class VideoReplyPanel extends StatefulWidget { final int? oid; final int rpid; final String? replyLevel; + final Function(ScrollController)? onControllerCreated; const VideoReplyPanel({ this.bvid, this.oid, this.rpid = 0, this.replyLevel, + this.onControllerCreated, super.key, }); @@ -68,6 +70,7 @@ class _VideoReplyPanelState extends State _futureBuilderFuture = _videoReplyController.queryReplyList(); scrollController = ScrollController(); + widget.onControllerCreated?.call(scrollController); fabAnimationCtr.forward(); scrollListener(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 22271c2b..ea29bf78 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -387,6 +387,7 @@ class _VideoDetailPageState extends State dividerColor: Colors.transparent, tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + onTap: (index) => vdCtr.onTapTabbar(index), ), ), ), @@ -676,6 +677,7 @@ class _VideoDetailPageState extends State () => VideoReplyPanel( bvid: vdCtr.bvid, oid: vdCtr.oid.value, + onControllerCreated: vdCtr.onControllerCreated, ), ) ], From c5247b27c7b478bd15912d18521f1f13448a078b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 23 Jun 2024 23:12:04 +0800 Subject: [PATCH 164/438] =?UTF-8?q?mod:=20=E6=B6=88=E6=81=AF=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E6=B8=85=E9=9B=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fccdd844..e31e942e 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -69,7 +69,14 @@ class _WhisperPageState extends State { children: [ ..._whisperController.noticesList.map((element) { return InkWell( - onTap: () => Get.toNamed(element['path']), + onTap: () { + Get.toNamed(element['path']); + + if (element['count'] > 0) { + element['count'] = 0; + } + _whisperController.noticesList.refresh(); + }, onLongPress: () {}, borderRadius: StyleString.mdRadius, child: Column( From 4fa9bdc3804c4ca3f4bd2e33973b42d7969c532a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 23 Jun 2024 23:23:05 +0800 Subject: [PATCH 165/438] =?UTF-8?q?mod:=20chatItem=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 90 ++++++++----------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index f64cf223..77e38073 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,4 +1,5 @@ // ignore_for_file: must_be_immutable +// ignore_for_file: constant_identifier_names import 'dart:convert'; import 'package:flutter/material.dart'; @@ -8,7 +9,6 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; - import '../../../http/search.dart'; enum MsgType { @@ -409,12 +409,6 @@ class ChatItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(width: safeDistanceval), - if (isOwner) - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context).textTheme.labelSmall!.copyWith( - color: Theme.of(context).colorScheme.outline), - ), Container( constraints: const BoxConstraints( maxWidth: 300.0, // 设置最大宽度为200.0 @@ -444,51 +438,45 @@ class ChatItem extends StatelessWidget { right: 8, ), padding: const EdgeInsets.all(paddingVal), - child: messageContent(context), - // child: Column( - // crossAxisAlignment: isOwner - // ? CrossAxisAlignment.end - // : CrossAxisAlignment.start, - // children: [ - // messageContent(context), - // SizedBox(height: isPic ? 7 : 2), - // Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text( - // Utils.dateFormat(item.timestamp), - // style: Theme.of(context) - // .textTheme - // .labelSmall! - // .copyWith( - // color: isOwner - // ? Theme.of(context) - // .colorScheme - // .onPrimary - // .withOpacity(0.8) - // : Theme.of(context) - // .colorScheme - // .onSecondaryContainer - // .withOpacity(0.8)), - // ), - // item.msgStatus == 1 - // ? Text( - // ' 已撤回', - // style: - // Theme.of(context).textTheme.labelSmall!, - // ) - // : const SizedBox() - // ], - // ) - // ], - // ), - ), - if (!isOwner) - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context).textTheme.labelSmall!.copyWith( - color: Theme.of(context).colorScheme.outline), + child: Column( + crossAxisAlignment: isOwner + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + messageContent(context), + SizedBox(height: isPic ? 7 : 4), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith( + color: isOwner + ? Theme.of(context) + .colorScheme + .onPrimary + .withOpacity(0.8) + : Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.8)), + ), + item.msgStatus == 1 + ? Text( + ' 已撤回', + style: Theme.of(context) + .textTheme + .labelSmall!, + ) + : const SizedBox() + ], + ) + ], ), + ), const SizedBox(width: safeDistanceval), ], ), From abfb7a14391513474d30a7f19875b008c04b0931 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 24 Jun 2024 23:04:32 +0800 Subject: [PATCH 166/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA=20/?= =?UTF-8?q?=3F=20=E9=93=BE=E6=8E=A5=E8=B7=B3=E8=BD=AC=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 08e4d405..02d004e8 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -645,7 +645,7 @@ InlineSpan buildContent( '', ); } else { - Uri uri = Uri.parse(matchStr); + Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( scheme: uri.scheme, host: uri.host, From cc32224daf5c2304999d1dfe0952b6b41395c924 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 24 Jun 2024 23:43:37 +0800 Subject: [PATCH 167/438] =?UTF-8?q?feat:=20=E9=9F=B3=E9=A2=91=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E6=96=B9=E5=BC=8F=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/ao_output.dart | 6 ++++++ lib/pages/setting/play_setting.dart | 29 ++++++++++++++++++++++++++++ lib/plugin/pl_player/controller.dart | 9 ++++++++- lib/utils/storage.dart | 2 ++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/models/video/play/ao_output.dart diff --git a/lib/models/video/play/ao_output.dart b/lib/models/video/play/ao_output.dart new file mode 100644 index 00000000..170a78c5 --- /dev/null +++ b/lib/models/video/play/ao_output.dart @@ -0,0 +1,6 @@ +final List aoOutputList = [ + {'title': 'audiotrack,opensles', 'value': '0'}, + {'title': 'opensles,audiotrack', 'value': '1'}, + {'title': 'audiotrack', 'value': '2'}, + {'title': 'opensles', 'value': '3'}, +]; diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 4a8495e5..0f7dcdc3 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/video/play/ao_output.dart'; import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; @@ -28,6 +29,7 @@ class _PlaySettingState extends State { late dynamic defaultDecode; late int defaultFullScreenMode; late int defaultBtmProgressBehavior; + late String defaultAoOutput; @override void initState() { @@ -44,6 +46,8 @@ class _PlaySettingState extends State { defaultValue: FullScreenMode.values.first.code); defaultBtmProgressBehavior = setting.get(SettingBoxKey.btmProgressBehavior, defaultValue: BtmProgresBehavior.values.first.code); + defaultAoOutput = + setting.get(SettingBoxKey.defaultAoOutput, defaultValue: '0'); } @override @@ -263,6 +267,31 @@ class _PlaySettingState extends State { } }, ), + ListTile( + dense: false, + title: Text('音频输出方式', style: titleStyle), + subtitle: Text( + '当前输出方式 ${aoOutputList.firstWhere((element) => element['value'] == defaultAoOutput)['title']}', + style: subTitleStyle, + ), + onTap: () async { + String? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '音频输出方式', + value: defaultAoOutput, + values: aoOutputList, + ); + }, + ); + if (result != null) { + defaultAoOutput = result; + setting.put(SettingBoxKey.defaultAoOutput, result); + setState(() {}); + } + }, + ), ListTile( dense: false, title: Text('默认全屏方式', style: titleStyle), diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 2865a117..a614d75d 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -13,6 +13,7 @@ import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/video.dart'; +import 'package:pilipala/models/video/play/ao_output.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; @@ -453,7 +454,13 @@ class PlPlayerController { // 音量不一致 if (Platform.isAndroid) { await pp.setProperty("volume-max", "100"); - await pp.setProperty("ao", "audiotrack,opensles"); + String defaultAoOutput = + setting.get(SettingBoxKey.defaultAoOutput, defaultValue: '0'); + await pp.setProperty( + "ao", + aoOutputList + .where((e) => e['value'] == defaultAoOutput) + .first['title']); } await player.setAudioTrack( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 4a163446..bf9074e3 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -102,6 +102,8 @@ class SettingBoxKey { autoPiP = 'autoPiP', enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', enablePlayerControlAnimation = 'enablePlayerControlAnimation', + // 默认音频输出方式 + defaultAoOutput = 'defaultAoOutput', // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', From 7aa02be25197df858b32c5bfe7eb3fbc8f8dd1f8 Mon Sep 17 00:00:00 2001 From: guozhibin Date: Tue, 25 Jun 2024 23:51:32 +0800 Subject: [PATCH 168/438] mod --- assets/images/coin.png | Bin 7159 -> 1701 bytes lib/pages/message/reply/view.dart | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/images/coin.png b/assets/images/coin.png index bc2952a7a5cf519a331b83c0f268eef88769d471..afca87b286cdc1f677aef1cf5c80f5d4fc4d980e 100644 GIT binary patch literal 1701 zcmV;W23q-vP)JA z04>JA0Xnmvc>n+gS4l)cRCt{2n|p}WMHI(BdpC6}ZBf&-4K=OYinQEHi?X*;TU1Cz z_9C*9!U&_VEdMF!K}3J_pe1^1DN$`pN*ku;J0GpRU-Zz3-nwmB`;G(W!ms1}?(cVZ z=H9)pc(i}+b-Z+ zU_G!1xC595RPrCi!N5FV1JDk1_pr4BtALwbOBbg*G|sW7VkHKppUL!otY^EddVf z4a3XSn3f#Mi9@>q80j(3XkaezUXI214Y-sdVjtkS7-c$umw=iAEci)4Lym=b92h`> zF*q1LcoHZ`E`y--YgNOHdM)y)o+#FFkUDFo^c3&HITz83v_h$29`PWe9~9& z)efPJfs8*3_!`&_oEK=Pu5jpkMNb8eQU@0m_EErX|7Aq(_e!OGAveIMM(15>OWt_h z2olnBIN`i_i{TIb0?9rS_|xbvZ-~4w%|SSXqK3j4bA`#{>5zc(Z*&}m>1hXN^vxa} zygI-Z6=yd?iInTLC=0$Bm>FQMa@9F1e{))gJKYQvWsmh#Xt3i*^s=WS`|0X;=h;38Kx>y6nQtWx7^My=x-BPrD)EBDxK2H0!ZDuCaV%c(oO_ay zC$^2tHQ-h=V3Vf;$2**b%x;p5rm548I;u9a{SU$q_5pQfiZLgcF%m8I!2u>PE5|_oZ4aF7a6&%FXtX9ev4p{r zuov)mWL$fIYjKmY?Lt?lI~@@gAIP2igV|AO<9Ayl<37V54;kC;b#;2tbh<9U#4C-= z+ft4@LW8Z#0!5iFOGAw}nog$#nD`bWQ|Rv*6d%W>Io>-=C*kY95Xbtnk=w4vYqGv9 z3m(W+rx(4Be|2%^W^-PJ!G*vjIU>28nj74%r04PwQJk27NvoOnU+AxjWIMD4R%C7- z>9TJE1xTzzwgJ?lE=q^QjVEp^pFqZiYGvZQAQLo(PncGLddUWD6G`BP7WO!;D>W1H{_3!Avd% z4nC-JoM&NWaS&Skjv2GiQ-@hbo)lZeVxH+Nu{s}2anRtk`r5NN6nNXg2Tu3Y!7ixy zx}4>0QOeq-fkhuIav0U2i(98*v(+)AsNOSqI>CGZvF)&=L5)TUi4*vsQny`qo=w7O z`+Ls4(8!eJX(DPJU&}WrF(gGbS+p$=zyK$j>r~qLX$EFYNUB@Urf(i!1;I$*3&$i|ls)zq zsx`RbB2mGsMMuXh!cNV+6th$|qnWFg7~>1}>yj!5Ns3-I@El|L#{G6{ zv-yCewpBAtC53WjbZdI>E3h=JL(AN1OYvuOu6o_Of%iRsL)f9u=lMzHZKaGV-5K|5 vP(+T3@c-z-$lt%{Lq-xQ_W&0Z@=E&$KxEtUmHWF=00000NkvXXu0mjfN2xS6 literal 7159 zcmai3XEa<vI?2oo<*AX$uJ|9 zbE)c;5wTPqLBNLDXyI`F_$sdFW^)=nesPzJh7-d7Tk-$I#4N)}`MRL7DXk^Ce63JJ z$@|x~Dt)%t|F(sSB|<)Jwbk{iJW~8(_(!4PBksrHCfo7KN9jac37>Zv1N(QM&AyBK zr6Q{qIyv=nqi|5paxTn8tu9Ps`T@6=^q%B)@V@A&uz#M=&%bz?S5^*F<32*K3>Lq3 zVXCYAgH*bFr5u6k<-WPOe*5v6)S&0*WP^jaVU`j396F&bw*nD7yp@`KZ zf_Bfei3C%9{_H8dcljj8=IFOlR=nC(1>f%J2`k5Joj9#YKV0weokY&;Ba0H6&ZavJ z-`kJUtqiVj-R64NcxCU+tIcgOuD!4d!s@}{U5PNqxU%EJs~0&lomZ&`ck18=J=A); zb?~oe*?p-K*30XWEGdh}g@sRLjkpq!)vYGro@xZx9t*R_g`PET$k5@)GQTZ9J^Ip3 zhi=jp_$_R~_P|jrsiCnAy?(ms>v?kAYr%H%_Zc&~C#(w6&{!%z0lH5u2k{X5QrdV? z>xSUJRZ<+M1Tb_)0bVO6@f26cL}69`JOaG|*3rf&If+G(*Y-T_=f3;wHgy@fWS)E& zHluGHXednEwVc-1VNG-0^Lk9BHQQqGkTFM?w(8#C9bi$K>jVvL?rb^II?Ez=s94`P z|DpD2@$y-3KQj|!lYV#<|FExhgWWbZY_vAw`%md~ed)g9;CYj2=Oa~3!(Ri{A-X8v zpx$XkRN%?|H7(bPeTDT$U2Rn5Pt08M4%Dr=hUddQ3bO}vgt~_!e--cCO+GX`r6@mF z4)W?sy!%7r`bKCnc77>HZbRWXU4Qf!i{94oamm_O@#VKk*x?6i_IbSE{(qqL@pX>6g6tMuMySM63bXjKKQW`zugF43{dpMj=NYW(uw%8RJ+=#W zI?~pKSmdm9yVR-Bt?)~To7;!c|EzB$#BF(@wND79z8upagtno+y&?wL1Om$oC)Mkz?W>meIH|OtG$Eu09UW5x;^+{3Pb=)_j+O07=RCt<= zvDJ3?+-3010YS=Mw`{sCT}H(Vj8IsLVh$DT(sSUnyxOSFSSV_e;wcryI^ye~*LfKWB*0r%2$vcM19va@SGLQVv5rKjb7b-dKIVwZtba{LV?Nauw z4~NDSM}5AC{0d;OV4IsBZW5tZ;O)rno#Li7l~M2ob00O-f|9b_@h@czYKS^W*^iiF z8Esz>@pwWp6&sLaJH&b$=P@Q_@`O+0$9gxKdvw2I16+{7=P&FjN*)+bsfk0h^`^>Y$s z1Jl{Rze6gqdDR;=?7fNt1SYa`L%)B^8jU_a>8lF|`!Pxs@J@1uy!=`;zD(PFYkhQm zhqhbq(~@455`m}W;==ln7oTFf1hALu97-*24?!-b8%C-e2;|NQ_$GFB(7W9kU59@N z)6IT0UUMUwXIY{111C|1&)!n|fcE;n)ld*K>*r#LNCb zNDed2a>}ny*$^chgGqo-gJcIKPu7@TPaB?3sSNzuW`>pPiJa;N)ml43RU@x~Hp$PD<*cS3_SGnAH+P{(iOgzWiuelm zy;)VB^g6EwzBe=8r-$}bJ#h>aWW=$aC%E=(R+|mAJJx?}+2nS&yHE|}r}<_O$y7J4+{c!?hJv;GL$Qx-qLBx5v2?txK2h=+ z#IJ9zS)3%_ZJg0of3vwC)xJ?KRVM)R#<{Bf{4r!98A36E&b~=zN8rh9apQ~mfI4Q& zl9EWHY{TE+aQnRZ(ZYtnlilpbM`D~A4WP5JcJ8<{ngMj24hFJ2N!k0C=cAl0K_eTZ zt8|s*nLV>nHyCjHRFUN@+2|^Xsh8`ja9RhsWa#$(OkM0^OMr3Y;K>6dp&Ht75<@Zu z!i%bN`?*JQ>S830vo~#l&QomI(bRIh+cRbQtH7p$vyzgKBnH%0<3Uf&ad9=e!JCdq z3GoodNF`M%Vg$Llzj0}f4?8ZoS{E893BBNrRYj*bm2z1af$&Yz8ZC2_;QFcc`ApgfE@m_6@j3NSowGS>vcBjyf(feK zguOKg%X=Epv;?eJ+yLx6jm>F-Z|6@5{qjzVj^P>QWv%w?sg#7s6!o6mOL9(b0!`1eQFzt7 z{wCnv&gTR~k6phrGQZ$|4P~nvHPiM!DZ~n_A z4>-!;5yStQTKT3*2Kz~0@txrO*UwltY>P7MX&8gB&2Mv=Hi}hpSgS2nMz!YE`f5Ef z{To&zQPW?zohc;Mu)k6;!g$Kb=dAW1yDb~x4;J!$>T4B-g1L<;8z3Gtz~?ZVwo}`` z=ZR*?Qcv>#U{5ZNoB%Q-6uz2$(=haqpperZP%$u8q0qYzlW+dzeMb5Y7yMbAqHDPV z(B&ZfhXSTXKyp*q95nLy;jE|on>S!zy+fOTNawa$7Y>dMfN84C=(Sq_a%JpNs?7K` zirCMB2yi#NhlHr9!=s0!G-)VgFvhdPgvzLtSD?9(#7 znZYH~qFt1M0SiOU-+rLWm4ie#d<9N|E^=bIWoiab4f_7G_PYrz?5(ra+nNWJai!Kc z3aAA~oIkx_?jG~w{0cA(e4zp1eZV@bu|{J=jG4VOfemZCpr|~y(d)%EkFFp$ZlJ*z zoLsvDr!_gZTSohkQ^p57p>ok9&S$YJ&5y|noJ*tq%AeBFvt6sx#r{cN`tjxMZ*B}V z{_5IGl{Fr$+g(bV$EEZyw^fc$uNf-4KfC9~T(opPy!jgNsMDM6eRCdt$MBo+Lwm(6 ziUmXZY%ch<kP$X5Z#1YG)4EL>;v@HohmWj$TvEO`G}z} zYVkwBeo&MYQ2CQ&A=sgbW-$xE#U&$-Z@>PagwI9ia<@<66W-Ac(9Vwddl_R5h!CBc zG3K{kL(FVG?M8#RxCzhOZWWJJ04VgU`hks8Ysy(&VTKk<4&!z~2Y_BQSX~)E1!xDw zjZhS5iqhhNJa9`oI76`NxlhZBOVLv^26!eH;Xb15DkKN2{*^+x6nt@)5w3)i&RHwt zQboiRTkG>>88hLm&*R^zDW)<%LjjT^#IZmUj4A#$wo7v>s@Dt)x+uDf{hLTspnLvV6%+(ehY}w; zmDXjAH9@v_To}8w#qmpTPoIYf&1}4u>wMgSLK%m!^H6Hvqp>?#&;ipCmB69~I= zP@-4DjsyVY4qWP|N}u$C6Pa zyr3P!P383j*$5`MBdZ8j*h!B!{YKGMTkJI?Nr;+j0dNJVw(v2(QWOtGChlmb^a6lg zC*Vy`187H|qcuIw&&>zKib3?2@B4d=I3f|tE=TS#Lb6{FoXnnV8(!p+{|LB5$;Az{ z2A+ikYYI^lb@B4v0??(6Z)6L!Nz#a(@Wm~_d`BeR`P(5|EYG6-v%-`dpfFoZx|R)= zhuTCF&VuQe4f!WSUSJRO1L?LT@x~wn`^b_8B1pf-g^J8Y=6W3qP?Ix+MF#y5GYXVs zzKgYC?AM?$J5&V5U}?^{KJMlrn%Mf;XlVM-kfb*+_j1mf#LdW(1kI9glLva(V}Ras zF{)ERm`uhjH$m8G=je1&Wr!^4y>g`>nKHEF2GZ3)d-^%Q6Cjra<`%`BR@x6yDmBy{ zHxkQrZYkWy{uxyWN-Ck&iKNU~( zWp)o*2#?mOWTYj1%yvQZY>#qXKaek|QheO(^CysR0;xjTK}l3c){!iQu@FM}trG|C zqY5YrCR2^jqeeTmTMjzX|*d>wip!(kJ z!p}ZGWEgbn^dT6EXIQ{dF&V4f$)GFc1>o6F*#N?qI-mq9NVB>2W%IJvD((_Ov>zC; zu{ehOwMC3t{exWrr>uB6A3XckT-Ko<(By@ar-Nz~l#rN2z7VMueFfhr{wL{>qc8VT za4)%7;@3;z(18rs?VL0QEcpD3zelnAO%41*Jxptp02w{oQt!yIhddsb>ZGuGBcG;& zCe_KaJv4fb1KcqwV3SW|970g^gl~qV3WF7Ipei19>cs%90ZzXU1|?=Vpp*nBPwXgP8yQTOq*RrTP3P>%daQQbea)PLeks}Xrr+6LA*Ka(u5c}R5qmD52lqO8pTp!r6U29u7)!;n}G=UUpKi0-g@d0#GkcZcR&@3mbP6tml39i)NU^5%gvTlEWZ)^j?U!1zv=LD z!cwU^j(5y)=1!4??g7o-RxJ@awi=BZ4I`dZMR1?u?4Yfu((sKL{p)@z`uM8nN^5c^K%u4`D8*BuHq@?_UtvzAyTb`C1qB-=ha`Nx9t3_oxpo6^h1 zLM2H4wb#om6&YhU*B0U$ZCn{SJPNu9LdmLBv_B-54aox;Io4ZZr9HA#Llq594)q}s z+cQB$`Ze5Z{@TNP4~)m;&mmcs2b5Y(vE&1i%W@XGUhgHH_fpNnD!&_Ng!Rvttj~Ri zNGcPl#OH#pRBRk(9sH(aeY|t8Fn{1(j_%`RG1ESs!f+A&cf!ONSwps)>!a zVZ*@Vw=oUde@rIMA9m=!>^7h6(R}C;9$^*&KBeOG^$(0(%Gjin-dU9vwE021SmV3A z3pQF;IZ#Oc!*hWuKZ1EpRWFD{=m*1t#}vz7#veSv>qt)Ccay~l3?j}M_W^Sb> zanl})m_^+3yBa@tnNLoh!B$qRt9@J-uDM^O)Yw_e5fK!-#Hhf<4~$C1tYDZ+VR*j2 z{AXn_JknafKU@GSfFq;bO$J00#NCOj=YfX{vw@|#%eclfp=l9mVYiTjtw?C3fz@>` z64MaIIvWz}K}O^ZXYc3Z0QPjEN28^=OFvS~3bHRx_2$Opi5)U+-2V1_n7e#)<_Bm& zPx&(X!U8W>knt#XH`GULXw_N{>k=#HTX{r7$qnCfh zx#>2(>CSNv;(8i+@0HuB6L)}((vj_K;ACWkoW_Gkca-~j4<3I0x4&E;JFSuX-cPN1 zPTh(Ela&}+8UF9VvIY8T5A@WKUU-)00i*sUX0gY_>-?{$-IJLMSGW$I2ivT{Oio1U z9K+mo%PCIl%UPt$X72CNc{zD~9%J>anzm~xLt4ovwS|KGIL&hCdKI7CUGl6pHbbD{R_;~c|h%se}} zrD8#cvGbkY-XL9k?27}j;imls*~h=d@@E$;iY=ulsFvNxE(CR+#E=Z4$~mA#@or`07naUm?|czbR&_QxkoW|i^+tLG?WF2;EYGN+Xu zq{gQryB(ta#Js$)LG(M@QD^CaMY$*DJ?*4ZBwR+z1Jj!7e9TGb-_M0z8I+;Gv^lR? z)8teeQF2KBc46zCd*)gEhIaC%ql<(yDSIs+^?_Cn;Kf;qd{u&}$2A5?Bs==_6Q>lD z{wGM17^~LvGEx5({$?LdGm?b%>LS}?NmC1#2Asc6Xn(J@63(7#_mS#17$zM&d#6xK zRZYf(wPmrB);c{Isu~g(iD{%>353^R*Ho(LUkQ;?-yx zwd~$Igjqu3uA7dwQG^J?w=jk!&+A2roChWuZy(m~Q(KKkC8DPWUb}`eKP5Kkiu(Ac ztUt)act5wB=+d~|3OuN7HtL$%QB0$rT6|^H@07(_lJNDBf7^$r+dNc$%~nDcwuQAx zsqVpgo+C)J1?jgwX)jscx&ElNw3f?%S@~`1<8Xo56>eiau0M1B+ZQO?|Gpn1&hT(C l{=<}~mvg`V-P`>?i}iJ^lzFK-(e&S({%sST25l$Qe*pGzPR;-T diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index 881f8650..9776c7f7 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -196,7 +196,7 @@ class ReplyItem extends StatelessWidget { style: TextStyle(color: outline), ), const SizedBox(width: 16), - Text('回复', style: TextStyle(color: outline)), + // Text('回复', style: TextStyle(color: outline)), ], ) ], From 0031b229021429318174eed48303312837c89499 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 26 Jun 2024 22:18:49 +0800 Subject: [PATCH 169/438] =?UTF-8?q?mod:=20=E6=9C=AA=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=85=B3=E9=97=AD=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/controller.dart | 2 +- lib/pages/whisper/view.dart | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 2614bf5a..749a3482 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -17,7 +17,7 @@ class WhisperController extends GetxController { }, { 'icon': Icons.alternate_email, - 'title': '@ 我的', + 'title': '@我的', 'path': '/messageAt', 'count': 0, }, diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index e31e942e..9436e2be 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,5 +1,6 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/skeleton.dart'; @@ -70,6 +71,11 @@ class _WhisperPageState extends State { ..._whisperController.noticesList.map((element) { return InkWell( onTap: () { + if (['/messageAt', '/messageSystem'] + .contains(element['path'])) { + SmartDialog.showToast('功能开发中'); + return; + } Get.toNamed(element['path']); if (element['count'] > 0) { From dbfc31a1dff498d11d4e76f8fc3ec1f5f88ea728 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 26 Jun 2024 22:29:11 +0800 Subject: [PATCH 170/438] =?UTF-8?q?v1.0.24=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.24.0626.md | 23 +++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.24.0626.md diff --git a/change_log/1.0.24.0626.md b/change_log/1.0.24.0626.md new file mode 100644 index 00000000..d9a8892f --- /dev/null +++ b/change_log/1.0.24.0626.md @@ -0,0 +1,23 @@ +## 1.0.24 + +### 功能 ++ 私信功能 ++ 回复我的、收到的赞查看 ++ 新的登录方式 ++ 全屏选集 ++ 一键三连 ++ 按分区搜索 + +### 优化 ++ 页面跳转动画 ++ 评论区跳转 + +### 修复 ++ 音画不同步问题 ++ 分集字幕未同步 ++ 多语言字幕 ++ 弹幕设置未生效 ++ + + +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 66187abf..09169473 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.23+1023 +version: 1.0.24+1024 environment: sdk: ">=3.0.0 <4.0.0" From 7301673783dfde6c6ef6114a77ff57cc4db8e782 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 28 Jun 2024 23:24:42 +0800 Subject: [PATCH 171/438] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=8C=BA=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 21 +++++++++++-------- .../introduction/widgets/action_item.dart | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 93cc26c9..1e8d97f1 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -629,11 +630,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { child: Icon( key: ValueKey(likeStatus), likeStatus - ? Icons.thumb_up - : Icons.thumb_up_alt_outlined, + ? FontAwesomeIcons.solidThumbsUp + : FontAwesomeIcons.thumbsUp, color: likeStatus ? colorScheme.primary : colorScheme.outline, + size: 21, ), ), const SizedBox(height: 6), @@ -663,7 +665,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { (const IconThemeData.fallback().size! + 5) / 2, child: progressWidget(_progress)), ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, text: widget.videoDetail!.stat!.coin!.toString(), @@ -681,8 +684,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { (const IconThemeData.fallback().size! + 5) / 2, child: progressWidget(_progress)), ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), onTap: () => showFavBottomSheet(), onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, @@ -692,7 +695,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), 'watchLater': ActionItem( - icon: const Icon(Icons.watch_later_outlined), + icon: const Icon(FontAwesomeIcons.clock), onTap: () async { final res = await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); @@ -702,15 +705,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { text: '稍后看', ), 'share': ActionItem( - icon: const Icon(Icons.share), + icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, text: '分享', ), 'dislike': Obx( () => ActionItem( - icon: const Icon(Icons.thumb_down_alt_outlined), - selectIcon: const Icon(Icons.thumb_down), + icon: const Icon(FontAwesomeIcons.thumbsDown), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), onTap: () {}, selectStatus: videoIntroController.hasDisLike.value, text: '不喜欢', diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 3288b2fa..fd0b9fef 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -51,6 +51,7 @@ class ActionItem extends StatelessWidget { color: selectStatus ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, + size: 20, ) : Image.asset( key: ValueKey(selectStatus), From 6a28ccbf64f1f56382209ddfb84bf41ed8e49b52 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 28 Jun 2024 23:44:48 +0800 Subject: [PATCH 172/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 02d004e8..ebb266cd 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -700,9 +700,10 @@ InlineSpan buildContent( ); // 只显示一次 matchedStrs.add(matchStr); - } else if (content - .topicsMeta[matchStr.substring(1, matchStr.length - 1)] != - null) { + } else if (content.topicsMeta.keys.isNotEmpty && + matchStr.length > 1 && + content.topicsMeta[matchStr.substring(1, matchStr.length - 1)] != + null) { spanChilds.add( TextSpan( text: matchStr, From a63a9a28d7df50d38a359373fc9dcd2a680a4e3f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 13:53:43 +0800 Subject: [PATCH 173/438] =?UTF-8?q?feat:=20=E6=9C=80=E8=BF=91=E7=82=B9?= =?UTF-8?q?=E8=B5=9E=E7=9A=84=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 5 +- lib/models/member/like.dart | 210 ++++++++++++++++++++++++ lib/pages/member/controller.dart | 10 ++ lib/pages/member/view.dart | 62 +++++-- lib/pages/member/widgets/conis.dart | 8 +- lib/pages/member/widgets/like.dart | 31 ++++ lib/pages/member_like/widgets/item.dart | 96 +++++++++++ 7 files changed, 400 insertions(+), 22 deletions(-) create mode 100644 lib/models/member/like.dart create mode 100644 lib/pages/member/widgets/like.dart create mode 100644 lib/pages/member_like/widgets/item.dart diff --git a/lib/http/member.dart b/lib/http/member.dart index 1af0f9a4..20a2c728 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -1,5 +1,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/member/like.dart'; import '../common/constants.dart'; import '../models/dynamics/result.dart'; import '../models/follow/result.dart'; @@ -328,7 +329,9 @@ class MemberHttp { if (res.data['code'] == 0) { return { 'status': true, - 'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists']) + 'data': res.data['data']['list'] + .map((e) => MemberLikeDataModel.fromJson(e)) + .toList(), }; } else { return { diff --git a/lib/models/member/like.dart b/lib/models/member/like.dart new file mode 100644 index 00000000..df71e2ec --- /dev/null +++ b/lib/models/member/like.dart @@ -0,0 +1,210 @@ +class MemberLikeDataModel { + MemberLikeDataModel({ + this.aid, + this.videos, + this.tid, + this.tname, + this.pic, + this.title, + this.pubdate, + this.ctime, + this.desc, + this.state, + this.duration, + this.redirectUrl, + this.rights, + this.owner, + this.stat, + this.dimension, + this.cover43, + this.bvid, + this.interVideo, + this.resourceType, + this.subtitle, + this.enableVt, + }); + + final int? aid; + final int? videos; + final int? tid; + final String? tname; + final String? pic; + final String? title; + final int? pubdate; + final int? ctime; + final String? desc; + final int? state; + final int? duration; + final String? redirectUrl; + final Rights? rights; + final Owner? owner; + final Stat? stat; + final Dimension? dimension; + final String? cover43; + final String? bvid; + final bool? interVideo; + final String? resourceType; + final String? subtitle; + final int? enableVt; + + factory MemberLikeDataModel.fromJson(Map json) => + MemberLikeDataModel( + aid: json["aid"], + videos: json["videos"], + tid: json["tid"], + tname: json["tname"], + pic: json["pic"], + title: json["title"], + pubdate: json["pubdate"], + ctime: json["ctime"], + desc: json["desc"], + state: json["state"], + duration: json["duration"], + redirectUrl: json["redirect_url"], + rights: Rights.fromJson(json["rights"]), + owner: Owner.fromJson(json["owner"]), + stat: Stat.fromJson(json["stat"]), + dimension: Dimension.fromJson(json["dimension"]), + cover43: json["cover43"], + bvid: json["bvid"], + interVideo: json["inter_video"], + resourceType: json["resource_type"], + subtitle: json["subtitle"], + enableVt: json["enable_vt"], + ); +} + +class Dimension { + Dimension({ + required this.width, + required this.height, + required this.rotate, + }); + + final int width; + final int height; + final int rotate; + + factory Dimension.fromJson(Map json) => Dimension( + width: json["width"], + height: json["height"], + rotate: json["rotate"], + ); +} + +class Owner { + Owner({ + required this.mid, + required this.name, + required this.face, + }); + + final int mid; + final String name; + final String face; + + factory Owner.fromJson(Map json) => Owner( + mid: json["mid"], + name: json["name"], + face: json["face"], + ); +} + +class Rights { + Rights({ + required this.bp, + required this.elec, + required this.download, + required this.movie, + required this.pay, + required this.hd5, + required this.noReprint, + required this.autoplay, + required this.ugcPay, + required this.isCooperation, + required this.ugcPayPreview, + required this.noBackground, + required this.arcPay, + required this.payFreeWatch, + }); + + final int bp; + final int elec; + final int download; + final int movie; + final int pay; + final int hd5; + final int noReprint; + final int autoplay; + final int ugcPay; + final int isCooperation; + final int ugcPayPreview; + final int noBackground; + final int arcPay; + final int payFreeWatch; + + factory Rights.fromJson(Map json) => Rights( + bp: json["bp"], + elec: json["elec"], + download: json["download"], + movie: json["movie"], + pay: json["pay"], + hd5: json["hd5"], + noReprint: json["no_reprint"], + autoplay: json["autoplay"], + ugcPay: json["ugc_pay"], + isCooperation: json["is_cooperation"], + ugcPayPreview: json["ugc_pay_preview"], + noBackground: json["no_background"], + arcPay: json["arc_pay"], + payFreeWatch: json["pay_free_watch"], + ); +} + +class Stat { + Stat({ + required this.aid, + required this.view, + required this.danmaku, + required this.reply, + required this.favorite, + required this.coin, + required this.share, + required this.nowRank, + required this.hisRank, + required this.like, + required this.dislike, + required this.vt, + required this.vv, + }); + + final int aid; + final int view; + final int danmaku; + final int reply; + final int favorite; + final int coin; + final int share; + final int nowRank; + final int hisRank; + final int like; + final int dislike; + final int vt; + final int vv; + + factory Stat.fromJson(Map json) => Stat( + aid: json["aid"], + view: json["view"], + danmaku: json["danmaku"], + reply: json["reply"], + favorite: json["favorite"], + coin: json["coin"], + share: json["share"], + nowRank: json["now_rank"], + hisRank: json["his_rank"], + like: json["like"], + dislike: json["dislike"], + vt: json["vt"], + vv: json["vv"], + ); +} diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 0aa7166f..7db046d4 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -8,6 +8,7 @@ import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/member/archive.dart'; import 'package:pilipala/models/member/coin.dart'; import 'package:pilipala/models/member/info.dart'; +import 'package:pilipala/models/member/like.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; @@ -25,6 +26,7 @@ class MemberController extends GetxController { RxInt attribute = (-1).obs; RxString attributeText = '关注'.obs; RxList recentCoinsList = [].obs; + RxList recentLikeList = [].obs; @override void onInit() { @@ -208,6 +210,14 @@ class MemberController extends GetxController { return res; } + // 请求点赞视频 + Future getRecentLikeVideo() async { + if (userInfo == null) return; + var res = await MemberHttp.getRecentLikeVideo(mid: mid); + recentLikeList.value = res['data']; + return res; + } + // 跳转查看动态 void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index bb0d92be..9c0da652 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -9,6 +9,7 @@ import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/utils/utils.dart'; import 'widgets/conis.dart'; +import 'widgets/like.dart'; import 'widgets/profile.dart'; import 'widgets/seasons.dart'; @@ -26,6 +27,7 @@ class _MemberPageState extends State late Future _futureBuilderFuture; late Future _memberSeasonsFuture; late Future _memberCoinsFuture; + late Future _memberLikeFuture; final ScrollController _extendNestCtr = ScrollController(); final StreamController appbarStream = StreamController(); late int mid; @@ -39,6 +41,7 @@ class _MemberPageState extends State _futureBuilderFuture = _memberController.getInfo(); _memberSeasonsFuture = _memberController.getMemberSeasons(); _memberCoinsFuture = _memberController.getRecentCoinVideo(); + _memberLikeFuture = _memberController.getRecentLikeVideo(); _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; @@ -162,6 +165,7 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), ), + const Divider(height: 1, thickness: 0.1), /// 视频 ListTile( @@ -170,12 +174,10 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), ), + const Divider(height: 1, thickness: 0.1), /// 专栏 - ListTile( - onTap: () {}, - title: const Text('Ta的专栏'), - ), + const ListTile(title: Text('Ta的专栏')), MediaQuery.removePadding( removeTop: true, removeBottom: true, @@ -218,12 +220,7 @@ class _MemberPageState extends State /// 最近投币 Obx( () => _memberController.recentCoinsList.isNotEmpty - ? ListTile( - onTap: () {}, - title: const Text('最近投币的视频'), - // trailing: const Icon(Icons.arrow_forward_outlined, - // size: 19), - ) + ? const ListTile(title: Text('最近投币的视频')) : const SizedBox(), ), MediaQuery.removePadding( @@ -257,13 +254,44 @@ class _MemberPageState extends State ), ), ), - // 最近点赞 - // ListTile( - // onTap: () {}, - // title: const Text('最近点赞的视频'), - // trailing: - // const Icon(Icons.arrow_forward_outlined, size: 19), - // ), + + /// 最近点赞 + Obx( + () => _memberController.recentLikeList.isNotEmpty + ? const ListTile(title: Text('最近点赞的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberLikeFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberLikePanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ), ], ), ), diff --git a/lib/pages/member/widgets/conis.dart b/lib/pages/member/widgets/conis.dart index 57a8a583..bdff2120 100644 --- a/lib/pages/member/widgets/conis.dart +++ b/lib/pages/member/widgets/conis.dart @@ -4,8 +4,8 @@ import 'package:pilipala/models/member/coin.dart'; import 'package:pilipala/pages/member_coin/widgets/item.dart'; class MemberCoinsPanel extends StatelessWidget { - final List? data; - const MemberCoinsPanel({super.key, this.data}); + final List data; + const MemberCoinsPanel({super.key, required this.data}); @override Widget build(BuildContext context) { @@ -20,9 +20,9 @@ class MemberCoinsPanel extends StatelessWidget { ), physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, - itemCount: data!.length, + itemCount: data.length, itemBuilder: (context, i) { - return MemberCoinsItem(coinItem: data![i]); + return MemberCoinsItem(coinItem: data[i]); }, ); }, diff --git a/lib/pages/member/widgets/like.dart b/lib/pages/member/widgets/like.dart new file mode 100644 index 00000000..6342c274 --- /dev/null +++ b/lib/pages/member/widgets/like.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/pages/member_like/widgets/item.dart'; + +class MemberLikePanel extends StatelessWidget { + final List data; + const MemberLikePanel({super.key, required this.data}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, boxConstraints) { + return GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // Use a fixed count for GridView + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + childAspectRatio: 0.94, + ), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: data.length, + itemBuilder: (context, i) { + return MemberLikeItem(likeItem: data[i]); + }, + ); + }, + ); + } +} diff --git a/lib/pages/member_like/widgets/item.dart b/lib/pages/member_like/widgets/item.dart new file mode 100644 index 00000000..57798bb7 --- /dev/null +++ b/lib/pages/member_like/widgets/item.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/stat/view.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/utils/utils.dart'; + +class MemberLikeItem extends StatelessWidget { + final MemberLikeDataModel likeItem; + + const MemberLikeItem({ + Key? key, + required this.likeItem, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + String heroTag = Utils.makeHeroTag(likeItem.aid); + return Card( + elevation: 0, + clipBehavior: Clip.hardEdge, + margin: EdgeInsets.zero, + child: InkWell( + onTap: () async { + int cid = + await SearchHttp.ab2c(aid: likeItem.aid, bvid: likeItem.bvid); + Get.toNamed('/video?bvid=${likeItem.bvid}&cid=$cid', + arguments: {'videoItem': likeItem, 'heroTag': heroTag}); + }, + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + NetworkImgLayer( + src: likeItem.pic, + width: maxWidth, + height: maxHeight, + ), + if (likeItem.duration != null) + PBadge( + bottom: 6, + right: 6, + type: 'gray', + text: Utils.timeFormat(likeItem.duration), + ) + ], + ); + }), + ), + Padding( + padding: const EdgeInsets.fromLTRB(5, 6, 0, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + likeItem.title!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Row( + children: [ + StatView( + view: likeItem.stat!.view, + theme: 'gray', + ), + const Spacer(), + Text( + Utils.CustomStamp_str( + timestamp: likeItem.pubdate, date: 'MM-DD'), + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.outline, + ), + ), + const SizedBox(width: 6) + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} From ea7ae15384660071505332282154a6d8f2037ed9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 14:29:43 +0800 Subject: [PATCH 174/438] =?UTF-8?q?mod:=20=E5=90=88=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/controller.dart | 7 +++- lib/pages/member/view.dart | 52 +++++++++++++-------------- lib/pages/member/widgets/seasons.dart | 44 +++++++++++++---------- lib/pages/member_seasons/view.dart | 3 +- 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 7db046d4..cc928a8d 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -192,12 +192,17 @@ class MemberController extends GetxController { Share.share('${memberInfo.value.name} - https://space.bilibili.com/$mid'); } - // 请求专栏 + // 请求合集 Future getMemberSeasons() async { if (userInfo == null) return; var res = await MemberHttp.getMemberSeasons(mid, 1, 10); if (!res['status']) { SmartDialog.showToast("用户专栏请求异常:${res['msg']}"); + } else { + // 只取前四个专栏 + res['data'].seasonsList.map((e) { + e.archives = e.archives!.sublist(0, 4); + }).toList(); } return res; } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 9c0da652..f62ffacc 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -178,39 +178,37 @@ class _MemberPageState extends State /// 专栏 const ListTile(title: Text('Ta的专栏')), + const Divider(height: 1, thickness: 0.1), + + /// 合集 + const ListTile(title: Text('Ta的合集')), MediaQuery.removePadding( removeTop: true, removeBottom: true, context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberSeasonsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - if (data['data'].seasonsList.isEmpty) { - return commenWidget('用户没有设置专栏'); - } else { - return MemberSeasonsPanel(data: data['data']); - } - } else { - // 请求错误 - return const SizedBox(); - } - } else { + child: FutureBuilder( + future: _memberSeasonsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { return const SizedBox(); } - }, - ), + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + if (data['data'].seasonsList.isEmpty) { + return commenWidget('用户没有设置合集'); + } else { + return MemberSeasonsPanel(data: data['data']); + } + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, ), ), diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 125c978f..1367d6bd 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -25,7 +25,7 @@ class MemberSeasonsPanel extends StatelessWidget { children: [ ListTile( onTap: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), + '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}&seasonName=${item.meta!.name}'), title: Text( item.meta!.name!, maxLines: 1, @@ -44,24 +44,30 @@ class MemberSeasonsPanel extends StatelessWidget { ), ), const SizedBox(height: 10), - LayoutBuilder( - builder: (context, boxConstraints) { - return GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // Use a fixed count for GridView - crossAxisSpacing: StyleString.safeSpace, - mainAxisSpacing: StyleString.safeSpace, - childAspectRatio: 0.94, - ), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: item.archives!.length, - itemBuilder: (context, i) { - return MemberSeasonsItem(seasonItem: item.archives![i]); - }, - ); - }, + Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: LayoutBuilder( + builder: (context, boxConstraints) { + return GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // Use a fixed count for GridView + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + childAspectRatio: 0.94, + ), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: item.archives!.length, + itemBuilder: (context, i) { + return MemberSeasonsItem(seasonItem: item.archives![i]); + }, + ); + }, + ), ), ], ), diff --git a/lib/pages/member_seasons/view.dart b/lib/pages/member_seasons/view.dart index 06944f10..556e2ec5 100644 --- a/lib/pages/member_seasons/view.dart +++ b/lib/pages/member_seasons/view.dart @@ -43,7 +43,8 @@ class _MemberSeasonsPageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的专栏', style: Theme.of(context).textTheme.titleMedium), + title: Text(Get.parameters['seasonName']!, + style: Theme.of(context).textTheme.titleMedium), ), body: Padding( padding: const EdgeInsets.only( From eaae622f95b582bfd1f511e0ce1f70ecd08514ba Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 17:12:06 +0800 Subject: [PATCH 175/438] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 95 ------------------- lib/models/video/subTitile/result.dart | 8 -- lib/pages/video/detail/controller.dart | 9 -- .../video/detail/introduction/controller.dart | 3 +- .../video/detail/widgets/header_control.dart | 9 +- lib/plugin/pl_player/controller.dart | 27 +----- 6 files changed, 11 insertions(+), 140 deletions(-) delete mode 100644 lib/models/common/subtitle_type.dart diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart deleted file mode 100644 index ac3ee3e0..00000000 --- a/lib/models/common/subtitle_type.dart +++ /dev/null @@ -1,95 +0,0 @@ -enum SubtitleType { - // 中文(中国) - zhCN, - // 中文(自动翻译) - aizh, - // 英语(自动生成) - aien, - // 中文(简体) - zhHans, - // 英文(美国) - enUS, - // 中文繁体 - zhTW, - // - en, - // - pt, - // - es, -} - -extension SubtitleTypeExtension on SubtitleType { - String get description { - switch (this) { - case SubtitleType.zhCN: - return '中文(中国)'; - case SubtitleType.aizh: - return '中文(自动翻译)'; - case SubtitleType.aien: - return '英语(自动生成)'; - case SubtitleType.zhHans: - return '中文(简体)'; - case SubtitleType.enUS: - return '英文(美国)'; - case SubtitleType.zhTW: - return '中文(繁体)'; - case SubtitleType.en: - return '英文'; - case SubtitleType.pt: - return '葡萄牙语'; - case SubtitleType.es: - return '西班牙语'; - } - } -} - -extension SubtitleIdExtension on SubtitleType { - String get id { - switch (this) { - case SubtitleType.zhCN: - return 'zh-CN'; - case SubtitleType.aizh: - return 'ai-zh'; - case SubtitleType.aien: - return 'ai-en'; - case SubtitleType.zhHans: - return 'zh-Hans'; - case SubtitleType.enUS: - return 'en-US'; - case SubtitleType.zhTW: - return 'zh-TW'; - case SubtitleType.en: - return 'en'; - case SubtitleType.pt: - return 'pt'; - case SubtitleType.es: - return 'es'; - } - } -} - -extension SubtitleCodeExtension on SubtitleType { - int get code { - switch (this) { - case SubtitleType.zhCN: - return 1; - case SubtitleType.aizh: - return 2; - case SubtitleType.aien: - return 3; - case SubtitleType.zhHans: - return 4; - case SubtitleType.enUS: - return 5; - case SubtitleType.zhTW: - return 6; - case SubtitleType.en: - return 7; - case SubtitleType.pt: - return 8; - case SubtitleType.es: - return 9; - } - } -} diff --git a/lib/models/video/subTitile/result.dart b/lib/models/video/subTitile/result.dart index d3e32e55..e137439f 100644 --- a/lib/models/video/subTitile/result.dart +++ b/lib/models/video/subTitile/result.dart @@ -1,6 +1,3 @@ -import 'package:get/get.dart'; -import '../../common/subtitle_type.dart'; - class SubTitlteModel { SubTitlteModel({ this.aid, @@ -78,11 +75,6 @@ class SubTitlteItemModel { aiType: json["ai_type"], aiStatus: json["ai_status"], title: json["lan_doc"], - code: SubtitleType.values - .firstWhereOrNull( - (element) => element.id.toString() == json["lan"]) - ?.index ?? - -1, content: '', body: [], ); diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index ea85a5b9..901c4014 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -446,15 +446,6 @@ class VideoDetailController extends GetxController } } - // 获取字幕内容 - // Future getSubtitleContent(String url) async { - // var res = await Request().get('https:$url'); - // subtitleContents.value = res.data['body'].map((e) { - // return SubTitileContentModel.fromJson(e); - // }).toList(); - // setSubtitleContent(); - // } - setSubtitleContent() { plPlayerController.subtitleContent.value = ''; plPlayerController.subtitles.value = subtitles; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1a1b1b74..50aac4cd 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -435,7 +435,8 @@ class VideoIntroController extends GetxController { videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); - videoDetailCtr.getSubtitle(); + videoDetailCtr.clearSubtitleContent(); + await videoDetailCtr.getSubtitle(); videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 5072377a..45a9684a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -426,7 +426,12 @@ class _HeaderControlState extends State { /// 选择字幕 void showSubtitleDialog() async { int tempThemeValue = widget.controller!.subTitleCode.value; - int len = widget.videoDetailCtr!.subtitles.length; + final List subtitles = widget.videoDetailCtr!.subtitles; + int len = subtitles.length; + if (subtitles.firstWhereOrNull((element) => element.id == tempThemeValue) == + null) { + tempThemeValue = -1; + } showDialog( context: context, builder: (BuildContext context) { @@ -458,7 +463,7 @@ class _HeaderControlState extends State { ), ...widget.videoDetailCtr!.subtitles .map((e) => RadioListTile( - value: e.code, + value: e.id, title: Text(e.title), groupValue: tempThemeValue, onChanged: (value) { diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index a614d75d..a5de33fd 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -123,7 +123,7 @@ class PlPlayerController { PreferredSizeWidget? headerControl; PreferredSizeWidget? bottomControl; Widget? danmuWidget; - late RxList subtitles; + RxList subtitles = [].obs; String videoType = 'archive'; /// 数据加载监听 @@ -642,10 +642,6 @@ class PlPlayerController { const Duration(seconds: 1), () => videoPlayerServiceHandler.onPositionChange(event)); }), - - // onSubTitleOpenChanged.listen((bool event) { - // toggleSubtitle(event ? subTitleCode.value : -1); - // }) ], ); } @@ -1049,25 +1045,6 @@ class PlPlayerController { void toggleSubtitle(int code) { _subTitleOpen.value = code != -1; _subTitleCode.value = code; - // if (code == -1) { - // // 关闭字幕 - // _subTitleOpen.value = false; - // _subTitleCode.value = code; - // _videoPlayerController?.setSubtitleTrack(SubtitleTrack.no()); - // return; - // } - // final SubTitlteItemModel? subtitle = subtitles?.firstWhereOrNull( - // (element) => element.code == code, - // ); - // _subTitleOpen.value = true; - // _subTitleCode.value = code; - // _videoPlayerController?.setSubtitleTrack( - // SubtitleTrack.data( - // subtitle!.content!, - // title: subtitle.title, - // language: subtitle.lan, - // ), - // ); } void querySubtitleContent(double progress) { @@ -1079,7 +1056,7 @@ class PlPlayerController { return; } final SubTitlteItemModel? subtitle = subtitles.firstWhereOrNull( - (element) => element.code == subTitleCode.value, + (element) => element.id == subTitleCode.value, ); if (subtitle != null && subtitle.body!.isNotEmpty) { for (var content in subtitle.body!) { From de86dba39dee578a77c54c938397552cc962f56d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 18:47:04 +0800 Subject: [PATCH 176/438] =?UTF-8?q?feat:=20=E6=B8=AF=E6=BE=B3=E5=8F=B0?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/constants.dart | 1 + lib/http/init.dart | 40 ++++++++++++++++++++--------- lib/pages/setting/play_setting.dart | 9 +++++++ lib/utils/storage.dart | 2 ++ 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/lib/http/constants.dart b/lib/http/constants.dart index 3d749ee8..cf6e00f2 100644 --- a/lib/http/constants.dart +++ b/lib/http/constants.dart @@ -5,6 +5,7 @@ class HttpString { static const String appBaseUrl = 'https://app.bilibili.com'; static const String liveBaseUrl = 'https://api.live.bilibili.com'; static const String passBaseUrl = 'https://passport.bilibili.com'; + static const String bangumiBaseUrl = 'https://bili.meark.me'; static const List validateStatusCodes = [ 302, 304, diff --git a/lib/http/init.dart b/lib/http/init.dart index a0b36369..cb9d6f39 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -27,11 +27,13 @@ class Request { late bool enableSystemProxy; late String systemProxyHost; late String systemProxyPort; - static final RegExp spmPrefixExp = RegExp(r''); + static final RegExp spmPrefixExp = + RegExp(r''); /// 设置cookie static setCookie() async { Box userInfoCache = GStrorage.userInfo; + Box setting = GStrorage.setting; final String cookiePath = await Utils.getCookiePath(); final PersistCookieJar cookieJar = PersistCookieJar( ignoreExpires: true, @@ -54,7 +56,11 @@ class Request { } } setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); - + String baseUrlType = 'default'; + if (setting.get(SettingBoxKey.enableGATMode, defaultValue: false)) { + baseUrlType = 'bangumi'; + } + setBaseUrl(type: baseUrlType); try { await buvidActivate(); } catch (e) { @@ -95,11 +101,10 @@ class Request { String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!; Random rand = Random(); String rand_png_end = base64.encode( - List.generate(32, (_) => rand.nextInt(256)) + - List.filled(4, 0) + - [73, 69, 78, 68] + - List.generate(4, (_) => rand.nextInt(256)) - ); + List.generate(32, (_) => rand.nextInt(256)) + + List.filled(4, 0) + + [73, 69, 78, 68] + + List.generate(4, (_) => rand.nextInt(256))); String jsonData = json.encode({ '3064': 1, @@ -110,11 +115,9 @@ class Request { }, }); - await Request().post( - Api.activateBuvidApi, - data: {'payload': jsonData}, - options: Options(contentType: 'application/json') - ); + await Request().post(Api.activateBuvidApi, + data: {'payload': jsonData}, + options: Options(contentType: 'application/json')); } /* @@ -294,4 +297,17 @@ class Request { } return headerUa; } + + static setBaseUrl({String type = 'default'}) { + switch (type) { + case 'default': + dio.options.baseUrl = HttpString.apiBaseUrl; + break; + case 'bangumi': + dio.options.baseUrl = HttpString.bangumiBaseUrl; + break; + default: + dio.options.baseUrl = HttpString.apiBaseUrl; + } + } } diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 0f7dcdc3..cb8a3996 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/http/init.dart'; import 'package:pilipala/models/video/play/ao_output.dart'; import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; @@ -163,6 +164,14 @@ class _PlaySettingState extends State { callFn: (bool val) { GlobalData().enablePlayerControlAnimation = val; }), + SetSwitchItem( + title: '港澳台模式', + setKey: SettingBoxKey.enableGATMode, + defaultVal: false, + callFn: (bool val) { + Request.setBaseUrl(type: val ? 'bangumi' : 'default'); + }, + ), ListTile( dense: false, title: Text('默认视频画质', style: titleStyle), diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index bf9074e3..dca5a158 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -104,6 +104,8 @@ class SettingBoxKey { enablePlayerControlAnimation = 'enablePlayerControlAnimation', // 默认音频输出方式 defaultAoOutput = 'defaultAoOutput', + // 港澳台模式 + enableGATMode = 'enableGATMode', // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', From 9f1b74b7e23f2b09c0c506dfbb5dd26a8217b506 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 00:28:26 +0800 Subject: [PATCH 177/438] mod --- lib/pages/video/detail/reply/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index be1bd331..4fe69481 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -188,7 +188,7 @@ class _VideoReplyPanelState extends State if (snapshot.connectionState == ConnectionState.done) { var data = snapshot.data; if (_videoReplyController.replyList.isNotEmpty || - (data && data['status'])) { + (data != null && data['status'])) { // 请求成功 return Obx( () => _videoReplyController.isLoadingMore && From a19129c59615c5eda9750ffd0d881528d1054d66 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 13:02:50 +0800 Subject: [PATCH 178/438] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E6=98=BE=E9=9A=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 46 +++++++++++-------- .../video/detail/widgets/header_control.dart | 17 ++++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 901c4014..c5e37be1 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -141,13 +141,7 @@ class VideoDetailController extends GetxController if (Platform.isAndroid) { floating = Floating(); } - headerControl = HeaderControl( - controller: plPlayerController, - videoDetailCtr: this, - floating: floating, - bvid: bvid, - videoType: videoType, - ); + // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); // 预设的画质 @@ -158,7 +152,18 @@ class VideoDetailController extends GetxController defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); - getSubtitle(); + getSubtitle().then( + (subtitles) { + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + bvid: bvid, + videoType: videoType, + showSubtitleBtn: subtitles.isNotEmpty, + ); + }, + ); } showReplyReplyPanel(oid, fRpid, firstFloor) { @@ -432,17 +437,22 @@ class VideoDetailController extends GetxController if (result['status']) { if (result['data'].subtitles.isNotEmpty) { subtitles = result['data'].subtitles; - if (subtitles.isNotEmpty) { - for (var i in subtitles) { - final Map res = await VideoHttp.getSubtitleContent( - i.subtitleUrl, - ); - i.content = res['content']; - i.body = res['body']; - } - } + getDanmaku(subtitles); + } + return subtitles; + } + } + + // 获取弹幕 + Future getDanmaku(List subtitles) async { + if (subtitles.isNotEmpty) { + for (var i in subtitles) { + final Map res = await VideoHttp.getSubtitleContent( + i.subtitleUrl, + ); + i.content = res['content']; + i.body = res['body']; } - return result['data']; } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 45a9684a..f3b9549a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -30,6 +30,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { this.floating, this.bvid, this.videoType, + this.showSubtitleBtn, super.key, }); final PlPlayerController? controller; @@ -37,6 +38,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { final Floating? floating; final String? bvid; final SearchType? videoType; + final bool? showSubtitleBtn; @override State createState() => _HeaderControlState(); @@ -1327,14 +1329,15 @@ class _HeaderControlState extends State { ], /// 字幕 - ComBtn( - icon: const Icon( - Icons.closed_caption_off, - size: 22, - color: Colors.white, + if (widget.showSubtitleBtn!) + ComBtn( + icon: const Icon( + Icons.closed_caption_off, + size: 22, + color: Colors.white, + ), + fuc: () => showSubtitleDialog(), ), - fuc: () => showSubtitleDialog(), - ), SizedBox(width: buttonSpace), Obx( () => SizedBox( From c7ba9dc97bd9adb0042ded14858638ede7050ae9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 22:01:12 +0800 Subject: [PATCH 179/438] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 + lib/http/constants.dart | 1 + lib/http/msg.dart | 27 +++++- lib/models/msg/system.dart | 77 +++++++++++++++++ lib/pages/message/system/controller.dart | 14 ++- lib/pages/message/system/view.dart | 104 +++++++++++++++++++++++ lib/pages/whisper/view.dart | 2 +- 7 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 lib/models/msg/system.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index f20b8bcf..46bbb6ac 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -548,4 +548,8 @@ class Api { /// 收到的赞 static const String messageLikeAPi = '/x/msgfeed/like'; + + /// 系统通知 + static const String messageSystemAPi = + '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; } diff --git a/lib/http/constants.dart b/lib/http/constants.dart index 3d749ee8..cad413ef 100644 --- a/lib/http/constants.dart +++ b/lib/http/constants.dart @@ -5,6 +5,7 @@ class HttpString { static const String appBaseUrl = 'https://app.bilibili.com'; static const String liveBaseUrl = 'https://api.live.bilibili.com'; static const String passBaseUrl = 'https://passport.bilibili.com'; + static const String messageBaseUrl = 'https://message.bilibili.com'; static const List validateStatusCodes = [ 302, 304, diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7c168230..86789fd1 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:dio/dio.dart'; import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; +import 'package:pilipala/models/msg/system.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -149,7 +150,7 @@ class MsgHttp { 'msg[msg_status]': 0, 'msg[content]': jsonEncode(content), 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, - 'msg[new_face_version]': 0, + 'msg[new_face_version]': 1, 'msg[dev_id]': getDevId(), 'from_firework': 0, 'build': 0, @@ -287,4 +288,28 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + static Future messageSystem() async { + var res = await Request().get(Api.messageSystemAPi, data: { + 'csrf': await Request.getCsrf(), + 'page_size': 20, + 'build': 0, + 'mobi_app': 'web', + }); + if (res.data['code'] == 0) { + try { + print(res.data['data']['system_notify_list']); + return { + 'status': true, + 'data': res.data['data']['system_notify_list'] + .map((e) => MessageSystemModel.fromJson(e)) + .toList(), + }; + } catch (err) { + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/models/msg/system.dart b/lib/models/msg/system.dart new file mode 100644 index 00000000..20427707 --- /dev/null +++ b/lib/models/msg/system.dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +class MessageSystemModel { + int? id; + int? cursor; + int? type; + String? title; + Map? content; + Source? source; + String? timeAt; + int? cardType; + String? cardBrief; + String? cardMsgBrief; + String? cardCover; + String? cardStoryTitle; + String? cardLink; + String? mc; + int? isStation; + int? isSend; + int? notifyCursor; + + MessageSystemModel({ + this.id, + this.cursor, + this.type, + this.title, + this.content, + this.source, + this.timeAt, + this.cardType, + this.cardBrief, + this.cardMsgBrief, + this.cardCover, + this.cardStoryTitle, + this.cardLink, + this.mc, + this.isStation, + this.isSend, + this.notifyCursor, + }); + + factory MessageSystemModel.fromJson(Map jsons) => + MessageSystemModel( + id: jsons["id"], + cursor: jsons["cursor"], + type: jsons["type"], + title: jsons["title"], + content: json.decode(jsons["content"]), + source: Source.fromJson(jsons["source"]), + timeAt: jsons["time_at"], + cardType: jsons["card_type"], + cardBrief: jsons["card_brief"], + cardMsgBrief: jsons["card_msg_brief"], + cardCover: jsons["card_cover"], + cardStoryTitle: jsons["card_story_title"], + cardLink: jsons["card_link"], + mc: jsons["mc"], + isStation: jsons["is_station"], + isSend: jsons["is_send"], + notifyCursor: jsons["notify_cursor"], + ); +} + +class Source { + String? name; + String? logo; + + Source({ + this.name, + this.logo, + }); + + factory Source.fromJson(Map json) => Source( + name: json["name"], + logo: json["logo"], + ); +} diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart index ad28af56..bf31f6bc 100644 --- a/lib/pages/message/system/controller.dart +++ b/lib/pages/message/system/controller.dart @@ -1,3 +1,15 @@ import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/system.dart'; -class MessageSystemController extends GetxController {} +class MessageSystemController extends GetxController { + RxList systemItems = [].obs; + + Future queryMessageSystem({String type = 'init'}) async { + var res = await MsgHttp.messageSystem(); + if (res['status']) { + systemItems.addAll(res['data']); + } + return res; + } +} diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart index da0f1219..f7b94e5a 100644 --- a/lib/pages/message/system/view.dart +++ b/lib/pages/message/system/view.dart @@ -1,4 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/models/msg/system.dart'; +import 'controller.dart'; class MessageSystemPage extends StatefulWidget { const MessageSystemPage({super.key}); @@ -8,12 +12,112 @@ class MessageSystemPage extends StatefulWidget { } class _MessageSystemPageState extends State { + final MessageSystemController _messageSystemCtr = + Get.put(MessageSystemController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageSystemCtr.queryMessageSystem(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('系统通知'), ), + body: RefreshIndicator( + onRefresh: () async { + await _messageSystemCtr.queryMessageSystem(); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final systemItems = _messageSystemCtr.systemItems; + print(systemItems.length); + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => SystemItem( + item: systemItems[index], + index: index, + messageSystemCtr: _messageSystemCtr, + ), + itemCount: systemItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 14, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageSystemCtr.queryMessageSystem(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class SystemItem extends StatelessWidget { + final MessageSystemModel item; + final int index; + final MessageSystemController messageSystemCtr; + + const SystemItem( + {super.key, + required this.item, + required this.index, + required this.messageSystemCtr}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(14, 14, 14, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.title!, + style: + const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 4), + Text( + item.timeAt!, + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(height: 6), + Text(item.content!['web']), + ], + ), ); } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 9436e2be..1814c274 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -71,7 +71,7 @@ class _WhisperPageState extends State { ..._whisperController.noticesList.map((element) { return InkWell( onTap: () { - if (['/messageAt', '/messageSystem'] + if (['/messageAt'] .contains(element['path'])) { SmartDialog.showToast('功能开发中'); return; From 0d96327f344b99424c54be2e395c71ef9debeb08 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Jul 2024 23:11:11 +0800 Subject: [PATCH 180/438] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 11e097e1..52c423d7 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -17,7 +17,11 @@ class PlDanmakuController { int segCount = (videoDuration / segmentLength).ceil(); requestedSeg = List.generate(segCount, (index) => false); } - queryDanmaku(calcSegment(progress)); + try { + queryDanmaku(calcSegment(progress)); + } catch (e) { + print(e); + } } void dispose() { @@ -31,16 +35,18 @@ class PlDanmakuController { void queryDanmaku(int segmentIndex) async { assert(requestedSeg[segmentIndex] == false); - requestedSeg[segmentIndex] = true; - final DmSegMobileReply result = await DanmakaHttp.queryDanmaku( - cid: cid, segmentIndex: segmentIndex + 1); - if (result.elems.isNotEmpty) { - for (var element in result.elems) { - int pos = element.progress ~/ 100; //每0.1秒存储一次 - if (dmSegMap[pos] == null) { - dmSegMap[pos] = []; + if (requestedSeg.length > segmentIndex) { + requestedSeg[segmentIndex] = true; + final DmSegMobileReply result = await DanmakaHttp.queryDanmaku( + cid: cid, segmentIndex: segmentIndex + 1); + if (result.elems.isNotEmpty) { + for (var element in result.elems) { + int pos = element.progress ~/ 100; //每0.1秒存储一次 + if (dmSegMap[pos] == null) { + dmSegMap[pos] = []; + } + dmSegMap[pos]!.add(element); } - dmSegMap[pos]!.add(element); } } } From 739e1d9ada6d7fddd0dedf9a2a6def33c45bdb53 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 5 Jul 2024 22:54:21 +0800 Subject: [PATCH 181/438] =?UTF-8?q?opt:=20AppBar=E6=B8=90=E5=8F=98?= =?UTF-8?q?=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index e485fe41..74072b2f 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -78,16 +78,16 @@ class _HomePageState extends State Theme.of(context) .colorScheme .primary - .withOpacity(0.9), + .withOpacity(0.4), Theme.of(context) .colorScheme - .primary + .surface .withOpacity(0.5), Theme.of(context).colorScheme.surface ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.1, 0.3, 0.5]), ), ), ), From b3f1400c044e6505e62936df8fc2b021b1e39e54 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 00:07:02 +0800 Subject: [PATCH 182/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ebb266cd..d8f696d4 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -644,6 +644,15 @@ InlineSpan buildContent( title, '', ); + } else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) { + Get.toNamed( + '/webview', + parameters: { + 'url': 'https://www.bilibili.com/read/$matchStr', + 'type': 'url', + 'pageTitle': title + }, + ); } else { Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( From 7ae637ed6acd7454723dc4e45821efa8ba5e95a6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 15:37:50 +0800 Subject: [PATCH 183/438] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8Bup=E4=B8=BB?= =?UTF-8?q?=E6=94=B6=E8=97=8F=E8=AE=A2=E9=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/controller.dart | 8 +++- lib/pages/fav/view.dart | 22 ++++++--- lib/pages/fav/widgets/item.dart | 5 +- lib/pages/fav_detail/controller.dart | 2 + lib/pages/fav_detail/view.dart | 1 + .../fav_detail/widget/fav_video_card.dart | 7 ++- lib/pages/fav_search/view.dart | 1 + lib/pages/member/controller.dart | 9 +++- lib/pages/member/view.dart | 46 +++++++++++++------ lib/pages/subscription/controller.dart | 8 +++- lib/pages/subscription/view.dart | 9 ++-- lib/pages/subscription/widgets/item.dart | 45 +++++++++++------- 12 files changed, 115 insertions(+), 48 deletions(-) diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 8fcbf971..5be46bf0 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -16,10 +16,16 @@ class FavController extends GetxController { int currentPage = 1; int pageSize = 60; RxBool hasMore = true.obs; + late int mid; + late int ownerMid; + RxBool isOwner = false.obs; @override void onInit() { + mid = int.parse(Get.parameters['mid'] ?? '-1'); userInfo = userInfoCache.get('userInfoCache'); + ownerMid = userInfo != null ? userInfo!.mid! : -1; + isOwner.value = mid == -1 || mid == ownerMid; super.onInit(); } @@ -33,7 +39,7 @@ class FavController extends GetxController { var res = await UserHttp.userfavFolder( pn: currentPage, ps: pageSize, - mid: userInfo!.mid!, + mid: isOwner.value ? ownerMid : mid, ); if (res['status']) { if (type == 'init') { diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 4f48213e..7010ba0d 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -42,17 +42,25 @@ class _FavPageState extends State { appBar: AppBar( centerTitle: false, titleSpacing: 0, - title: Text( - '我的收藏', - style: Theme.of(context).textTheme.titleMedium, - ), + title: Obx(() => Text( + '${_favController.isOwner.value ? '我' : 'Ta'}的收藏', + style: Theme.of(context).textTheme.titleMedium, + )), actions: [ + Obx(() => !_favController.isOwner.value + ? IconButton( + onPressed: () => + Get.toNamed('/subscription?mid=${_favController.mid}'), + icon: const Icon(Icons.subscriptions_outlined, size: 21), + tooltip: 'Ta的订阅', + ) + : const SizedBox.shrink()), IconButton( onPressed: () => Get.toNamed( '/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'), icon: const Icon(Icons.search_outlined), ), - const SizedBox(width: 6), + const SizedBox(width: 14), ], ), body: FutureBuilder( @@ -67,7 +75,9 @@ class _FavPageState extends State { itemCount: _favController.favFolderList.length, itemBuilder: (context, index) { return FavItem( - favFolderItem: _favController.favFolderList[index]); + favFolderItem: _favController.favFolderList[index], + isOwner: _favController.isOwner.value, + ); }, ), ); diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 3c44ec9d..9d453fb5 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -7,7 +7,9 @@ import 'package:pilipala/utils/utils.dart'; class FavItem extends StatelessWidget { // ignore: prefer_typing_uninitialized_variables final favFolderItem; - const FavItem({super.key, required this.favFolderItem}); + final bool isOwner; + const FavItem( + {super.key, required this.favFolderItem, required this.isOwner}); @override Widget build(BuildContext context) { @@ -20,6 +22,7 @@ class FavItem extends StatelessWidget { parameters: { 'heroTag': heroTag, 'mediaId': favFolderItem.id.toString(), + 'isOwner': isOwner ? '1' : '0', }, ); }, diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 7af398e8..3f87c226 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -19,6 +19,7 @@ class FavDetailController extends GetxController { RxList favList = [].obs; RxString loadingText = '加载中...'.obs; RxInt mediaCount = 0.obs; + late String isOwner; @override void onInit() { @@ -26,6 +27,7 @@ class FavDetailController extends GetxController { if (Get.parameters.keys.isNotEmpty) { mediaId = int.parse(Get.parameters['mediaId']!); heroTag = Get.parameters['heroTag']!; + isOwner = Get.parameters['isOwner']!; } super.onInit(); } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 1bf5cb6f..cb9d7e7b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -212,6 +212,7 @@ class _FavDetailPageState extends State { SliverChildBuilderDelegate((context, index) { return FavVideoCardH( videoItem: favList[index], + isOwner: _favDetailController.isOwner, callFn: () => _favDetailController .onCancelFav(favList[index].id), ); diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 79e5c073..9779c549 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -18,12 +18,14 @@ class FavVideoCardH extends StatelessWidget { final dynamic videoItem; final Function? callFn; final int? searchType; + final String isOwner; const FavVideoCardH({ Key? key, required this.videoItem, this.callFn, this.searchType, + required this.isOwner, }) : super(key: key); @override @@ -123,6 +125,7 @@ class FavVideoCardH extends StatelessWidget { videoItem: videoItem, callFn: callFn, searchType: searchType, + isOwner: isOwner, ) ], ), @@ -140,11 +143,13 @@ class VideoContent extends StatelessWidget { final dynamic videoItem; final Function? callFn; final int? searchType; + final String isOwner; const VideoContent({ super.key, required this.videoItem, this.callFn, this.searchType, + required this.isOwner, }); @override @@ -211,7 +216,7 @@ class VideoContent extends StatelessWidget { ), ], ), - searchType != 1 + searchType != 1 && isOwner == '1' ? Positioned( right: 0, bottom: -4, diff --git a/lib/pages/fav_search/view.dart b/lib/pages/fav_search/view.dart index 9b2ab15d..2654ccb1 100644 --- a/lib/pages/fav_search/view.dart +++ b/lib/pages/fav_search/view.dart @@ -100,6 +100,7 @@ class _FavSearchPageState extends State { return FavVideoCardH( videoItem: _favSearchCtr.favList[index], searchType: searchType, + isOwner: '0', callFn: () => searchType != 1 ? _favSearchCtr .onCancelFav(_favSearchCtr.favList[index].id!) diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index cc928a8d..ada869b5 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -27,6 +27,7 @@ class MemberController extends GetxController { RxString attributeText = '关注'.obs; RxList recentCoinsList = [].obs; RxList recentLikeList = [].obs; + RxBool isOwner = false.obs; @override void onInit() { @@ -34,6 +35,7 @@ class MemberController extends GetxController { mid = int.parse(Get.parameters['mid']!); userInfo = userInfoCache.get('userInfoCache'); ownerMid = userInfo != null ? userInfo.mid : -1; + isOwner.value = mid == ownerMid; face.value = Get.arguments['face'] ?? ''; heroTag = Get.arguments['heroTag'] ?? ''; relationSearch(); @@ -197,11 +199,12 @@ class MemberController extends GetxController { if (userInfo == null) return; var res = await MemberHttp.getMemberSeasons(mid, 1, 10); if (!res['status']) { - SmartDialog.showToast("用户专栏请求异常:${res['msg']}"); + SmartDialog.showToast("用户合集请求异常:${res['msg']}"); } else { // 只取前四个专栏 res['data'].seasonsList.map((e) { - e.archives = e.archives!.sublist(0, 4); + e.archives = + e.archives!.length > 4 ? e.archives!.sublist(0, 4) : e.archives!; }).toList(); } return res; @@ -235,4 +238,6 @@ class MemberController extends GetxController { void pushRecentCoinsPage() async { if (recentCoinsList.isNotEmpty) {} } + + void pushfavPage() => Get.toNamed('/fav?mid=$mid'); } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index f62ffacc..c721d638 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -159,29 +159,47 @@ class _MemberPageState extends State profileWidget(), /// 动态链接 - ListTile( - onTap: _memberController.pushDynamicsPage, - title: const Text('Ta的动态'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), + Obx( + () => ListTile( + onTap: _memberController.pushDynamicsPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的动态'), + trailing: + const Icon(Icons.arrow_forward_outlined, size: 19), + ), ), const Divider(height: 1, thickness: 0.1), /// 视频 - ListTile( - onTap: _memberController.pushArchivesPage, - title: const Text('Ta的投稿'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), - ), + Obx(() => ListTile( + onTap: _memberController.pushArchivesPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'), + trailing: const Icon(Icons.arrow_forward_outlined, + size: 19), + )), + const Divider(height: 1, thickness: 0.1), + + /// 他的收藏夹 + Obx(() => ListTile( + onTap: _memberController.pushfavPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'), + trailing: const Icon(Icons.arrow_forward_outlined, + size: 19), + )), const Divider(height: 1, thickness: 0.1), /// 专栏 - const ListTile(title: Text('Ta的专栏')), + Obx(() => ListTile( + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'))), const Divider(height: 1, thickness: 0.1), /// 合集 - const ListTile(title: Text('Ta的合集')), + Obx(() => ListTile( + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的合集'))), MediaQuery.removePadding( removeTop: true, removeBottom: true, @@ -212,8 +230,6 @@ class _MemberPageState extends State ), ), - /// 收藏 - /// 追番 /// 最近投币 Obx( diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index d8a76d44..b59a42f0 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -16,11 +16,17 @@ class SubController extends GetxController { int currentPage = 1; int pageSize = 20; RxBool hasMore = true.obs; + late int mid; + late int ownerMid; + RxBool isOwner = false.obs; @override void onInit() { super.onInit(); + mid = int.parse(Get.parameters['mid'] ?? '-1'); userInfo = userInfoCache.get('userInfoCache'); + ownerMid = userInfo != null ? userInfo!.mid! : -1; + isOwner.value = mid == -1 || mid == ownerMid; } Future querySubFolder({type = 'init'}) async { @@ -30,7 +36,7 @@ class SubController extends GetxController { var res = await UserHttp.userSubFolder( pn: currentPage, ps: pageSize, - mid: userInfo!.mid!, + mid: isOwner.value ? ownerMid : mid, ); if (res['status']) { if (type == 'init') { diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index e1d1820d..cb9993b0 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -42,10 +42,10 @@ class _SubPageState extends State { appBar: AppBar( centerTitle: false, titleSpacing: 0, - title: Text( - '我的订阅', - style: Theme.of(context).textTheme.titleMedium, - ), + title: Obx(() => Text( + '${_subController.isOwner.value ? '我' : 'Ta'}的订阅', + style: Theme.of(context).textTheme.titleMedium, + )), ), body: FutureBuilder( future: _futureBuilderFuture, @@ -62,6 +62,7 @@ class _SubPageState extends State { return SubItem( subFolderItem: _subController.subFolderData.value.list![index], + isOwner: _subController.isOwner.value, cancelSub: _subController.cancelSub); }, ), diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index b244d3c7..0389b4a6 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -8,10 +8,12 @@ import '../../../models/user/sub_folder.dart'; class SubItem extends StatelessWidget { final SubFolderItemData subFolderItem; + final bool isOwner; final Function(SubFolderItemData) cancelSub; const SubItem({ super.key, required this.subFolderItem, + required this.isOwner, required this.cancelSub, }); @@ -59,6 +61,7 @@ class SubItem extends StatelessWidget { ), VideoContent( subFolderItem: subFolderItem, + isOwner: isOwner, cancelSub: cancelSub, ) ], @@ -73,8 +76,14 @@ class SubItem extends StatelessWidget { class VideoContent extends StatelessWidget { final SubFolderItemData subFolderItem; + final bool isOwner; final Function(SubFolderItemData)? cancelSub; - const VideoContent({super.key, required this.subFolderItem, this.cancelSub}); + const VideoContent({ + super.key, + required this.subFolderItem, + required this.isOwner, + this.cancelSub, + }); @override Widget build(BuildContext context) { @@ -111,22 +120,24 @@ class VideoContent extends StatelessWidget { ), ), const Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => cancelSub?.call(subFolderItem), - icon: Icon( - Icons.clear_outlined, - color: Theme.of(context).colorScheme.outline, - size: 18, - ), - ) - ], - ) + isOwner + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => cancelSub?.call(subFolderItem), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ) + ], + ) + : const SizedBox() ], ), ), From ba430eaa2eb1038eaf5155293e5046beb636e7c4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 15:56:25 +0800 Subject: [PATCH 184/438] =?UTF-8?q?mod:=20up=E4=B8=BB=E5=90=88=E9=9B=86?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/seasons.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/models/member/seasons.dart b/lib/models/member/seasons.dart index 70230367..88b93c78 100644 --- a/lib/models/member/seasons.dart +++ b/lib/models/member/seasons.dart @@ -9,11 +9,18 @@ class MemberSeasonsDataModel { MemberSeasonsDataModel.fromJson(Map json) { page = json['page']; - seasonsList = json['seasons_list'] != null + var tempList1 = json['seasons_list'] != null ? json['seasons_list'] .map((e) => MemberSeasonsList.fromJson(e)) .toList() : []; + var tempList2 = json['series_list'] != null + ? json['series_list'] + .map((e) => MemberSeasonsList.fromJson(e)) + .toList() + : []; + + seasonsList = [...tempList1, ...tempList2]; } } From a23a18f3bc8c995d5043fec9fa66c50d6c567c51 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 19:31:28 +0800 Subject: [PATCH 185/438] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95=E5=AF=BC?= =?UTF-8?q?=E8=87=B4headControl=E5=88=9D=E5=A7=8B=E5=8C=96=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 2 +- lib/pages/video/detail/controller.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 7c1d9ba6..834a0102 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -492,7 +492,7 @@ class VideoHttp { return {'status': false, 'data': [], 'msg': res.data['msg']}; } } catch (err) { - print(err); + return {'status': false, 'data': [], 'msg': res.data['msg']}; } } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c5e37be1..dbdd9fce 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -440,6 +440,8 @@ class VideoDetailController extends GetxController getDanmaku(subtitles); } return subtitles; + } else { + return []; } } From dac0898729862fc8ae25d31f4ecf0fdf939015ed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 23:43:50 +0800 Subject: [PATCH 186/438] =?UTF-8?q?opt:=20AppBar=E6=B8=90=E5=8F=98?= =?UTF-8?q?=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/view.dart | 7 +- lib/pages/home/view.dart | 149 ++++++++++++------------------- lib/pages/main/controller.dart | 3 + lib/pages/main/view.dart | 49 ++++++++-- lib/pages/media/view.dart | 13 ++- lib/pages/rank/controller.dart | 3 - lib/pages/rank/view.dart | 124 +++++++++---------------- lib/pages/video/detail/view.dart | 1 + 8 files changed, 162 insertions(+), 187 deletions(-) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 1775798e..495eb770 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; @@ -77,10 +78,14 @@ class _DynamicsPageState extends State Widget build(BuildContext context) { super.build(context); return Scaffold( + backgroundColor: Colors.transparent, appBar: AppBar( elevation: 0, scrolledUnderElevation: 0, - titleSpacing: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, title: SizedBox( height: 34, child: Stack( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 74072b2f..0163723b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -46,104 +46,66 @@ class _HomePageState extends State @override Widget build(BuildContext context) { super.build(context); - Brightness currentBrightness = MediaQuery.of(context).platformBrightness; - // 设置状态栏图标的亮度 - if (_homeController.enableGradientBg) { - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); - } return Scaffold( extendBody: true, extendBodyBehindAppBar: true, - appBar: _homeController.enableGradientBg - ? null - : AppBar(toolbarHeight: 0, elevation: 0), - body: Stack( + backgroundColor: Colors.transparent, + appBar: AppBar( + toolbarHeight: 0, + elevation: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), + body: Column( children: [ - // gradient background - if (_homeController.enableGradientBg) ...[ - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.4), - Theme.of(context) - .colorScheme - .surface - .withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - stops: const [0.1, 0.3, 0.5]), + CustomAppBar( + stream: _homeController.hideSearchBar + ? stream + : StreamController.broadcast().stream, + ctr: _homeController, + callback: showUserBottomSheet, + ), + if (_homeController.tabs.length > 1) ...[ + if (_homeController.enableGradientBg) ...[ + const CustomTabs(), + ] else ...[ + Container( + width: double.infinity, + height: 42, + padding: const EdgeInsets.only(top: 4), + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _homeController.tabController, + tabs: [ + for (var i in _homeController.tabs) Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_homeController.initialIndex.value == value) { + _homeController.tabsCtrList[value]().animateToTop(); + } + _homeController.initialIndex.value = value; + }, ), ), ), - ), - ], - Column( - children: [ - CustomAppBar( - stream: _homeController.hideSearchBar - ? stream - : StreamController.broadcast().stream, - ctr: _homeController, - callback: showUserBottomSheet, - ), - if (_homeController.tabs.length > 1) ...[ - if (_homeController.enableGradientBg) ...[ - const CustomTabs(), - ] else ...[ - const SizedBox(height: 4), - SizedBox( - width: double.infinity, - height: 42, - child: Align( - alignment: Alignment.center, - child: TabBar( - controller: _homeController.tabController, - tabs: [ - for (var i in _homeController.tabs) - Tab(text: i['label']) - ], - isScrollable: true, - dividerColor: Colors.transparent, - enableFeedback: true, - splashBorderRadius: BorderRadius.circular(10), - tabAlignment: TabAlignment.center, - onTap: (value) { - feedBack(); - if (_homeController.initialIndex.value == value) { - _homeController.tabsCtrList[value]().animateToTop(); - } - _homeController.initialIndex.value = value; - }, - ), - ), - ), - ], - ] else ...[ - const SizedBox(height: 6), - ], - Expanded( - child: TabBarView( - controller: _homeController.tabController, - children: _homeController.tabsPageList, - ), - ), ], + ] else ...[ + const SizedBox(height: 6), + ], + Expanded( + child: TabBarView( + controller: _homeController.tabController, + children: _homeController.tabsPageList, + ), ), ], ), @@ -280,7 +242,10 @@ class DefaultUser extends StatelessWidget { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { - return Theme.of(context).colorScheme.onInverseSurface; + return Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.05); }), ), onPressed: () => callback?.call(), @@ -317,7 +282,7 @@ class _CustomTabsState extends State { Widget build(BuildContext context) { return Container( height: 44, - margin: const EdgeInsets.only(top: 4), + margin: const EdgeInsets.only(top: 8), child: Obx( () => ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 14.0), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 2ef2bf7f..849e16d5 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -26,6 +26,7 @@ class MainController extends GetxController { Box userInfoCache = GStrorage.userInfo; RxBool userLogin = false.obs; late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; + late bool enableGradientBg; @override void onInit() { @@ -44,6 +45,8 @@ class MainController extends GetxController { if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { getUnreadDynamic(); } + enableGradientBg = + setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); } void onBackPressed(BuildContext context) { diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 29573501..20a12d35 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -117,14 +117,47 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { }, child: Scaffold( extendBody: true, - body: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _mainController.pageController, - onPageChanged: (index) { - _mainController.selectedIndex = index; - setState(() {}); - }, - children: _mainController.pages, + body: Stack( + children: [ + if (_mainController.enableGradientBg) + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height, + height: MediaQuery.of(context).padding.top + 400, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.6), + Theme.of(context) + .colorScheme + .surface + .withOpacity(0.3), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.1, 0.8, 1]), + ), + ), + ), + ), + PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _mainController.pageController, + onPageChanged: (index) { + _mainController.selectedIndex = index; + setState(() {}); + }, + children: _mainController.pages, + ), + ], ), bottomNavigationBar: _mainController.navigationBars.length > 1 ? StreamBuilder( diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index cc413e59..3694fa68 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/utils.dart'; class MediaPage extends StatefulWidget { @@ -46,7 +46,16 @@ class _MediaPageState extends State super.build(context); Color primary = Theme.of(context).colorScheme.primary; return Scaffold( - appBar: AppBar(toolbarHeight: 30), + backgroundColor: Colors.transparent, + appBar: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + toolbarHeight: 30, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), body: SingleChildScrollView( controller: mediaController.scrollController, child: Column( diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 64395ec7..bb34b13f 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -17,13 +17,10 @@ class RankController extends GetxController with GetTickerProviderStateMixin { Box setting = GStrorage.setting; late final StreamController searchBarStream = StreamController.broadcast(); - late bool enableGradientBg; @override void onInit() { super.onInit(); - enableGradientBg = - setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); // 进行tabs配置 setTabConfig(); } diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 4efa2b4e..218e60cf 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -31,94 +31,56 @@ class _RankPageState extends State @override Widget build(BuildContext context) { super.build(context); - Brightness currentBrightness = MediaQuery.of(context).platformBrightness; - // 设置状态栏图标的亮度 - if (_rankController.enableGradientBg) { - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); - } return Scaffold( extendBody: true, - extendBodyBehindAppBar: false, - appBar: _rankController.enableGradientBg - ? null - : AppBar(toolbarHeight: 0, elevation: 0), - body: Stack( + extendBodyBehindAppBar: true, + backgroundColor: Colors.transparent, + appBar: AppBar( + toolbarHeight: 0, + elevation: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), + body: Column( children: [ - // gradient background - if (_rankController.enableGradientBg) ...[ - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.9), - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), - ), + const CustomAppBar(), + if (_rankController.tabs.length > 1) ...[ + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: 42, + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _rankController.tabController, + tabs: [ + for (var i in _rankController.tabs) Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_rankController.initialIndex.value == value) { + _rankController.tabsCtrList[value].animateToTop(); + } + _rankController.initialIndex.value = value; + }, ), ), ), + ] else ...[ + const SizedBox(height: 6), ], - Column( - children: [ - const CustomAppBar(), - if (_rankController.tabs.length > 1) ...[ - const SizedBox(height: 4), - SizedBox( - width: double.infinity, - height: 42, - child: Align( - alignment: Alignment.center, - child: TabBar( - controller: _rankController.tabController, - tabs: [ - for (var i in _rankController.tabs) - Tab(text: i['label']) - ], - isScrollable: true, - dividerColor: Colors.transparent, - enableFeedback: true, - splashBorderRadius: BorderRadius.circular(10), - tabAlignment: TabAlignment.center, - onTap: (value) { - feedBack(); - if (_rankController.initialIndex.value == value) { - _rankController.tabsCtrList[value].animateToTop(); - } - _rankController.initialIndex.value = value; - }, - ), - ), - ), - ] else ...[ - const SizedBox(height: 6), - ], - Expanded( - child: TabBarView( - controller: _rankController.tabController, - children: _rankController.tabsPageList, - ), - ), - ], + Expanded( + child: TabBarView( + controller: _rankController.tabController, + children: _rankController.tabsPageList, + ), ), ], ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 02a0bbe1..beb9dca1 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; From c248d33109c053ecef5bdb895e6a3ea2c9d857af Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Jul 2024 11:01:09 +0800 Subject: [PATCH 187/438] mod --- lib/pages/dynamics/view.dart | 5 ----- lib/pages/main/view.dart | 13 +++++++------ lib/pages/media/view.dart | 11 +---------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 495eb770..0fc16dcf 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -78,14 +78,9 @@ class _DynamicsPageState extends State Widget build(BuildContext context) { super.build(context); return Scaffold( - backgroundColor: Colors.transparent, appBar: AppBar( elevation: 0, scrolledUnderElevation: 0, - backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, title: SizedBox( height: 34, child: Stack( diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 20a12d35..00eafa0d 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -123,27 +123,28 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { Align( alignment: Alignment.topLeft, child: Opacity( - opacity: 0.6, + opacity: Theme.of(context).brightness == Brightness.dark + ? 0.3 + : 0.6, child: Container( width: MediaQuery.of(context).size.width, - // height: MediaQuery.of(context).size.height, - height: MediaQuery.of(context).padding.top + 400, + height: MediaQuery.of(context).size.height, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Theme.of(context) .colorScheme .primary - .withOpacity(0.6), + .withOpacity(0.7), + Theme.of(context).colorScheme.surface, Theme.of(context) .colorScheme .surface .withOpacity(0.3), - Theme.of(context).colorScheme.surface ], begin: Alignment.topCenter, end: Alignment.bottomCenter, - stops: const [0.1, 0.8, 1]), + stops: const [0.1, 0.3, 5]), ), ), ), diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 3694fa68..eb6d139c 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -46,16 +46,7 @@ class _MediaPageState extends State super.build(context); Color primary = Theme.of(context).colorScheme.primary; return Scaffold( - backgroundColor: Colors.transparent, - appBar: AppBar( - elevation: 0, - scrolledUnderElevation: 0, - toolbarHeight: 30, - backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - ), + appBar: AppBar(toolbarHeight: 30), body: SingleChildScrollView( controller: mediaController.scrollController, child: Column( From aab51443717b4fb7bdf3b2284ee3bb7ecbbc3270 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Jul 2024 22:27:28 +0800 Subject: [PATCH 188/438] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95req=E5=AF=BC?= =?UTF-8?q?=E8=87=B4headerControl=E5=88=9D=E5=A7=8B=E5=8C=96=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 21 +++++++------------ .../video/detail/widgets/header_control.dart | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index dbdd9fce..662f45f2 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -152,17 +152,13 @@ class VideoDetailController extends GetxController defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); - getSubtitle().then( - (subtitles) { - headerControl = HeaderControl( - controller: plPlayerController, - videoDetailCtr: this, - floating: floating, - bvid: bvid, - videoType: videoType, - showSubtitleBtn: subtitles.isNotEmpty, - ); - }, + getSubtitle(); + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + bvid: bvid, + videoType: videoType, ); } @@ -439,9 +435,6 @@ class VideoDetailController extends GetxController subtitles = result['data'].subtitles; getDanmaku(subtitles); } - return subtitles; - } else { - return []; } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f3b9549a..b2bed21c 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1329,7 +1329,7 @@ class _HeaderControlState extends State { ], /// 字幕 - if (widget.showSubtitleBtn!) + if (widget.showSubtitleBtn ?? true) ComBtn( icon: const Icon( Icons.closed_caption_off, From 6f69212952d840a409cf153ff6a3f05c3a4b639f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Jul 2024 23:44:39 +0800 Subject: [PATCH 189/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=BC=82=E5=B8=B8=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 116 ++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index beb9dca1..a9353546 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1,16 +1,15 @@ import 'dart:async'; import 'dart:io'; -import 'dart:ui'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; @@ -29,6 +28,7 @@ import 'package:status_bar_control/status_bar_control.dart'; import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../services/shutdown_timer_service.dart'; import 'widgets/app_bar.dart'; +import 'widgets/header_control.dart'; class VideoDetailPage extends StatefulWidget { const VideoDetailPage({Key? key}) : super(key: key); @@ -494,45 +494,77 @@ class _VideoDetailPageState extends State exitFullScreen(); } + Widget buildLoadingWidget() { + return Center(child: Lottie.asset('assets/loading.json', width: 200)); + } + + Widget buildVideoPlayerWidget(AsyncSnapshot snapshot) { + return Obx(() => !vdCtr.autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + headerControl: vdCtr.headerControl, + danmuWidget: PlDanmaku( + key: Key(vdCtr.danmakuCid.value.toString()), + cid: vdCtr.danmakuCid.value, + playerController: plPlayerController!, + ), + bottomList: vdCtr.bottomList, + showEposideCb: () => vdCtr.videoType == SearchType.video + ? videoIntroController.showEposideHandler() + : bangumiIntroController.showEposideHandler(), + fullScreenCb: (bool status) { + videoHeight.value = + status ? Get.size.height : defaultVideoHeight; + }, + )); + } + + Widget buildErrorWidget(dynamic error) { + return Obx( + () => SizedBox( + height: videoHeight.value, + width: Get.size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text('加载失败', style: TextStyle(color: Colors.white)), + Text('$error', style: const TextStyle(color: Colors.white)), + const SizedBox(height: 10), + IconButton.filled( + onPressed: () { + setState(() { + _futureBuilderFuture = vdCtr.queryVideoUrl(); + }); + }, + icon: const Icon(Icons.refresh), + ) + ], + ), + ), + ); + } + /// 播放器面板 - Widget videoPlayerPanel = FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return Obx( - () { - return !vdCtr.autoPlay.value - ? const SizedBox() - : Obx( - () => PLVideoPlayer( - controller: plPlayerController!, - headerControl: vdCtr.headerControl, - danmuWidget: PlDanmaku( - key: Key(vdCtr.danmakuCid.value.toString()), - cid: vdCtr.danmakuCid.value, - playerController: plPlayerController!, - ), - bottomList: vdCtr.bottomList, - showEposideCb: () => vdCtr.videoType == SearchType.video - ? videoIntroController.showEposideHandler() - : bangumiIntroController.showEposideHandler(), - fullScreenCb: (bool status) { - if (status) { - videoHeight.value = Get.size.height; - } else { - videoHeight.value = defaultVideoHeight; - } - }, - ), - ); - }, - ); - } else { - // 加载失败异常处理 - return const SizedBox(); - } - }, - ); + Widget buildVideoPlayerPanel() { + return FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return buildLoadingWidget(); + } else if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData && snapshot.data['status']) { + return buildVideoPlayerWidget(snapshot); + } else { + return buildErrorWidget(snapshot.error); + } + } else { + return buildErrorWidget('未知错误'); + } + }, + ); + } Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && @@ -607,7 +639,7 @@ class _VideoDetailPageState extends State tag: heroTag, child: Stack( children: [ - if (isShowing) videoPlayerPanel, + if (isShowing) buildVideoPlayerPanel(), /// 关闭自动播放时 手动播放 Obx( @@ -717,7 +749,7 @@ class _VideoDetailPageState extends State if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: videoPlayerPanel, + childWhenEnabled: buildVideoPlayerPanel(), floating: floating, ); } else { From 189abbc747b2887a94d8fb57b45eb0a59e62344e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Jul 2024 23:58:55 +0800 Subject: [PATCH 190/438] =?UTF-8?q?fix:=20=E5=AF=BC=E8=88=AA=E6=A0=8F?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 14 +++++++++++--- lib/pages/rank/view.dart | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 0163723b..187ed6c3 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -54,9 +55,16 @@ class _HomePageState extends State toolbarHeight: 0, elevation: 0, backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + ) + : Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, ), body: Column( children: [ diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 218e60cf..cf7789a2 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -39,9 +40,16 @@ class _RankPageState extends State toolbarHeight: 0, elevation: 0, backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + ) + : Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, ), body: Column( children: [ From cc2595fd7b9246e97691569184bf03ed16e84850 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Jul 2024 16:47:44 +0800 Subject: [PATCH 191/438] =?UTF-8?q?opt:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/pull_to_refresh_header.dart | 130 ------ lib/pages/dynamics/widgets/content_panel.dart | 92 ++-- lib/pages/dynamics/widgets/pic_panel.dart | 68 ++- lib/pages/main/controller.dart | 1 + lib/pages/preview/controller.dart | 50 --- lib/pages/preview/index.dart | 4 - lib/pages/preview/view.dart | 290 ------------- .../detail/reply/widgets/reply_item.dart | 137 +++--- lib/pages/video/detail/view.dart | 11 + lib/plugin/pl_gallery/custom_dismissible.dart | 156 +++++++ lib/plugin/pl_gallery/hero_dialog_route.dart | 63 +++ lib/plugin/pl_gallery/index.dart | 6 + .../interactive_viewer_boundary.dart | 117 +++++ .../pl_gallery/interactiveviewer_gallery.dart | 399 ++++++++++++++++++ lib/router/app_pages.dart | 8 - pubspec.lock | 24 -- pubspec.yaml | 1 - 17 files changed, 958 insertions(+), 599 deletions(-) delete mode 100644 lib/common/widgets/pull_to_refresh_header.dart delete mode 100644 lib/pages/preview/controller.dart delete mode 100644 lib/pages/preview/index.dart delete mode 100644 lib/pages/preview/view.dart create mode 100644 lib/plugin/pl_gallery/custom_dismissible.dart create mode 100644 lib/plugin/pl_gallery/hero_dialog_route.dart create mode 100644 lib/plugin/pl_gallery/index.dart create mode 100644 lib/plugin/pl_gallery/interactive_viewer_boundary.dart create mode 100644 lib/plugin/pl_gallery/interactiveviewer_gallery.dart diff --git a/lib/common/widgets/pull_to_refresh_header.dart b/lib/common/widgets/pull_to_refresh_header.dart deleted file mode 100644 index 46db5138..00000000 --- a/lib/common/widgets/pull_to_refresh_header.dart +++ /dev/null @@ -1,130 +0,0 @@ -// ignore_for_file: depend_on_referenced_packages - -import 'dart:math'; -import 'dart:ui' as ui show Image; - -import 'package:extended_image/extended_image.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart'; - -double get maxDragOffset => 100; -double hideHeight = maxDragOffset / 2.3; -double refreshHeight = maxDragOffset / 1.5; - -class PullToRefreshHeader extends StatelessWidget { - const PullToRefreshHeader( - this.info, - this.lastRefreshTime, { - this.color, - super.key, - }); - - final PullToRefreshScrollNotificationInfo? info; - final DateTime? lastRefreshTime; - final Color? color; - - @override - Widget build(BuildContext context) { - final PullToRefreshScrollNotificationInfo? infos = info; - if (infos == null) { - return const SizedBox(); - } - String text = ''; - if (infos.mode == PullToRefreshIndicatorMode.armed) { - text = 'Release to refresh'; - } else if (infos.mode == PullToRefreshIndicatorMode.refresh || - infos.mode == PullToRefreshIndicatorMode.snap) { - text = 'Loading...'; - } else if (infos.mode == PullToRefreshIndicatorMode.done) { - text = 'Refresh completed.'; - } else if (infos.mode == PullToRefreshIndicatorMode.drag) { - text = 'Pull to refresh'; - } else if (infos.mode == PullToRefreshIndicatorMode.canceled) { - text = 'Cancel refresh'; - } - - final TextStyle ts = const TextStyle( - color: Colors.grey, - ).copyWith(fontSize: 14); - - final double dragOffset = info?.dragOffset ?? 0.0; - - final DateTime time = lastRefreshTime ?? DateTime.now(); - final double top = -hideHeight + dragOffset; - return Container( - height: dragOffset, - color: color ?? Colors.transparent, - // padding: EdgeInsets.only(top: dragOffset / 3), - // padding: EdgeInsets.only(bottom: 5.0), - child: Stack( - children: [ - Positioned( - left: 0.0, - right: 0.0, - top: top, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - alignment: Alignment.centerRight, - margin: const EdgeInsets.only(right: 12.0), - child: RefreshImage(top, null), - ), - ), - Column( - children: [ - Text(text, style: ts), - Text( - 'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}', - style: ts.copyWith(fontSize: 14), - ) - ], - ), - const Spacer(), - ], - ), - ) - ], - ), - ); - } -} - -class RefreshImage extends StatelessWidget { - const RefreshImage(this.top, Key? key) : super(key: key); - - final double top; - - @override - Widget build(BuildContext context) { - const double imageSize = 30; - return ExtendedImage.asset( - 'assets/flutterCandies_grey.png', - width: imageSize, - height: imageSize, - afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) { - final double imageHeight = image.height.toDouble(); - final double imageWidth = image.width.toDouble(); - final Size size = rect.size; - final double y = - (1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight; - - canvas.drawImageRect( - image, - Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y), - Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height, - size.width, (imageHeight - y) / imageHeight * size.height), - Paint() - ..colorFilter = - const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn) - ..isAntiAlias = false - ..filterQuality = FilterQuality.low, - ); - - //canvas.restore(); - }, - ); - } -} diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index e1beaeb2..28451dde 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -1,11 +1,11 @@ // 内容 +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/result.dart'; -import 'package:pilipala/pages/preview/index.dart'; - +import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'rich_node_panel.dart'; // ignore: must_be_immutable @@ -59,17 +59,15 @@ class _ContentState extends State { (pictureItem.height != null && pictureItem.width != null ? pictureItem.height! / pictureItem.width! : 1); - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: 0, imgList: picList); - }, - ); + return Hero( + tag: pictureItem.url!, + placeholderBuilder: + (BuildContext context, Size heroSize, Widget child) { + return child; }, - child: Container( + child: GestureDetector( + onTap: () => onPreviewImg(picList, 1, context), + child: Container( padding: const EdgeInsets.only(top: 4), constraints: BoxConstraints(maxHeight: maxHeight), width: box.maxWidth / 2, @@ -91,7 +89,9 @@ class _ContentState extends State { ) : const SizedBox(), ], - )), + ), + ), + ), ); }, ), @@ -102,26 +102,23 @@ class _ContentState extends State { List list = []; for (var i = 0; i < len; i++) { picList.add(pics[i].url!); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { double maxWidth = box.maxWidth.truncateToDouble(); - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); - }, - child: NetworkImgLayer( - src: pics[i].url, - width: maxWidth, - height: maxWidth, - origAspectRatio: - pics[i].width!.toInt() / pics[i].height!.toInt(), + return Hero( + tag: picList[i], + child: GestureDetector( + onTap: () => onPreviewImg(picList, i, context), + child: NetworkImgLayer( + src: pics[i].url, + width: maxWidth, + height: maxWidth, + origAspectRatio: + pics[i].width!.toInt() / pics[i].height!.toInt(), + ), ), ); }, @@ -163,6 +160,43 @@ class _ContentState extends State { ); } + void onPreviewImg(picList, initIndex, context) { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); + } + @override Widget build(BuildContext context) { TextStyle authorStyle = diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 4e94e6fd..783fe89b 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,9 +1,47 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/pages/preview/index.dart'; +import 'package:pilipala/plugin/pl_gallery/index.dart'; + +void onPreviewImg(currentUrl, picList, initIndex, context) { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); +} Widget picWidget(item, context) { String type = item.modules.moduleDynamic.major.type; @@ -21,25 +59,25 @@ Widget picWidget(item, context) { List list = []; for (var i = 0; i < len; i++) { picList.add(pictures[i].src ?? pictures[i].url); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); + return Hero( + tag: picList[i], + placeholderBuilder: + (BuildContext context, Size heroSize, Widget child) { + return child; }, - child: NetworkImgLayer( - src: pictures[i].src ?? pictures[i].url, - width: box.maxWidth, - height: box.maxWidth, + child: GestureDetector( + onTap: () => onPreviewImg(picList[i], picList, i, context), + child: NetworkImgLayer( + src: pictures[i].src ?? pictures[i].url, + width: box.maxWidth, + height: box.maxWidth, + ), ), - // ), ); }, ), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 849e16d5..ad2a7781 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -27,6 +27,7 @@ class MainController extends GetxController { RxBool userLogin = false.obs; late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; late bool enableGradientBg; + bool imgPreviewStatus = false; @override void onInit() { diff --git a/lib/pages/preview/controller.dart b/lib/pages/preview/controller.dart deleted file mode 100644 index bb06b275..00000000 --- a/lib/pages/preview/controller.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:dio/dio.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:share_plus/share_plus.dart'; - -class PreviewController extends GetxController { - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - RxInt initialPage = 0.obs; - RxInt currentPage = 1.obs; - RxList imgList = [].obs; - bool storage = true; - bool videos = true; - bool photos = true; - String currentImgUrl = ''; - - requestPermission() async { - Map statuses = await [ - Permission.storage, - // Permission.photos - ].request(); - - statuses[Permission.storage].toString(); - // final photosInfo = statuses[Permission.photos].toString(); - } - - // 图片分享 - void onShareImg() async { - SmartDialog.showLoading(); - var response = await Dio().get(imgList[initialPage.value], - options: Options(responseType: ResponseType.bytes)); - final temp = await getTemporaryDirectory(); - SmartDialog.dismiss(); - String imgName = - "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; - var path = '${temp.path}/$imgName'; - File(path).writeAsBytesSync(response.data); - Share.shareXFiles([XFile(path)], subject: imgList[initialPage.value]); - } - - void onChange(int index) { - initialPage.value = index; - currentPage.value = index + 1; - currentImgUrl = imgList[index]; - } -} diff --git a/lib/pages/preview/index.dart b/lib/pages/preview/index.dart deleted file mode 100644 index 9fb82e8d..00000000 --- a/lib/pages/preview/index.dart +++ /dev/null @@ -1,4 +0,0 @@ -library preview; - -export './controller.dart'; -export './view.dart'; diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart deleted file mode 100644 index 13868d37..00000000 --- a/lib/pages/preview/view.dart +++ /dev/null @@ -1,290 +0,0 @@ -// ignore_for_file: library_private_types_in_public_api - -import 'dart:io'; - -import 'package:dismissible_page/dismissible_page.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:flutter/material.dart'; -import 'package:extended_image/extended_image.dart'; -import 'package:pilipala/utils/download.dart'; -import 'controller.dart'; -import 'package:status_bar_control/status_bar_control.dart'; - -typedef DoubleClickAnimationListener = void Function(); - -class ImagePreview extends StatefulWidget { - final int? initialPage; - final List? imgList; - const ImagePreview({ - Key? key, - this.initialPage, - this.imgList, - }) : super(key: key); - - @override - _ImagePreviewState createState() => _ImagePreviewState(); -} - -class _ImagePreviewState extends State - with TickerProviderStateMixin { - final PreviewController _previewController = Get.put(PreviewController()); - // late AnimationController animationController; - late AnimationController _doubleClickAnimationController; - Animation? _doubleClickAnimation; - late DoubleClickAnimationListener _doubleClickAnimationListener; - List doubleTapScales = [1.0, 2.0]; - bool _dismissDisabled = false; - - @override - void initState() { - super.initState(); - - _previewController.initialPage.value = widget.initialPage!; - _previewController.currentPage.value = widget.initialPage! + 1; - _previewController.imgList.value = widget.imgList!; - _previewController.currentImgUrl = widget.imgList![widget.initialPage!]; - // animationController = AnimationController( - // vsync: this, duration: const Duration(milliseconds: 400)); - setStatusBar(); - _doubleClickAnimationController = AnimationController( - duration: const Duration(milliseconds: 250), vsync: this); - } - - onOpenMenu() { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - _previewController.onShareImg(); - Get.back(); - }, - dense: true, - title: const Text('分享', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () { - Clipboard.setData( - ClipboardData(text: _previewController.currentImgUrl)) - .then((value) { - Get.back(); - SmartDialog.showToast('已复制到粘贴板'); - }).catchError((err) { - SmartDialog.showNotify( - msg: err.toString(), - notifyType: NotifyType.error, - ); - }); - }, - dense: true, - title: const Text('复制链接', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () { - Get.back(); - DownloadUtils.downloadImg(_previewController.currentImgUrl); - }, - dense: true, - title: const Text('保存到手机', style: TextStyle(fontSize: 14)), - ), - ], - ), - ); - }, - ); - } - - // 隐藏状态栏,避免遮挡图片内容 - setStatusBar() async { - if (Platform.isIOS || Platform.isAndroid) { - await StatusBarControl.setHidden(true, - animation: StatusBarAnimation.SLIDE); - } - } - - @override - void dispose() { - // animationController.dispose(); - try { - StatusBarControl.setHidden(false, animation: StatusBarAnimation.SLIDE); - } catch (_) {} - _doubleClickAnimationController.dispose(); - clearGestureDetailsCache(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - primary: false, - extendBody: true, - appBar: AppBar( - primary: false, - toolbarHeight: 0, - backgroundColor: Colors.black, - systemOverlayStyle: SystemUiOverlayStyle.dark, - ), - body: Stack( - children: [ - GestureDetector( - onLongPress: () => onOpenMenu(), - child: ExtendedImageGesturePageView.builder( - controller: ExtendedPageController( - initialPage: _previewController.initialPage.value, - pageSpacing: 0, - ), - onPageChanged: (int index) => _previewController.onChange(index), - canScrollPage: (GestureDetails? gestureDetails) => - gestureDetails!.totalScale! <= 1.0, - itemCount: widget.imgList!.length, - itemBuilder: (BuildContext context, int index) { - return ExtendedImage.network( - widget.imgList![index], - fit: BoxFit.contain, - mode: ExtendedImageMode.gesture, - onDoubleTap: (ExtendedImageGestureState state) { - final Offset? pointerDownPosition = - state.pointerDownPosition; - final double? begin = state.gestureDetails!.totalScale; - double end; - - //remove old - _doubleClickAnimation - ?.removeListener(_doubleClickAnimationListener); - - //stop pre - _doubleClickAnimationController.stop(); - - //reset to use - _doubleClickAnimationController.reset(); - - if (begin == doubleTapScales[0]) { - setState(() { - _dismissDisabled = true; - }); - end = doubleTapScales[1]; - } else { - setState(() { - _dismissDisabled = false; - }); - end = doubleTapScales[0]; - } - - _doubleClickAnimationListener = () { - state.handleDoubleTap( - scale: _doubleClickAnimation!.value, - doubleTapPosition: pointerDownPosition); - }; - _doubleClickAnimation = _doubleClickAnimationController - .drive(Tween(begin: begin, end: end)); - - _doubleClickAnimation! - .addListener(_doubleClickAnimationListener); - - _doubleClickAnimationController.forward(); - }, - // ignore: body_might_complete_normally_nullable - loadStateChanged: (ExtendedImageState state) { - if (state.extendedImageLoadState == LoadState.loading) { - final ImageChunkEvent? loadingProgress = - state.loadingProgress; - final double? progress = - loadingProgress?.expectedTotalBytes != null - ? loadingProgress!.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null; - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 150.0, - child: LinearProgressIndicator( - value: progress, - color: Colors.white, - ), - ), - // const SizedBox(height: 10.0), - // Text('${((progress ?? 0.0) * 100).toInt()}%',), - ], - ), - ); - } - }, - initGestureConfigHandler: (ExtendedImageState state) { - return GestureConfig( - inPageView: true, - initialScale: 1.0, - maxScale: 5.0, - animationMaxScale: 6.0, - initialAlignment: InitialAlignment.center, - ); - }, - ); - }, - ), - ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Container( - padding: EdgeInsets.only( - left: 20, - right: 20, - bottom: MediaQuery.of(context).padding.bottom + 30), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black87, - ], - tileMode: TileMode.mirror, - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - widget.imgList!.length > 1 - ? Obx( - () => Text.rich( - textAlign: TextAlign.center, - TextSpan( - style: const TextStyle( - color: Colors.white, fontSize: 16), - children: [ - TextSpan( - text: _previewController.currentPage - .toString()), - const TextSpan(text: ' / '), - TextSpan( - text: - widget.imgList!.length.toString()), - ]), - ), - ) - : const SizedBox(), - IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.close, color: Colors.white), - ), - ], - )), - ), - ], - ), - ); - } -} diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index d8f696d4..c4c26e6b 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,4 +1,5 @@ import 'package:appscheme/appscheme.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -9,9 +10,10 @@ import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; -import 'package:pilipala/pages/preview/index.dart'; +import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; +import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; @@ -540,6 +542,53 @@ InlineSpan buildContent( ); } + void onPreviewImg(picList, initIndex) { + final MainController mainController = Get.find(); + mainController.imgPreviewStatus = true; + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + final MainController mainController = + Get.find(); + mainController.imgPreviewStatus = false; + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + onDismissed: (int value) { + print('onDismissed'); + final MainController mainController = Get.find(); + mainController.imgPreviewStatus = false; + }, + ), + ), + ); + } + // 分割文本并处理每个部分 content.message.splitMapJoin( pattern, @@ -831,38 +880,33 @@ InlineSpan buildContent( .truncateToDouble(); } catch (_) {} - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (BuildContext context) { - return ImagePreview(initialPage: 0, imgList: picList); - }, - ); - }, - child: Container( - padding: const EdgeInsets.only(top: 4), - constraints: BoxConstraints(maxHeight: maxHeight), - width: box.maxWidth / 2, - height: height, - child: Stack( - children: [ - Positioned.fill( - child: NetworkImgLayer( - src: pictureItem['img_src'], - width: box.maxWidth / 2, - height: height, + return Hero( + tag: picList[0], + child: GestureDetector( + onTap: () => onPreviewImg(picList, 0), + child: Container( + padding: const EdgeInsets.only(top: 4), + constraints: BoxConstraints(maxHeight: maxHeight), + width: box.maxWidth / 2, + height: height, + child: Stack( + children: [ + Positioned.fill( + child: NetworkImgLayer( + src: picList[0], + width: box.maxWidth / 2, + height: height, + ), ), - ), - height > Get.size.height * 0.9 - ? const PBadge( - text: '长图', - right: 8, - bottom: 8, - ) - : const SizedBox(), - ], + height > Get.size.height * 0.9 + ? const PBadge( + text: '长图', + right: 8, + bottom: 8, + ) + : const SizedBox(), + ], + ), ), ), ); @@ -874,25 +918,22 @@ InlineSpan buildContent( List list = []; for (var i = 0; i < len; i++) { picList.add(content.pictures[i]['img_src']); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); - }, - child: NetworkImgLayer( - src: content.pictures[i]['img_src'], - width: box.maxWidth, - height: box.maxWidth, - origAspectRatio: content.pictures[i]['img_width'] / - content.pictures[i]['img_height']), + return Hero( + tag: picList[i], + child: GestureDetector( + onTap: () => onPreviewImg(picList, i), + child: NetworkImgLayer( + src: picList[i], + width: box.maxWidth, + height: box.maxWidth, + origAspectRatio: content.pictures[i]['img_width'] / + content.pictures[i]['img_height']), + ), ); }, ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a9353546..331986dd 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/pages/bangumi/introduction/index.dart'; import 'package:pilipala/pages/danmaku/view.dart'; +import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; @@ -240,6 +241,11 @@ class _VideoDetailPageState extends State @override // 离开当前页面时 void didPushNext() async { + final MainController mainController = Get.find(); + if (mainController.imgPreviewStatus) { + return; + } + /// 开启 if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false) as bool) { @@ -259,6 +265,11 @@ class _VideoDetailPageState extends State @override // 返回当前页面时 void didPopNext() async { + final MainController mainController = Get.find(); + if (mainController.imgPreviewStatus) { + return; + } + if (plPlayerController != null && plPlayerController!.videoPlayerController != null) { setState(() { diff --git a/lib/plugin/pl_gallery/custom_dismissible.dart b/lib/plugin/pl_gallery/custom_dismissible.dart new file mode 100644 index 00000000..05761cac --- /dev/null +++ b/lib/plugin/pl_gallery/custom_dismissible.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; + +/// A widget used to dismiss its [child]. +/// +/// Similar to [Dismissible] with some adjustments. +class CustomDismissible extends StatefulWidget { + const CustomDismissible({ + required this.child, + this.onDismissed, + this.dismissThreshold = 0.2, + this.enabled = true, + Key? key, + }) : super(key: key); + + final Widget child; + final double dismissThreshold; + final VoidCallback? onDismissed; + final bool enabled; + + @override + State createState() => _CustomDismissibleState(); +} + +class _CustomDismissibleState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animateController; + late Animation _moveAnimation; + late Animation _scaleAnimation; + late Animation _opacityAnimation; + + double _dragExtent = 0; + bool _dragUnderway = false; + + bool get _isActive => _dragUnderway || _animateController.isAnimating; + + @override + void initState() { + super.initState(); + + _animateController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + _updateMoveAnimation(); + } + + @override + void dispose() { + _animateController.dispose(); + + super.dispose(); + } + + void _updateMoveAnimation() { + final double end = _dragExtent.sign; + + _moveAnimation = _animateController.drive( + Tween( + begin: Offset.zero, + end: Offset(0, end), + ), + ); + + _scaleAnimation = _animateController.drive(Tween( + begin: 1, + end: 0.5, + )); + + _opacityAnimation = DecorationTween( + begin: const BoxDecoration(color: Color(0xFF000000)), + end: const BoxDecoration(color: Color(0x00000000)), + ).animate(_animateController); + } + + void _handleDragStart(DragStartDetails details) { + _dragUnderway = true; + + if (_animateController.isAnimating) { + _dragExtent = + _animateController.value * context.size!.height * _dragExtent.sign; + _animateController.stop(); + } else { + _dragExtent = 0.0; + _animateController.value = 0.0; + } + setState(_updateMoveAnimation); + } + + void _handleDragUpdate(DragUpdateDetails details) { + if (!_isActive || _animateController.isAnimating) { + return; + } + + final double delta = details.primaryDelta!; + final double oldDragExtent = _dragExtent; + + if (_dragExtent + delta < 0) { + _dragExtent += delta; + } else if (_dragExtent + delta > 0) { + _dragExtent += delta; + } + + if (oldDragExtent.sign != _dragExtent.sign) { + setState(_updateMoveAnimation); + } + + if (!_animateController.isAnimating) { + _animateController.value = _dragExtent.abs() / context.size!.height; + } + } + + void _handleDragEnd(DragEndDetails details) { + if (!_isActive || _animateController.isAnimating) { + return; + } + + _dragUnderway = false; + + if (_animateController.isCompleted) { + return; + } + + if (!_animateController.isDismissed) { + // if the dragged value exceeded the dismissThreshold, call onDismissed + // else animate back to initial position. + if (_animateController.value > widget.dismissThreshold) { + widget.onDismissed?.call(); + } else { + _animateController.reverse(); + } + } + } + + @override + Widget build(BuildContext context) { + final Widget content = DecoratedBoxTransition( + decoration: _opacityAnimation, + child: SlideTransition( + position: _moveAnimation, + child: ScaleTransition( + scale: _scaleAnimation, + child: widget.child, + ), + ), + ); + + return GestureDetector( + behavior: HitTestBehavior.translucent, + onVerticalDragStart: widget.enabled ? _handleDragStart : null, + onVerticalDragUpdate: widget.enabled ? _handleDragUpdate : null, + onVerticalDragEnd: widget.enabled ? _handleDragEnd : null, + child: content, + ); + } +} diff --git a/lib/plugin/pl_gallery/hero_dialog_route.dart b/lib/plugin/pl_gallery/hero_dialog_route.dart new file mode 100644 index 00000000..d9f129d2 --- /dev/null +++ b/lib/plugin/pl_gallery/hero_dialog_route.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +/// A [PageRoute] with a semi transparent background. +/// +/// Similar to calling [showDialog] except it can be used with a [Navigator] to +/// show a [Hero] animation. +class HeroDialogRoute extends PageRoute { + HeroDialogRoute({ + required this.builder, + this.onBackgroundTap, + }) : super(); + + final WidgetBuilder builder; + + /// Called when the background is tapped. + final VoidCallback? onBackgroundTap; + + @override + bool get opaque => false; + + @override + bool get barrierDismissible => true; + + @override + String? get barrierLabel => null; + + @override + Duration get transitionDuration => const Duration(milliseconds: 300); + + @override + bool get maintainState => true; + + @override + Color? get barrierColor => null; + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return FadeTransition( + opacity: CurvedAnimation(parent: animation, curve: Curves.easeOut), + child: child, + ); + } + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + final Widget child = builder(context); + final Widget result = Semantics( + scopesRoute: true, + explicitChildNodes: true, + child: child, + ); + return result; + } +} diff --git a/lib/plugin/pl_gallery/index.dart b/lib/plugin/pl_gallery/index.dart new file mode 100644 index 00000000..5a6f04e1 --- /dev/null +++ b/lib/plugin/pl_gallery/index.dart @@ -0,0 +1,6 @@ +library pl_gallery; + +export './hero_dialog_route.dart'; +export './custom_dismissible.dart'; +export './interactiveviewer_gallery.dart'; +export './interactive_viewer_boundary.dart'; diff --git a/lib/plugin/pl_gallery/interactive_viewer_boundary.dart b/lib/plugin/pl_gallery/interactive_viewer_boundary.dart new file mode 100644 index 00000000..929b7a6d --- /dev/null +++ b/lib/plugin/pl_gallery/interactive_viewer_boundary.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +/// A callback for the [InteractiveViewerBoundary] that is called when the scale +/// changed. +typedef ScaleChanged = void Function(double scale); + +/// Builds an [InteractiveViewer] and provides callbacks that are called when a +/// horizontal boundary has been hit. +/// +/// The callbacks are called when an interaction ends by listening to the +/// [InteractiveViewer.onInteractionEnd] callback. +class InteractiveViewerBoundary extends StatefulWidget { + const InteractiveViewerBoundary({ + required this.child, + required this.boundaryWidth, + this.controller, + this.onScaleChanged, + this.onLeftBoundaryHit, + this.onRightBoundaryHit, + this.onNoBoundaryHit, + this.maxScale, + this.minScale, + Key? key, + }) : super(key: key); + + final Widget child; + + /// The max width this widget can have. + /// + /// If the [InteractiveViewer] can take up the entire screen width, this + /// should be set to `MediaQuery.of(context).size.width`. + final double boundaryWidth; + + /// The [TransformationController] for the [InteractiveViewer]. + final TransformationController? controller; + + /// Called when the scale changed after an interaction ended. + final ScaleChanged? onScaleChanged; + + /// Called when the left boundary has been hit after an interaction ended. + final VoidCallback? onLeftBoundaryHit; + + /// Called when the right boundary has been hit after an interaction ended. + final VoidCallback? onRightBoundaryHit; + + /// Called when no boundary has been hit after an interaction ended. + final VoidCallback? onNoBoundaryHit; + + final double? maxScale; + + final double? minScale; + + @override + InteractiveViewerBoundaryState createState() => + InteractiveViewerBoundaryState(); +} + +class InteractiveViewerBoundaryState extends State { + TransformationController? _controller; + + double? _scale; + + @override + void initState() { + super.initState(); + + _controller = widget.controller ?? TransformationController(); + } + + @override + void dispose() { + _controller!.dispose(); + + super.dispose(); + } + + void _updateBoundaryDetection() { + final double scale = _controller!.value.row0[0]; + + if (_scale != scale) { + // the scale changed + _scale = scale; + widget.onScaleChanged?.call(scale); + } + + if (scale <= 1.01) { + // cant hit any boundaries when the child is not scaled + return; + } + + final double xOffset = _controller!.value.row0[3]; + final double boundaryWidth = widget.boundaryWidth; + final double boundaryEnd = boundaryWidth * scale; + final double xPos = boundaryEnd + xOffset; + + if (boundaryEnd.round() == xPos.round()) { + // left boundary hit + widget.onLeftBoundaryHit?.call(); + } else if (boundaryWidth.round() == xPos.round()) { + // right boundary hit + widget.onRightBoundaryHit?.call(); + } else { + widget.onNoBoundaryHit?.call(); + } + } + + @override + Widget build(BuildContext context) { + return InteractiveViewer( + maxScale: widget.maxScale!, + minScale: widget.minScale!, + transformationController: _controller, + onInteractionEnd: (_) => _updateBoundaryDetection(), + child: widget.child, + ); + } +} diff --git a/lib/plugin/pl_gallery/interactiveviewer_gallery.dart b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart new file mode 100644 index 00000000..03ff4642 --- /dev/null +++ b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart @@ -0,0 +1,399 @@ +library interactiveviewer_gallery; + +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pilipala/utils/download.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:status_bar_control/status_bar_control.dart'; +import 'custom_dismissible.dart'; +import 'interactive_viewer_boundary.dart'; + +/// Builds a carousel controlled by a [PageView] for the tweet media sources. +/// +/// Used for showing a full screen view of the [TweetMedia] sources. +/// +/// The sources can be panned and zoomed interactively using an +/// [InteractiveViewer]. +/// An [InteractiveViewerBoundary] is used to detect when the boundary of the +/// source is hit after zooming in to disable or enable the swiping gesture of +/// the [PageView]. +/// +typedef IndexedFocusedWidgetBuilder = Widget Function( + BuildContext context, int index, bool isFocus, bool enablePageView); + +typedef IndexedTagStringBuilder = String Function(int index); + +class InteractiveviewerGallery extends StatefulWidget { + const InteractiveviewerGallery({ + required this.sources, + required this.initIndex, + required this.itemBuilder, + this.maxScale = 4.5, + this.minScale = 1.0, + this.onPageChanged, + this.onDismissed, + Key? key, + }) : super(key: key); + + /// The sources to show. + final List sources; + + /// The index of the first source in [sources] to show. + final int initIndex; + + /// The item content + final IndexedFocusedWidgetBuilder itemBuilder; + + final double maxScale; + + final double minScale; + + final ValueChanged? onPageChanged; + + final ValueChanged? onDismissed; + + @override + State createState() => + _InteractiveviewerGalleryState(); +} + +class _InteractiveviewerGalleryState extends State + with SingleTickerProviderStateMixin { + PageController? _pageController; + TransformationController? _transformationController; + + /// The controller to animate the transformation value of the + /// [InteractiveViewer] when it should reset. + late AnimationController _animationController; + Animation? _animation; + + /// `true` when an source is zoomed in and not at the at a horizontal boundary + /// to disable the [PageView]. + bool _enablePageView = true; + + /// `true` when an source is zoomed in to disable the [CustomDismissible]. + bool _enableDismiss = true; + + late Offset _doubleTapLocalPosition; + + int? currentIndex; + + @override + void initState() { + super.initState(); + + _pageController = PageController(initialPage: widget.initIndex); + + _transformationController = TransformationController(); + + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + ) + ..addListener(() { + _transformationController!.value = + _animation?.value ?? Matrix4.identity(); + }) + ..addStatusListener((AnimationStatus status) { + if (status == AnimationStatus.completed && !_enableDismiss) { + setState(() { + _enableDismiss = true; + }); + } + }); + + currentIndex = widget.initIndex; + setStatusBar(); + } + + setStatusBar() async { + if (Platform.isIOS || Platform.isAndroid) { + await StatusBarControl.setHidden(true, + animation: StatusBarAnimation.FADE); + } + } + + @override + void dispose() { + _pageController!.dispose(); + _animationController.dispose(); + try { + StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); + } catch (_) {} + super.dispose(); + } + + /// When the source gets scaled up, the swipe up / down to dismiss gets + /// disabled. + /// + /// When the scale resets, the dismiss and the page view swiping gets enabled. + void _onScaleChanged(double scale) { + final bool initialScale = scale <= widget.minScale; + + if (initialScale) { + if (!_enableDismiss) { + setState(() { + _enableDismiss = true; + }); + } + + if (!_enablePageView) { + setState(() { + _enablePageView = true; + }); + } + } else { + if (_enableDismiss) { + setState(() { + _enableDismiss = false; + }); + } + + if (_enablePageView) { + setState(() { + _enablePageView = false; + }); + } + } + } + + /// When the left boundary has been hit after scaling up the source, the page + /// view swiping gets enabled if it has a page to swipe to. + void _onLeftBoundaryHit() { + if (!_enablePageView && _pageController!.page!.floor() > 0) { + setState(() { + _enablePageView = true; + }); + } + } + + /// When the right boundary has been hit after scaling up the source, the page + /// view swiping gets enabled if it has a page to swipe to. + void _onRightBoundaryHit() { + if (!_enablePageView && + _pageController!.page!.floor() < widget.sources.length - 1) { + setState(() { + _enablePageView = true; + }); + } + } + + /// When the source has been scaled up and no horizontal boundary has been hit, + /// the page view swiping gets disabled. + void _onNoBoundaryHit() { + if (_enablePageView) { + setState(() { + _enablePageView = false; + }); + } + } + + /// When the page view changed its page, the source will animate back into the + /// original scale if it was scaled up. + /// + /// Additionally the swipe up / down to dismiss gets enabled. + void _onPageChanged(int page) { + setState(() { + currentIndex = page; + }); + widget.onPageChanged?.call(page); + if (_transformationController!.value != Matrix4.identity()) { + // animate the reset for the transformation of the interactive viewer + + _animation = Matrix4Tween( + begin: _transformationController!.value, + end: Matrix4.identity(), + ).animate( + CurveTween(curve: Curves.easeOut).animate(_animationController), + ); + + _animationController.forward(from: 0); + } + } + + @override + Widget build(BuildContext context) { + return InteractiveViewerBoundary( + controller: _transformationController, + boundaryWidth: MediaQuery.of(context).size.width, + onScaleChanged: _onScaleChanged, + onLeftBoundaryHit: _onLeftBoundaryHit, + onRightBoundaryHit: _onRightBoundaryHit, + onNoBoundaryHit: _onNoBoundaryHit, + maxScale: widget.maxScale, + minScale: widget.minScale, + child: Stack(children: [ + CustomDismissible( + onDismissed: () { + Navigator.of(context).pop(); + widget.onDismissed?.call(_pageController!.page!.floor()); + }, + enabled: _enableDismiss, + child: PageView.builder( + onPageChanged: _onPageChanged, + controller: _pageController, + physics: + _enablePageView ? null : const NeverScrollableScrollPhysics(), + itemCount: widget.sources.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onDoubleTapDown: (TapDownDetails details) { + _doubleTapLocalPosition = details.localPosition; + }, + onDoubleTap: onDoubleTap, + child: widget.itemBuilder( + context, + index, + index == currentIndex, + _enablePageView, + ), + ); + }, + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: EdgeInsets.fromLTRB( + 12, 8, 20, MediaQuery.of(context).padding.bottom + 8), + decoration: _enablePageView + ? BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.3) + ], + ), + ) + : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + Navigator.of(context).pop(); + widget.onDismissed?.call(_pageController!.page!.floor()); + }, + ), + widget.sources.length > 1 + ? Text( + "${currentIndex! + 1}/${widget.sources.length}", + style: const TextStyle(color: Colors.white), + ) + : const SizedBox(), + PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + value: 0, + onTap: () => onShareImg(widget.sources[currentIndex!]), + child: const Text("分享图片"), + ), + PopupMenuItem( + value: 1, + onTap: () { + Clipboard.setData(ClipboardData( + text: + widget.sources[currentIndex!].toString())) + .then((value) { + SmartDialog.showToast('已复制到粘贴板'); + }).catchError((err) { + SmartDialog.showNotify( + msg: err.toString(), + notifyType: NotifyType.error, + ); + }); + }, + child: const Text("复制图片"), + ), + PopupMenuItem( + value: 2, + onTap: () { + DownloadUtils.downloadImg( + widget.sources[currentIndex!]); + }, + child: const Text("保存图片"), + ), + ]; + }, + child: const Icon(Icons.more_horiz, color: Colors.white), + ), + ], + ), + ), + ), + ]), + ); + } + + // 图片分享 + void onShareImg(String imgUrl) async { + SmartDialog.showLoading(); + var response = await Dio() + .get(imgUrl, options: Options(responseType: ResponseType.bytes)); + final temp = await getTemporaryDirectory(); + SmartDialog.dismiss(); + String imgName = + "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; + var path = '${temp.path}/$imgName'; + File(path).writeAsBytesSync(response.data); + Share.shareXFiles([XFile(path)], subject: imgUrl); + } + + onDoubleTap() { + Matrix4 matrix = _transformationController!.value.clone(); + double currentScale = matrix.row0.x; + + double targetScale = widget.minScale; + + if (currentScale <= widget.minScale) { + targetScale = widget.maxScale * 0.7; + } + + double offSetX = targetScale == 1.0 + ? 0.0 + : -_doubleTapLocalPosition.dx * (targetScale - 1); + double offSetY = targetScale == 1.0 + ? 0.0 + : -_doubleTapLocalPosition.dy * (targetScale - 1); + + matrix = Matrix4.fromList([ + targetScale, + matrix.row1.x, + matrix.row2.x, + matrix.row3.x, + matrix.row0.y, + targetScale, + matrix.row2.y, + matrix.row3.y, + matrix.row0.z, + matrix.row1.z, + targetScale, + matrix.row3.z, + offSetX, + offSetY, + matrix.row2.w, + matrix.row3.w + ]); + + _animation = Matrix4Tween( + begin: _transformationController!.value, + end: matrix, + ).animate( + CurveTween(curve: Curves.easeOut).animate(_animationController), + ); + _animationController + .forward(from: 0) + .whenComplete(() => _onScaleChanged(targetScale)); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index a6b48f0d..7840c126 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -70,14 +70,6 @@ class Routes { CustomGetPage(name: '/hot', page: () => const HotPage()), // 视频详情 CustomGetPage(name: '/video', page: () => const VideoDetailPage()), - // 图片预览 - // GetPage( - // name: '/preview', - // page: () => const ImagePreview(), - // transition: Transition.fade, - // transitionDuration: const Duration(milliseconds: 300), - // showCupertinoParallax: false, - // ), // CustomGetPage(name: '/webview', page: () => const WebviewPage()), // 设置 diff --git a/pubspec.lock b/pubspec.lock index c73ce395..a46127f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -441,22 +441,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.1" - extended_image: - dependency: "direct main" - description: - name: extended_image - sha256: d7f091d068fcac7246c4b22a84b8dac59a62e04d29a5c172710c696e67a22f94 - url: "https://pub.flutter-io.cn" - source: hosted - version: "8.2.0" - extended_image_library: - dependency: transitive - description: - name: extended_image_library - sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6 - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.0.4" extended_list: dependency: transitive description: @@ -726,14 +710,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" - http_client_helper: - dependency: transitive - description: - name: http_client_helper - sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.0.0" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 09169473..b5d45714 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,7 +49,6 @@ dependencies: # 图片 cached_network_image: ^3.3.0 - extended_image: ^8.2.0 saver_gallery: ^3.0.1 # 存储 From b489a45481915b37452b2d9f7bce7e4f5d0f44ee Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Jul 2024 00:41:59 +0800 Subject: [PATCH 192/438] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E3=80=81=E6=94=B6=E5=88=B0=E7=9A=84=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/media/view.dart | 8 +++++--- lib/pages/message/like/view.dart | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 3694fa68..420d7c44 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -245,9 +245,11 @@ class FavFolderItem extends StatelessWidget { return Container( margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), child: GestureDetector( - onTap: () => Get.toNamed('/favDetail', - arguments: item, - parameters: {'mediaId': item!.id.toString(), 'heroTag': heroTag}), + onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { + 'mediaId': item!.id.toString(), + 'heroTag': heroTag, + 'isOwner': '1', + }), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index e677fb25..80b20117 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -209,7 +209,7 @@ class LikeItem extends StatelessWidget { style: TextStyle(color: outline), ), TextSpan( - text: '赞了我的评论', + text: '赞了我的${item.item!.business}', style: TextStyle(color: outline), ), ])), From 04bdf5c2934316b8ff2dce20a0a418700e9ba7c0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Jul 2024 21:47:37 +0800 Subject: [PATCH 193/438] =?UTF-8?q?feat:=20=E8=BD=AE=E6=92=AD=E6=9F=A5?= =?UTF-8?q?=E7=9C=8Bup=E5=8A=A8=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/controller.dart | 10 +- lib/pages/dynamics/up_dynamic/controller.dart | 46 +++++ lib/pages/dynamics/up_dynamic/index.dart | 4 + .../dynamics/up_dynamic/route_panel.dart | 151 +++++++++++++++ lib/pages/dynamics/up_dynamic/view.dart | 178 ++++++++++++++++++ lib/pages/dynamics/view.dart | 19 +- lib/pages/dynamics/widgets/up_panel.dart | 49 ++--- lib/plugin/pl_popup/index.dart | 43 +++++ 8 files changed, 472 insertions(+), 28 deletions(-) create mode 100644 lib/pages/dynamics/up_dynamic/controller.dart create mode 100644 lib/pages/dynamics/up_dynamic/index.dart create mode 100644 lib/pages/dynamics/up_dynamic/route_panel.dart create mode 100644 lib/pages/dynamics/up_dynamic/view.dart create mode 100644 lib/plugin/pl_popup/index.dart diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 9bed3685..1cf9db9f 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -6,9 +6,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/http/search.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/dynamics_type.dart'; -import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; @@ -16,7 +14,6 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:pilipala/utils/utils.dart'; class DynamicsController extends GetxController { int page = 1; @@ -282,4 +279,11 @@ class DynamicsController extends GetxController { dynamicsList.value = []; queryFollowDynamic(); } + + // 点击up主 + void onTapUp(data) { + mid.value = data.mid; + upInfo.value = data; + onSelectUp(data.mid); + } } diff --git a/lib/pages/dynamics/up_dynamic/controller.dart b/lib/pages/dynamics/up_dynamic/controller.dart new file mode 100644 index 00000000..a30a0148 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/controller.dart @@ -0,0 +1,46 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/dynamics.dart'; +import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/models/dynamics/up.dart'; + +class UpDynamicsController extends GetxController { + UpDynamicsController(this.upInfo); + UpItem upInfo; + RxList dynamicsList = [].obs; + RxBool isLoadingDynamic = false.obs; + String? offset = ''; + int page = 1; + + Future queryFollowDynamic({type = 'init'}) async { + if (type == 'init') { + dynamicsList.clear(); + } + // 下拉刷新数据渲染时会触发onLoad + if (type == 'onLoad' && page == 1) { + return; + } + isLoadingDynamic.value = true; + var res = await DynamicsHttp.followDynamic( + page: type == 'init' ? 1 : page, + type: 'all', + offset: offset, + mid: upInfo.mid, + ); + isLoadingDynamic.value = false; + if (res['status']) { + if (type == 'onLoad' && res['data'].items.isEmpty) { + SmartDialog.showToast('没有更多了'); + return; + } + if (type == 'init') { + dynamicsList.value = res['data'].items; + } else { + dynamicsList.addAll(res['data'].items); + } + offset = res['data'].offset; + page++; + } + return res; + } +} diff --git a/lib/pages/dynamics/up_dynamic/index.dart b/lib/pages/dynamics/up_dynamic/index.dart new file mode 100644 index 00000000..2de4c7e4 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/index.dart @@ -0,0 +1,4 @@ +library up_dynamics; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/dynamics/up_dynamic/route_panel.dart b/lib/pages/dynamics/up_dynamic/route_panel.dart new file mode 100644 index 00000000..40c725a8 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/route_panel.dart @@ -0,0 +1,151 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/dynamics/up.dart'; +import 'package:pilipala/utils/feed_back.dart'; +import '../controller.dart'; +import 'index.dart'; + +class OverlayPanel extends StatefulWidget { + const OverlayPanel({super.key, required this.ctr, required this.upInfo}); + + final DynamicsController ctr; + final UpItem upInfo; + + @override + State createState() => _OverlayPanelState(); +} + +class _OverlayPanelState extends State + with SingleTickerProviderStateMixin { + static const itemPadding = EdgeInsets.symmetric(horizontal: 6, vertical: 0); + final PageController pageController = PageController(); + late double contentWidth = 50; + late List upList; + late RxInt currentMid = (-1).obs; + TabController? _tabController; + + @override + void initState() { + super.initState(); + upList = widget.ctr.upData.value.upList! + .map((element) => element) + .toList(); + upList.removeAt(0); + _tabController = TabController(length: upList.length, vsync: this); + + currentMid.value = widget.upInfo.mid!; + + pageController.addListener(() { + int index = pageController.page!.round(); + int mid = upList[index].mid!; + if (mid != currentMid.value) { + currentMid.value = mid; + _tabController?.animateTo(index, + duration: Duration.zero, curve: Curves.linear); + onClickUp(upList[index], index, type: 'pageChange'); + } + }); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + int index = + upList.indexWhere((element) => element.mid == widget.upInfo.mid); + pageController.jumpToPage(index); + onClickUp(widget.upInfo, index); + _tabController?.animateTo(index, + duration: Duration.zero, curve: Curves.linear); + onClickUp(upList[index], index, type: 'pageChange'); + }); + } + + void onClickUp(data, i, {type = 'click'}) { + if (type == 'click') { + pageController.jumpToPage(i); + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: Get.width, + height: Get.height, + clipBehavior: Clip.antiAlias, + margin: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(20), + ), + child: Column( + children: [ + SizedBox( + height: 50, + child: TabBar( + controller: _tabController, + dividerColor: Colors.transparent, + automaticIndicatorColorAdjustment: false, + tabAlignment: TabAlignment.start, + padding: const EdgeInsets.only(left: 12, right: 12), + indicatorPadding: EdgeInsets.zero, + indicatorSize: TabBarIndicatorSize.label, + indicator: const BoxDecoration(), + labelPadding: itemPadding, + indicatorWeight: 1, + isScrollable: true, + tabs: upList.map((e) => Tab(child: upItemBuild(e))).toList(), + onTap: (index) { + feedBack(); + EasyThrottle.throttle( + 'follow', const Duration(milliseconds: 200), () { + onClickUp(upList[index], index); + }); + }, + ), + ), + Expanded( + child: PageView.builder( + itemCount: upList.length, + controller: pageController, + itemBuilder: (BuildContext context, int index) { + return Container( + clipBehavior: Clip.antiAlias, + margin: const EdgeInsets.fromLTRB(10, 12, 10, 0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(20), + ), + child: UpDyanmicsPage(upInfo: upList[index], ctr: widget.ctr), + ); + }, + ), + ), + ], + ), + ); + } + + Widget upItemBuild(data) { + return Obx( + () => AnimatedOpacity( + opacity: currentMid == data.mid ? 1 : 0.3, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: currentMid == data.mid ? 1 : 0.9, + child: NetworkImgLayer( + width: contentWidth, + height: contentWidth, + src: data.face, + type: 'avatar', + ), + ), + ), + ); + } +} diff --git a/lib/pages/dynamics/up_dynamic/view.dart b/lib/pages/dynamics/up_dynamic/view.dart new file mode 100644 index 00000000..4b3fb0c7 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/view.dart @@ -0,0 +1,178 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/dynamic_card.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; +import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/models/dynamics/up.dart'; +import 'package:pilipala/pages/dynamics/up_dynamic/index.dart'; + +import '../index.dart'; +import '../widgets/dynamic_panel.dart'; + +class UpDyanmicsPage extends StatefulWidget { + final UpItem upInfo; + final DynamicsController ctr; + + const UpDyanmicsPage({ + required this.upInfo, + required this.ctr, + Key? key, + }) : super(key: key); + + @override + State createState() => _UpDyanmicsPageState(); +} + +class _UpDyanmicsPageState extends State + with AutomaticKeepAliveClientMixin { + late UpDynamicsController _upDynamicsController; + final ScrollController scrollController = ScrollController(); + late Future _futureBuilderFuture; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + _upDynamicsController = Get.put(UpDynamicsController(widget.upInfo), + tag: widget.upInfo.mid.toString()); + _futureBuilderFuture = _upDynamicsController.queryFollowDynamic(); + + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle( + 'queryFollowDynamic', const Duration(seconds: 1), () { + _upDynamicsController.queryFollowDynamic(type: 'onLoad'); + }); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return CustomScrollView( + controller: scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPersistentHeader( + pinned: true, + floating: true, + delegate: _MySliverPersistentHeaderDelegate( + child: Container( + height: 50, + padding: const EdgeInsets.fromLTRB(20, 4, 4, 4), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + bottom: BorderSide( + color: Theme.of(context).colorScheme.onSurface, + width: 0.1, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.upInfo.uname!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close), + ) + ], + ), + ), + ), + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SliverToBoxAdapter(child: SizedBox()); + } + Map? data = snapshot.data; + if (data != null && data['status']) { + List list = + _upDynamicsController.dynamicsList; + return Obx( + () { + if (list.isEmpty) { + if (_upDynamicsController.isLoadingDynamic.value) { + return skeleton(); + } else { + return const NoData(); + } + } else { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return DynamicPanel(item: list[index]); + }, + childCount: list.length, + ), + ); + } + }, + ); + } else { + return HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () {}, + ); + } + } else { + // 骨架屏 + return skeleton(); + } + }, + ), + ], + ); + } + + Widget skeleton() { + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const DynamicCardSkeleton(); + }, childCount: 5), + ); + } +} + +class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { + _MySliverPersistentHeaderDelegate({required this.child}); + final double _minExtent = 50; + final double _maxExtent = 50; + final Widget child; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => _maxExtent; + + @override + double get minExtent => _minExtent; + + @override + bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) { + return true; + } +} diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 0fc16dcf..38411613 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,13 +3,13 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/route_push.dart'; @@ -18,6 +18,7 @@ import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; import 'controller.dart'; import 'widgets/dynamic_panel.dart'; +import 'up_dynamic/route_panel.dart'; import 'widgets/up_panel.dart'; class DynamicsPage extends StatefulWidget { @@ -202,7 +203,21 @@ class _DynamicsPageState extends State } Map data = snapshot.data; if (data['status']) { - return Obx(() => UpPanel(_dynamicsController.upData.value)); + return Obx( + () => UpPanel( + upData: _dynamicsController.upData.value, + onClickUpCb: (data) { + // _dynamicsController.onTapUp(data); + Navigator.push( + context, + PlPopupRoute( + child: OverlayPanel( + ctr: _dynamicsController, upInfo: data), + ), + ); + }, + ), + ); } else { return const SliverToBoxAdapter( child: SizedBox(height: 80), diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index f8c973a0..3a0edcf6 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -4,13 +4,18 @@ import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; -import 'package:pilipala/pages/dynamics/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/utils.dart'; class UpPanel extends StatefulWidget { final FollowUpModel upData; - const UpPanel(this.upData, {Key? key}) : super(key: key); + final Function? onClickUpCb; + + const UpPanel({ + super.key, + required this.upData, + this.onClickUpCb, + }); @override State createState() => _UpPanelState(); @@ -33,27 +38,25 @@ class _UpPanelState extends State { void onClickUp(data, i) { currentMid = data.mid; - Get.find().mid.value = data.mid; - Get.find().upInfo.value = data; - Get.find().onSelectUp(data.mid); - int liveLen = liveList.length; - int upLen = upList.length; - double itemWidth = contentWidth + itemPadding.horizontal; - double screenWidth = MediaQuery.sizeOf(context).width; - double moveDistance = 0.0; - if (itemWidth * (upList.length + liveList.length) <= screenWidth) { - } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { - moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; - } else { - moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; - } - data.hasUpdate = false; - scrollController.animateTo( - moveDistance, - duration: const Duration(milliseconds: 200), - curve: Curves.linear, - ); - setState(() {}); + widget.onClickUpCb?.call(data); + // int liveLen = liveList.length; + // int upLen = upList.length; + // double itemWidth = contentWidth + itemPadding.horizontal; + // double screenWidth = MediaQuery.sizeOf(context).width; + // double moveDistance = 0.0; + // if (itemWidth * (upList.length + liveList.length) <= screenWidth) { + // } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { + // moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; + // } else { + // moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; + // } + // data.hasUpdate = false; + // scrollController.animateTo( + // moveDistance, + // duration: const Duration(milliseconds: 200), + // curve: Curves.linear, + // ); + // setState(() {}); } @override diff --git a/lib/plugin/pl_popup/index.dart b/lib/plugin/pl_popup/index.dart new file mode 100644 index 00000000..678eb049 --- /dev/null +++ b/lib/plugin/pl_popup/index.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class PlPopupRoute extends PopupRoute { + PlPopupRoute({ + this.backgroudColor, + this.alignment = Alignment.center, + required this.child, + this.onClick, + }); + + /// backgroudColor + final Color? backgroudColor; + + /// child'alignment, default value: [Alignment.center] + final Alignment alignment; + + /// child + final Widget child; + + /// backgroudView action + final Function? onClick; + + @override + Duration get transitionDuration => const Duration(milliseconds: 300); + + @override + bool get barrierDismissible => false; + + @override + Color get barrierColor => Colors.black54; + + @override + String? get barrierLabel => null; + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return child; + } +} From 9f676f8ef9ca13e6f6d93f01e9b2554e6ef3657f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 17 Jul 2024 23:37:55 +0800 Subject: [PATCH 194/438] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=95=B0=E6=8D=AE=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/badge.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/widgets/badge.dart b/lib/common/widgets/badge.dart index a8f2fc67..1e518f39 100644 --- a/lib/common/widgets/badge.dart +++ b/lib/common/widgets/badge.dart @@ -66,7 +66,7 @@ class PBadge extends StatelessWidget { border: Border.all(color: borderColor), ), child: Text( - text!, + text ?? '', style: TextStyle(fontSize: fs ?? fontSize, color: color), ), ); From 9c00d3c4d36c6ccd07d084095dc44eab4de2057d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Jul 2024 00:52:17 +0800 Subject: [PATCH 195/438] =?UTF-8?q?feat:=20up=E6=8A=95=E7=A8=BF=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=85=85=E7=94=B5=E4=B8=93=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 7 ++++ lib/pages/member_archive/controller.dart | 9 +++-- lib/pages/member_archive/view.dart | 46 +++++++++++++++++++----- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index 20a2c728..0fb010b0 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -96,7 +96,14 @@ class MemberHttp { 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', + ...order == 'charge' + ? { + 'order': 'pubdate', + 'special_type': 'charging', + } + : {} }); + var res = await Request().get( Api.memberArchive, data: params, diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 4c41de4c..667d16c5 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -9,12 +9,14 @@ class MemberArchiveController extends GetxController { int pn = 1; int count = 0; RxMap currentOrder = {}.obs; - List> orderList = [ + RxList> orderList = [ {'type': 'pubdate', 'label': '最新发布'}, {'type': 'click', 'label': '最多播放'}, {'type': 'stow', 'label': '最多收藏'}, - ]; + {'type': 'charge', 'label': '充电专属'}, + ].obs; RxList archivesList = [].obs; + RxBool isLoading = false.obs; @override void onInit() { @@ -27,6 +29,8 @@ class MemberArchiveController extends GetxController { Future getMemberArchive(type) async { if (type == 'init') { pn = 1; + archivesList.clear(); + isLoading.value = true; } var res = await MemberHttp.memberArchive( mid: mid, @@ -43,6 +47,7 @@ class MemberArchiveController extends GetxController { count = res['data'].page['count']; pn += 1; } + isLoading.value = false; return res; } diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index f38ca0cb..898aa915 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -1,6 +1,8 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/utils/utils.dart'; import '../../common/widgets/http_error.dart'; @@ -47,14 +49,29 @@ class _MemberArchivePageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的投稿', style: Theme.of(context).textTheme.titleMedium), + title: Obx( + () => Text( + '他的投稿 - ${_memberArchivesController.currentOrder['label']}', + style: Theme.of(context).textTheme.titleMedium), + ), actions: [ - Obx( - () => TextButton.icon( - icon: const Icon(Icons.sort, size: 20), - onPressed: _memberArchivesController.toggleSort, - label: Text(_memberArchivesController.currentOrder['label']!), - ), + // Obx( + PopupMenuButton( + icon: const Icon(Icons.more_vert), + onSelected: (value) { + // 这里处理选择逻辑 + _memberArchivesController.currentOrder.value = value; + _memberArchivesController.getMemberArchive('init'); + }, + itemBuilder: (BuildContext context) => + _memberArchivesController.orderList.map( + (e) { + return PopupMenuItem( + value: e, + child: Text(e['label']!), + ); + }, + ).toList(), ), const SizedBox(width: 6), ], @@ -85,7 +102,14 @@ class _MemberArchivePageState extends State { childCount: list.length, ), ) - : const SliverToBoxAdapter(), + : _memberArchivesController.isLoading.value + ? SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ) + : const NoData(), ); } else { return HttpError( @@ -100,7 +124,11 @@ class _MemberArchivePageState extends State { ); } } else { - return const SliverToBoxAdapter(); + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ); } }, ), From 75859fc9cf687a3c86a1ba9060d741f8e56bed14 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Jul 2024 01:11:26 +0800 Subject: [PATCH 196/438] =?UTF-8?q?mod:=20=E5=8F=96=E6=B6=88=E6=8A=95?= =?UTF-8?q?=E5=B8=81=E9=99=90=E5=88=B6&=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 58 +++++++++---------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 50aac4cd..cb9ad3c1 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; @@ -196,45 +197,38 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - if (hasCoin.value) { - SmartDialog.showToast('已投过币了'); - return; - } showDialog( context: Get.context!, builder: (context) { return AlertDialog( title: const Text('选择投币个数'), contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), - content: StatefulBuilder(builder: (context, StateSetter setState) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [1, 2] - .map( - (e) => RadioListTile( - value: e, - title: Text('$e枚'), - groupValue: _tempThemeValue, - onChanged: (value) async { - _tempThemeValue = value!; - setState(() {}); - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功'); - hasCoin.value = true; - videoDetail.value.stat!.coin = - videoDetail.value.stat!.coin! + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [1, 2] + .map( + (e) => ListTile( + title: Padding( + padding: const EdgeInsets.only(left: 20), + child: Text('$e 枚'), ), - ) - .toList(), - ); - }), + onTap: () async { + var res = + await VideoHttp.coinVideo(bvid: bvid, multiply: e); + if (res['status']) { + SmartDialog.showToast('投币成功'); + hasCoin.value = true; + videoDetail.value.stat!.coin = + videoDetail.value.stat!.coin! + e; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), + ), ); }); } From 38b6b6b4e09cf8e2db9b969f8e525b9abd43ae92 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Jul 2024 18:50:36 +0800 Subject: [PATCH 197/438] =?UTF-8?q?fix:=20=E6=B6=88=E6=81=AFmid=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 1814c274..1f69fa64 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -214,7 +214,7 @@ class SessionItem extends StatelessWidget { @override Widget build(BuildContext context) { - final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); + final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo?.mid ?? 0); final content = sessionItem.lastMsg.content; final msgStatus = sessionItem.lastMsg.msgStatus; From bc3ce33f78329d4cf0985d57ab8fa3a1a80b1753 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Jul 2024 22:40:43 +0800 Subject: [PATCH 198/438] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E4=BA=8C?= =?UTF-8?q?=E6=A5=BC=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +- lib/pages/video/detail/controller.dart | 4 +- lib/pages/video/detail/reply/view.dart | 10 +- .../detail/reply/widgets/reply_item.dart | 21 ++- .../video/detail/reply_reply/controller.dart | 13 +- lib/pages/video/detail/reply_reply/view.dart | 176 ++++++++++-------- 6 files changed, 139 insertions(+), 91 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index e83b5547..6b3d969d 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 662f45f2..2c81140d 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -162,7 +162,7 @@ class VideoDetailController extends GetxController ); } - showReplyReplyPanel(oid, fRpid, firstFloor) { + showReplyReplyPanel(oid, fRpid, firstFloor, currentReply, loadMore) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( @@ -175,6 +175,8 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, + currentReply: currentReply, + loadMore: loadMore, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 4fe69481..f91ef625 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -112,7 +112,7 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply, loadMore) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { @@ -120,7 +120,7 @@ class _VideoReplyPanelState extends State videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; videoDetailCtr.showReplyReplyPanel( - replyItem.oid, replyItem.rpid!, replyItem); + replyItem.oid, replyItem.rpid!, replyItem, currentReply, loadMore); } } @@ -232,8 +232,10 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply, + loadMore) => + replyReply(replyItem, currentReply, + loadMore), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index c4c26e6b..bba5ddbb 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -49,7 +49,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem); + replyReply!(replyItem, null, replyItem!.replies!.isNotEmpty); } }, onLongPress: () { @@ -362,9 +362,13 @@ class ReplyItemRow extends StatelessWidget { for (int i = 0; i < replies!.length; i++) ...[ InkWell( // 一楼点击评论展开评论详情 - // onTap: () { - // replyReply?.call(replyItem); - // }, + onTap: () { + replyReply?.call( + replyItem, + replies![i], + replyItem!.replies!.isNotEmpty, + ); + }, onLongPress: () { feedBack(); showModalBottomSheet( @@ -535,9 +539,12 @@ InlineSpan buildContent( spanChilds.add( TextSpan( text: str, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), + // recognizer: TapGestureRecognizer() + // ..onTap = () => replyReply?.call( + // replyItem.root == 0 ? replyItem : fReplyItem, + // replyItem, + // fReplyItem!.replies!.isNotEmpty, + // ), ), ); } diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index e94aaea5..3d5644e8 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init'}) async { + Future queryReplyList({type = 'init', currentReply}) async { if (type == 'init') { currentPage = 0; } @@ -63,6 +63,17 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } + if (replyList.isNotEmpty && currentReply != null) { + int indexToRemove = + replyList.indexWhere((item) => currentReply.rpid == item.rpid); + // 如果找到了指定ID的项,则移除 + if (indexToRemove != -1) { + replyList.removeAt(indexToRemove); + } + if (currentPage == 1 && type == 'init') { + replyList.insert(0, currentReply); + } + } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 6dda9512..439f5d1d 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,6 +20,8 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, + this.currentReply, + this.loadMore, super.key, }); final int? oid; @@ -29,6 +31,8 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; + final dynamic currentReply; + final bool? loadMore; @override State createState() => _VideoReplyReplyPanelState(); @@ -63,7 +67,9 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); } void replyReply(replyItem) {} @@ -107,7 +113,9 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList(); + return await _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -134,84 +142,102 @@ class _VideoReplyReplyPanelState extends State { ), ), ], - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - // 请求成功 - return Obx( - () => SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (index == - _videoReplyReplyController - .replyList.length) { - return Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - height: MediaQuery.of(context) - .padding - .bottom + - 100, - child: Center( - child: Obx( - () => Text( + widget.loadMore != null && widget.loadMore! + ? FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + // 请求成功 + return Obx( + () => SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == _videoReplyReplyController - .noMore.value, - style: TextStyle( - fontSize: 12, - color: Theme.of(context) - .colorScheme - .outline, + .replyList.length) { + return Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context) + .padding + .bottom), + height: MediaQuery.of(context) + .padding + .bottom + + 100, + child: Center( + child: Obx( + () => Text( + _videoReplyReplyController + .noMore.value, + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .colorScheme + .outline, + ), + ), + ), ), - ), - ), - ), - ); - } else { - return ReplyItem( - replyItem: _videoReplyReplyController - .replyList[index], - replyLevel: '2', - showReplyRow: false, - addReply: (replyItem) { - _videoReplyReplyController.replyList - .add(replyItem); + ); + } else { + return ReplyItem( + replyItem: + _videoReplyReplyController + .replyList[index], + replyLevel: '2', + showReplyRow: false, + addReply: (replyItem) { + _videoReplyReplyController + .replyList + .add(replyItem); + }, + replyType: widget.replyType, + replyReply: (replyItem) => + replyReply(replyItem), + ); + } }, - replyType: widget.replyType, - replyReply: (replyItem) => - replyReply(replyItem), - ); - } - }, - childCount: _videoReplyReplyController - .replyList.length + - 1, + childCount: _videoReplyReplyController + .replyList.length + + 1, + ), + ), + ); + } else { + // 请求错误 + return HttpError( + errMsg: data?['msg'] ?? '请求错误', + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ); + } + }, + ) + : SliverToBoxAdapter( + child: SizedBox( + height: 200, + child: Center( + child: Text( + '还没有评论', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), ), ), - ); - } else { - // 请求错误 - return HttpError( - errMsg: data?['msg'] ?? '请求错误', - fn: () => setState(() {}), - ); - } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ); - } - }, - ) + ), + ) ], ), ), From bc36493e60eb254c9e19e76a07f8aec4e8e37259 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Jul 2024 23:25:06 +0800 Subject: [PATCH 199/438] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=95=B0=E6=8D=AE=E6=A0=BC=E5=BC=8F=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 5 +- lib/http/video.dart | 72 ++++++------ lib/models/home/rcmd/result.dart | 12 +- lib/models/model_owner.dart | 9 +- lib/models/model_owner.g.dart | 47 -------- lib/models/model_rec_video_item.dart | 50 +------- lib/models/model_rec_video_item.g.dart | 154 ------------------------- lib/models/search/hot.dart | 11 -- lib/models/search/hot.g.dart | 84 -------------- lib/models/user/stat.dart | 8 -- lib/models/user/stat.g.dart | 47 -------- lib/utils/storage.dart | 5 - 12 files changed, 45 insertions(+), 459 deletions(-) delete mode 100644 lib/models/model_owner.g.dart delete mode 100644 lib/models/model_rec_video_item.g.dart delete mode 100644 lib/models/search/hot.g.dart delete mode 100644 lib/models/user/stat.g.dart diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 14476cdf..d8e1bb2c 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -215,9 +215,8 @@ class VideoContent extends StatelessWidget { children: [ if (videoItem.goto == 'bangumi') _buildBadge(videoItem.bangumiBadge, 'line', 9), - if (videoItem.rcmdReason?.content != null && - videoItem.rcmdReason.content != '') - _buildBadge(videoItem.rcmdReason.content, 'color'), + if (videoItem.rcmdReason != null) + _buildBadge(videoItem.rcmdReason, 'color'), if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9), if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'), Expanded( diff --git a/lib/http/video.dart b/lib/http/video.dart index 834a0102..160f5db2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -70,47 +70,43 @@ class VideoHttp { // 添加额外的loginState变量模拟未登录状态 static Future rcmdVideoListApp( {bool loginStatus = true, required int freshIdx}) async { - try { - var res = await Request().get( - Api.recommendListApp, - data: { - 'idx': freshIdx, - 'flush': '5', - 'column': '4', - 'device': 'pad', - 'device_type': 0, - 'device_name': 'vivo', - 'pull': freshIdx == 0 ? 'true' : 'false', - 'appkey': Constants.appKey, - 'access_key': loginStatus - ? (localCache.get(LocalCacheKey.accessKey, - defaultValue: {})['value'] ?? - '') - : '' - }, - ); - if (res.data['code'] == 0) { - List list = []; - List blackMidsList = - setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); - for (var i in res.data['data']['items']) { - // 屏蔽推广和拉黑用户 - if (i['card_goto'] != 'ad_av' && - (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && - (i['args'] != null && - !blackMidsList.contains(i['args']['up_mid']))) { - RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)) { - list.add(videoItem); - } + var res = await Request().get( + Api.recommendListApp, + data: { + 'idx': freshIdx, + 'flush': '5', + 'column': '4', + 'device': 'pad', + 'device_type': 0, + 'device_name': 'vivo', + 'pull': freshIdx == 0 ? 'true' : 'false', + 'appkey': Constants.appKey, + 'access_key': loginStatus + ? (localCache + .get(LocalCacheKey.accessKey, defaultValue: {})['value'] ?? + '') + : '' + }, + ); + if (res.data['code'] == 0) { + List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); + for (var i in res.data['data']['items']) { + // 屏蔽推广和拉黑用户 + if (i['card_goto'] != 'ad_av' && + (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && + (i['args'] != null && + !blackMidsList.contains(i['args']['up_mid']))) { + RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); + if (!RecommendFilter.filter(videoItem)) { + list.add(videoItem); } } - return {'status': true, 'data': list}; - } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; } - } catch (err) { - return {'status': false, 'data': [], 'msg': err.toString()}; + return {'status': true, 'data': list}; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; } } diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 0098fe95..98beda59 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -34,7 +34,7 @@ class RecVideoItemAppModel { String? title; int? isFollowed; RcmdOwner? owner; - RcmdReason? rcmdReason; + String? rcmdReason; String? goto; int? param; String? uri; @@ -64,17 +64,11 @@ class RecVideoItemAppModel { //duration = json['cover_right_text']; title = json['title']; owner = RcmdOwner.fromJson(json); - rcmdReason = json['rcmd_reason_style'] != null - ? RcmdReason.fromJson(json['rcmd_reason_style']) - : null; + rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason']; // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 RegExp regex = RegExp(r'已关注|新关注'); - isFollowed = rcmdReason != null && - rcmdReason!.content != null && - regex.hasMatch(rcmdReason!.content!) - ? 1 - : 0; + isFollowed = regex.hasMatch(rcmdReason ?? '') ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 if (isFollowed == 1) { rcmdReason = null; diff --git a/lib/models/model_owner.dart b/lib/models/model_owner.dart index 70396cbb..6ef425eb 100644 --- a/lib/models/model_owner.dart +++ b/lib/models/model_owner.dart @@ -1,19 +1,12 @@ -import 'package:hive/hive.dart'; - -part 'model_owner.g.dart'; - -@HiveType(typeId: 3) class Owner { Owner({ this.mid, this.name, this.face, }); - @HiveField(0) + int? mid; - @HiveField(1) String? name; - @HiveField(2) String? face; Owner.fromJson(Map json) { diff --git a/lib/models/model_owner.g.dart b/lib/models/model_owner.g.dart deleted file mode 100644 index de452713..00000000 --- a/lib/models/model_owner.g.dart +++ /dev/null @@ -1,47 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'model_owner.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class OwnerAdapter extends TypeAdapter { - @override - final int typeId = 3; - - @override - Owner read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Owner( - mid: fields[0] as int?, - name: fields[1] as String?, - face: fields[2] as String?, - ); - } - - @override - void write(BinaryWriter writer, Owner obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.mid) - ..writeByte(1) - ..write(obj.name) - ..writeByte(2) - ..write(obj.face); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is OwnerAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/model_rec_video_item.dart b/lib/models/model_rec_video_item.dart index 1503f192..b82510db 100644 --- a/lib/models/model_rec_video_item.dart +++ b/lib/models/model_rec_video_item.dart @@ -1,9 +1,5 @@ import './model_owner.dart'; -import 'package:hive/hive.dart'; -part 'model_rec_video_item.g.dart'; - -@HiveType(typeId: 0) class RecVideoItemModel { RecVideoItemModel({ this.id, @@ -21,32 +17,19 @@ class RecVideoItemModel { this.rcmdReason, }); - @HiveField(0) int? id = -1; - @HiveField(1) String? bvid = ''; - @HiveField(2) int? cid = -1; - @HiveField(3) String? goto = ''; - @HiveField(4) String? uri = ''; - @HiveField(5) String? pic = ''; - @HiveField(6) String? title = ''; - @HiveField(7) int? duration = -1; - @HiveField(8) int? pubdate = -1; - @HiveField(9) Owner? owner; - @HiveField(10) Stat? stat; - @HiveField(11) int? isFollowed; - @HiveField(12) - RcmdReason? rcmdReason; + String? rcmdReason; RecVideoItemModel.fromJson(Map json) { id = json["id"]; @@ -61,26 +44,20 @@ class RecVideoItemModel { owner = Owner.fromJson(json["owner"]); stat = Stat.fromJson(json["stat"]); isFollowed = json["is_followed"] ?? 0; - rcmdReason = json["rcmd_reason"] != null - ? RcmdReason.fromJson(json["rcmd_reason"]) - : RcmdReason(content: ''); + rcmdReason = json["rcmd_reason"]?['content']; } } -@HiveType(typeId: 1) class Stat { Stat({ this.view, this.like, this.danmu, }); - @HiveField(0) - int? view; - @HiveField(1) - int? like; - @HiveField(2) - int? danmu; + int? view; + int? like; + int? danmu; Stat.fromJson(Map json) { // 无需在model中转换以保留原始数据,在view层处理即可 view = json["view"]; @@ -88,20 +65,3 @@ class Stat { danmu = json['danmaku']; } } - -@HiveType(typeId: 2) -class RcmdReason { - RcmdReason({ - this.reasonType, - this.content, - }); - @HiveField(0) - int? reasonType; - @HiveField(1) - String? content = ''; - - RcmdReason.fromJson(Map json) { - reasonType = json["reason_type"]; - content = json["content"] ?? ''; - } -} diff --git a/lib/models/model_rec_video_item.g.dart b/lib/models/model_rec_video_item.g.dart deleted file mode 100644 index dc614354..00000000 --- a/lib/models/model_rec_video_item.g.dart +++ /dev/null @@ -1,154 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'model_rec_video_item.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class RecVideoItemModelAdapter extends TypeAdapter { - @override - final int typeId = 0; - - @override - RecVideoItemModel read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RecVideoItemModel( - id: fields[0] as int?, - bvid: fields[1] as String?, - cid: fields[2] as int?, - goto: fields[3] as String?, - uri: fields[4] as String?, - pic: fields[5] as String?, - title: fields[6] as String?, - duration: fields[7] as int?, - pubdate: fields[8] as int?, - owner: fields[9] as Owner?, - stat: fields[10] as Stat?, - isFollowed: fields[11] as int?, - rcmdReason: fields[12] as RcmdReason?, - ); - } - - @override - void write(BinaryWriter writer, RecVideoItemModel obj) { - writer - ..writeByte(13) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.bvid) - ..writeByte(2) - ..write(obj.cid) - ..writeByte(3) - ..write(obj.goto) - ..writeByte(4) - ..write(obj.uri) - ..writeByte(5) - ..write(obj.pic) - ..writeByte(6) - ..write(obj.title) - ..writeByte(7) - ..write(obj.duration) - ..writeByte(8) - ..write(obj.pubdate) - ..writeByte(9) - ..write(obj.owner) - ..writeByte(10) - ..write(obj.stat) - ..writeByte(11) - ..write(obj.isFollowed) - ..writeByte(12) - ..write(obj.rcmdReason); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecVideoItemModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class StatAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - Stat read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Stat( - view: fields[0] as int?, - like: fields[1] as int?, - danmu: fields[2] as int?, - ); - } - - @override - void write(BinaryWriter writer, Stat obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.view) - ..writeByte(1) - ..write(obj.like) - ..writeByte(2) - ..write(obj.danmu); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StatAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class RcmdReasonAdapter extends TypeAdapter { - @override - final int typeId = 2; - - @override - RcmdReason read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RcmdReason( - reasonType: fields[0] as int?, - content: fields[1] as String?, - ); - } - - @override - void write(BinaryWriter writer, RcmdReason obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.reasonType) - ..writeByte(1) - ..write(obj.content); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RcmdReasonAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/search/hot.dart b/lib/models/search/hot.dart index ce09b2ea..59d7e749 100644 --- a/lib/models/search/hot.dart +++ b/lib/models/search/hot.dart @@ -1,14 +1,8 @@ -import 'package:hive/hive.dart'; - -part 'hot.g.dart'; - -@HiveType(typeId: 6) class HotSearchModel { HotSearchModel({ this.list, }); - @HiveField(0) List? list; HotSearchModel.fromJson(Map json) { @@ -18,7 +12,6 @@ class HotSearchModel { } } -@HiveType(typeId: 7) class HotSearchItem { HotSearchItem({ this.keyword, @@ -27,14 +20,10 @@ class HotSearchItem { this.icon, }); - @HiveField(0) String? keyword; - @HiveField(1) String? showName; // 4/5热 11话题 8普通 7直播 - @HiveField(2) int? wordType; - @HiveField(3) String? icon; HotSearchItem.fromJson(Map json) { diff --git a/lib/models/search/hot.g.dart b/lib/models/search/hot.g.dart deleted file mode 100644 index a06dd475..00000000 --- a/lib/models/search/hot.g.dart +++ /dev/null @@ -1,84 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'hot.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class HotSearchModelAdapter extends TypeAdapter { - @override - final int typeId = 6; - - @override - HotSearchModel read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HotSearchModel( - list: (fields[0] as List?)?.cast(), - ); - } - - @override - void write(BinaryWriter writer, HotSearchModel obj) { - writer - ..writeByte(1) - ..writeByte(0) - ..write(obj.list); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HotSearchModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class HotSearchItemAdapter extends TypeAdapter { - @override - final int typeId = 7; - - @override - HotSearchItem read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HotSearchItem( - keyword: fields[0] as String?, - showName: fields[1] as String?, - wordType: fields[2] as int?, - icon: fields[3] as String?, - ); - } - - @override - void write(BinaryWriter writer, HotSearchItem obj) { - writer - ..writeByte(4) - ..writeByte(0) - ..write(obj.keyword) - ..writeByte(1) - ..write(obj.showName) - ..writeByte(2) - ..write(obj.wordType) - ..writeByte(3) - ..write(obj.icon); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HotSearchItemAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/user/stat.dart b/lib/models/user/stat.dart index 3b09acb1..0b56a499 100644 --- a/lib/models/user/stat.dart +++ b/lib/models/user/stat.dart @@ -1,8 +1,3 @@ -import 'package:hive/hive.dart'; - -part 'stat.g.dart'; - -@HiveType(typeId: 1) class UserStat { UserStat({ this.following, @@ -10,11 +5,8 @@ class UserStat { this.dynamicCount, }); - @HiveField(0) int? following; - @HiveField(1) int? follower; - @HiveField(2) int? dynamicCount; UserStat.fromJson(Map json) { diff --git a/lib/models/user/stat.g.dart b/lib/models/user/stat.g.dart deleted file mode 100644 index 290fe026..00000000 --- a/lib/models/user/stat.g.dart +++ /dev/null @@ -1,47 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stat.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class UserStatAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - UserStat read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return UserStat( - following: fields[0] as int?, - follower: fields[1] as int?, - dynamicCount: fields[2] as int?, - ); - } - - @override - void write(BinaryWriter writer, UserStat obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.following) - ..writeByte(1) - ..write(obj.follower) - ..writeByte(2) - ..write(obj.dynamicCount); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is UserStatAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index dca5a158..a132c5da 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -1,8 +1,6 @@ import 'dart:io'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:pilipala/models/model_owner.dart'; -import 'package:pilipala/models/search/hot.dart'; import 'package:pilipala/models/user/info.dart'; import '../models/common/gesture_mode.dart'; import 'global_data.dart'; @@ -54,11 +52,8 @@ class GStrorage { } static void regAdapter() { - Hive.registerAdapter(OwnerAdapter()); Hive.registerAdapter(UserInfoDataAdapter()); Hive.registerAdapter(LevelInfoAdapter()); - Hive.registerAdapter(HotSearchModelAdapter()); - Hive.registerAdapter(HotSearchItemAdapter()); } static Future close() async { From 5b78810ba43cd795b2bff953b08f41e0b4e680d3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Jul 2024 23:29:53 +0800 Subject: [PATCH 200/438] =?UTF-8?q?fix:=20=E6=B6=88=E6=81=AFmid=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 1f69fa64..7082619f 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -228,7 +228,7 @@ class SessionItem extends StatelessWidget { 'talkerId': sessionItem.talkerId.toString(), 'name': sessionItem.accountInfo.name, 'face': sessionItem.accountInfo.face, - 'mid': sessionItem.accountInfo.mid.toString(), + 'mid': (sessionItem.accountInfo?.mid ?? 0).toString(), 'heroTag': heroTag, }, ); From a0bc0314bf51c1da040e4a6f0326ab96737fbd28 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Jul 2024 00:08:18 +0800 Subject: [PATCH 201/438] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E6=9F=A5=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index bba5ddbb..5b7b0659 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -16,7 +16,6 @@ import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -436,7 +435,8 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem), + onTap: () => replyReply?.call(replyItem, null, true), + onLongPress: () => {}, child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), From 80d0b15ac88d4b9bdcb576d45519fc296c036b2a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Jul 2024 22:47:14 +0800 Subject: [PATCH 202/438] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E6=9F=A5=E7=9C=8B=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 9 ++++++--- lib/pages/video/detail/reply_reply/view.dart | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 6b3d969d..56af68fc 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem, currentReply, loadMore) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -125,6 +125,7 @@ class _DynamicDetailPageState extends State source: 'dynamic', replyType: ReplyType.values[replyType], firstFloor: replyItem, + loadMore: loadMore, ), ), ); @@ -324,8 +325,10 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: + (replyItem, currentReply, loadMore) => + replyReply(replyItem, + currentReply, loadMore), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 439f5d1d..06a40cd6 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -21,7 +21,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.replyType, this.sheetHeight, this.currentReply, - this.loadMore, + this.loadMore = true, super.key, }); final int? oid; @@ -32,7 +32,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final ReplyType? replyType; final double? sheetHeight; final dynamic currentReply; - final bool? loadMore; + final bool loadMore; @override State createState() => _VideoReplyReplyPanelState(); @@ -142,7 +142,7 @@ class _VideoReplyReplyPanelState extends State { ), ), ], - widget.loadMore != null && widget.loadMore! + widget.loadMore ? FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, snapshot) { From 5e6f082ade33223e30fc522ec6fb78f428a713a3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 11:00:19 +0800 Subject: [PATCH 203/438] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E4=B8=BA=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 28 +++- .../detail/reply/widgets/reply_save.dart | 140 ++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 lib/pages/video/detail/reply/widgets/reply_save.dart diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5b7b0659..9fc8b11e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -14,11 +14,13 @@ import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/plugin/pl_gallery/index.dart'; +import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; +import 'reply_save.dart'; import 'zan.dart'; Box setting = GStrorage.setting; @@ -58,7 +60,10 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(item: replyItem); + return MorePanel( + item: replyItem, + mainFloor: true, + ); }, ); }, @@ -1004,7 +1009,12 @@ InlineSpan buildContent( class MorePanel extends StatelessWidget { final dynamic item; - const MorePanel({super.key, required this.item}); + final bool mainFloor; + const MorePanel({ + super.key, + required this.item, + this.mainFloor = false, + }); Future menuActionHandler(String type) async { String message = item.content.message ?? item.content; @@ -1026,6 +1036,13 @@ class MorePanel extends StatelessWidget { }, ); break; + case 'save': + Get.back(); + Navigator.push( + Get.context!, + PlPopupRoute(child: ReplySave(replyItem: item)), + ); + break; // case 'block': // SmartDialog.showToast('加入黑名单'); // break; @@ -1076,6 +1093,13 @@ class MorePanel extends StatelessWidget { leading: const Icon(Icons.copy_outlined, size: 19), title: Text('自由复制', style: textTheme.titleSmall), ), + if (mainFloor) + ListTile( + onTap: () async => await menuActionHandler('save'), + minLeadingWidth: 0, + leading: const Icon(Icons.save_alt_rounded, size: 19), + title: Text('本地保存', style: textTheme.titleSmall), + ), // ListTile( // onTap: () async => await menuActionHandler('block'), // minLeadingWidth: 0, diff --git a/lib/pages/video/detail/reply/widgets/reply_save.dart b/lib/pages/video/detail/reply/widgets/reply_save.dart new file mode 100644 index 00000000..c083806f --- /dev/null +++ b/lib/pages/video/detail/reply/widgets/reply_save.dart @@ -0,0 +1,140 @@ +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/models/video/reply/item.dart'; +import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; +import 'package:saver_gallery/saver_gallery.dart'; + +class ReplySave extends StatefulWidget { + final ReplyItemModel? replyItem; + const ReplySave({required this.replyItem, super.key}); + + @override + State createState() => _ReplySaveState(); +} + +class _ReplySaveState extends State { + final _boundaryKey = GlobalKey(); + + void _generatePicWidget() async { + SmartDialog.showLoading(msg: '保存中'); + try { + RenderRepaintBoundary boundary = _boundaryKey.currentContext! + .findRenderObject() as RenderRepaintBoundary; + var image = await boundary.toImage(pixelRatio: 3); + ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); + Uint8List pngBytes = byteData!.buffer.asUint8List(); + String picName = + "plpl_reply_${DateTime.now().toString().replaceAll(RegExp(r'[- :]'), '').split('.').first}"; + final result = await SaverGallery.saveImage( + Uint8List.fromList(pngBytes), + name: '$picName.png', + androidRelativePath: "Pictures/PiliPala", + androidExistNotSave: false, + ); + if (result.isSuccess) { + SmartDialog.showToast('保存成功'); + } + } catch (err) { + print(err); + } finally { + SmartDialog.dismiss(); + } + } + + List _createWidgets(int count, Widget Function() builder) { + return List.generate(count, (_) => Expanded(child: builder())); + } + + List _createColumnWidgets() { + return _createWidgets(3, () => Row(children: _createRowWidgets())); + } + + List _createRowWidgets() { + return _createWidgets( + 4, + () => Center( + child: Transform.rotate( + angle: pi / 10, + child: const Text( + 'PiliPala', + style: TextStyle( + color: Color(0x08000000), + fontSize: 18, + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + width: Get.width, + height: Get.height, + margin: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + color: Colors.transparent, + child: Column( + children: [ + Expanded( + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: SingleChildScrollView( + child: RepaintBoundary( + key: _boundaryKey, + child: IntrinsicHeight( + child: Stack( + children: [ + ReplyItem( + replyItem: widget.replyItem, + showReplyRow: false, + ), + Positioned.fill( + child: Column( + children: _createColumnWidgets(), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () => Get.back(), + child: const Text('取消'), + ), + const SizedBox(width: 40), + FilledButton( + onPressed: _generatePicWidget, + child: const Text('保存'), + ), + ], + ), + ], + ), + ); + } +} From a2cbcca9091ce10dd041bb99fe6aba2f303df1eb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 11:02:42 +0800 Subject: [PATCH 204/438] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 98beda59..88657b33 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -62,7 +62,7 @@ class RecVideoItemAppModel { duration = json['player_args'] != null ? json['player_args']['duration'] : -1; //duration = json['cover_right_text']; - title = json['title']; + title = json['title'] ?? '获取标题失败'; owner = RcmdOwner.fromJson(json); rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason']; // 由于app端api并不会直接返回与owner的关注状态 @@ -74,7 +74,7 @@ class RecVideoItemAppModel { rcmdReason = null; } goto = json['goto']; - param = int.parse(json['param']); + param = int.parse(json['param'] ?? '-1'); uri = json['uri']; talkBack = json['talk_back']; From 8e0fbb2a54391dd84d536ab6ef6d78f8bfbf850f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 22:08:12 +0800 Subject: [PATCH 205/438] =?UTF-8?q?opt:=20=E9=9D=9Epip=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E8=87=B3=E5=90=8E=E5=8F=B0=E4=B8=8D=E5=85=B3?= =?UTF-8?q?=E9=97=ADBottomSheet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 331986dd..0388e962 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -330,12 +330,14 @@ class _VideoDetailPageState extends State plPlayerController?.danmakuController?.clear(); break; case 'pause': - vdCtr.hiddenReplyReplyPanel(); - if (vdCtr.videoType == SearchType.video) { - videoIntroController.hiddenEpisodeBottomSheet(); - } - if (vdCtr.videoType == SearchType.media_bangumi) { - bangumiIntroController.hiddenEpisodeBottomSheet(); + if (autoPiP) { + vdCtr.hiddenReplyReplyPanel(); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + } } break; } From 66edac428b99b1ff45860f9c6ed5b2ae66e51925 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 4 Aug 2024 00:35:42 +0800 Subject: [PATCH 206/438] =?UTF-8?q?mod:=20=E8=A1=A5=E5=85=85=E5=90=88?= =?UTF-8?q?=E9=9B=86=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/member.dart | 36 ++++++++++++++++++++ lib/models/member/seasons.dart | 13 ++++++++ lib/pages/member/widgets/seasons.dart | 23 +++++++++++-- lib/pages/member_seasons/controller.dart | 42 +++++++++++++++++++++--- lib/pages/member_seasons/view.dart | 7 ++-- 6 files changed, 114 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 46bbb6ac..31e5a38b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -487,6 +487,8 @@ class Api { static const getSeasonDetailApi = '/x/polymer/web-space/seasons_archives_list'; + static const getSeriesDetailApi = '/x/series/archives'; + /// 获取未读动态数 static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; diff --git a/lib/http/member.dart b/lib/http/member.dart index 0fb010b0..e87aa42e 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -520,4 +520,40 @@ class MemberHttp { }; } } + + static Future getSeriesDetail({ + required int mid, + required int currentMid, + required int seriesId, + required int pn, + }) async { + var res = await Request().get( + Api.getSeriesDetailApi, + data: { + 'mid': mid, + 'series_id': seriesId, + 'only_normal': true, + 'sort': 'desc', + 'pn': pn, + 'ps': 30, + 'current_mid': currentMid, + }, + ); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MemberSeasonsDataModel.fromJson(res.data['data']) + }; + } catch (err) { + print(err); + } + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/member/seasons.dart b/lib/models/member/seasons.dart index 88b93c78..275466a6 100644 --- a/lib/models/member/seasons.dart +++ b/lib/models/member/seasons.dart @@ -2,10 +2,12 @@ class MemberSeasonsDataModel { MemberSeasonsDataModel({ this.page, this.seasonsList, + this.seriesList, }); Map? page; List? seasonsList; + List? seriesList; MemberSeasonsDataModel.fromJson(Map json) { page = json['page']; @@ -19,6 +21,11 @@ class MemberSeasonsDataModel { .map((e) => MemberSeasonsList.fromJson(e)) .toList() : []; + seriesList = json['archives'] != null + ? json['archives'] + .map((e) => MemberArchiveItem.fromJson(e)) + .toList() + : []; seasonsList = [...tempList1, ...tempList2]; } @@ -93,6 +100,8 @@ class MamberMeta { this.ptime, this.seasonId, this.total, + this.seriesId, + this.category, }); String? cover; @@ -102,6 +111,8 @@ class MamberMeta { int? ptime; int? seasonId; int? total; + int? seriesId; + int? category; MamberMeta.fromJson(Map json) { cover = json['cover']; @@ -111,5 +122,7 @@ class MamberMeta { ptime = json['ptime']; seasonId = json['season_id']; total = json['total']; + seriesId = json['series_id']; + category = json['category']; } } diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 1367d6bd..1749ff45 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -24,8 +24,27 @@ class MemberSeasonsPanel extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( - onTap: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}&seasonName=${item.meta!.name}'), + onTap: () { + final int category = item.meta!.category!; + Map parameters = {}; + if (category == 0) { + parameters = { + 'category': '0', + 'mid': item.meta!.mid.toString(), + 'seasonId': item.meta!.seasonId.toString(), + 'seasonName': item.meta!.name!, + }; + } + if (category == 1) { + parameters = { + 'category': '1', + 'mid': item.meta!.mid.toString(), + 'seriesId': item.meta!.seriesId.toString(), + 'seasonName': item.meta!.name!, + }; + } + Get.toNamed('/memberSeasons', parameters: parameters); + }, title: Text( item.meta!.name!, maxLines: 1, diff --git a/lib/pages/member_seasons/controller.dart b/lib/pages/member_seasons/controller.dart index 82ef0af0..58a9035f 100644 --- a/lib/pages/member_seasons/controller.dart +++ b/lib/pages/member_seasons/controller.dart @@ -6,7 +6,9 @@ import 'package:pilipala/models/member/seasons.dart'; class MemberSeasonsController extends GetxController { final ScrollController scrollController = ScrollController(); late int mid; - late int seasonId; + int? seasonId; + int? seriesId; + late String category; int pn = 1; int ps = 30; int count = 0; @@ -17,17 +19,23 @@ class MemberSeasonsController extends GetxController { void onInit() { super.onInit(); mid = int.parse(Get.parameters['mid']!); - seasonId = int.parse(Get.parameters['seasonId']!); + category = Get.parameters['category']!; + if (category == '0') { + seasonId = int.parse(Get.parameters['seriesId']!); + } + if (category == '1') { + seriesId = int.parse(Get.parameters['seriesId']!); + } } - // 获取专栏详情 + // 获取专栏详情 0: 专栏 1: 系列 Future getSeasonDetail(type) async { if (type == 'onRefresh') { pn = 1; } var res = await MemberHttp.getSeasonDetail( mid: mid, - seasonId: seasonId, + seasonId: seasonId!, pn: pn, ps: ps, sortReverse: false, @@ -40,8 +48,32 @@ class MemberSeasonsController extends GetxController { return res; } + // 获取系列详情 0: 专栏 1: 系列 + Future getSeriesDetail(type) async { + if (type == 'onRefresh') { + pn = 1; + } + var res = await MemberHttp.getSeriesDetail( + mid: mid, + seriesId: seriesId!, + pn: pn, + currentMid: 17340771, + ); + if (res['status']) { + seasonsList.addAll(res['data'].seriesList); + page = res['data'].page; + pn += 1; + } + return res; + } + // 上拉加载 Future onLoad() async { - getSeasonDetail('onLoad'); + if (category == '0') { + getSeasonDetail('onLoad'); + } + if (category == '1') { + getSeriesDetail('onLoad'); + } } } diff --git a/lib/pages/member_seasons/view.dart b/lib/pages/member_seasons/view.dart index 556e2ec5..b8c0407d 100644 --- a/lib/pages/member_seasons/view.dart +++ b/lib/pages/member_seasons/view.dart @@ -17,12 +17,15 @@ class _MemberSeasonsPageState extends State { Get.put(MemberSeasonsController()); late Future _futureBuilderFuture; late ScrollController scrollController; + late String category; @override void initState() { super.initState(); - _futureBuilderFuture = - _memberSeasonsController.getSeasonDetail('onRefresh'); + category = Get.parameters['category']!; + _futureBuilderFuture = category == '0' + ? _memberSeasonsController.getSeasonDetail('onRefresh') + : _memberSeasonsController.getSeriesDetail('onRefresh'); scrollController = _memberSeasonsController.scrollController; scrollController.addListener( () { From 0bb9a68cc55cb65bbc1a5bf41c683a47f8d9827d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 9 Aug 2024 18:39:16 +0800 Subject: [PATCH 207/438] =?UTF-8?q?mod:=20=E4=BF=9D=E5=AD=98=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E6=97=B6=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/image_save.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0b77b7cc..99513ac8 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -57,7 +57,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { child: Row( children: [ Expanded( - child: Text( + child: SelectableText( videoItem.title! as String, style: Theme.of(context).textTheme.titleSmall, ), From 61337338bd42d0e43ffcee264a952b5d52318c6a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 10 Aug 2024 23:11:21 +0800 Subject: [PATCH 208/438] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88Hero=20tag=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/reply/widgets/reply_item.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5b7b0659..5f53b398 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:appscheme/appscheme.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; @@ -549,7 +551,7 @@ InlineSpan buildContent( ); } - void onPreviewImg(picList, initIndex) { + void onPreviewImg(picList, initIndex, randomInt) { final MainController mainController = Get.find(); mainController.imgPreviewStatus = true; Navigator.of(context).push( @@ -575,7 +577,7 @@ InlineSpan buildContent( }, child: Center( child: Hero( - tag: picList[index], + tag: picList[index] + randomInt, child: CachedNetworkImage( fadeInDuration: const Duration(milliseconds: 0), imageUrl: picList[index], @@ -886,11 +888,12 @@ InlineSpan buildContent( pictureItem['img_width'])) .truncateToDouble(); } catch (_) {} + String randomInt = Random().nextInt(101).toString(); return Hero( - tag: picList[0], + tag: picList[0] + randomInt, child: GestureDetector( - onTap: () => onPreviewImg(picList, 0), + onTap: () => onPreviewImg(picList, 0, randomInt), child: Container( padding: const EdgeInsets.only(top: 4), constraints: BoxConstraints(maxHeight: maxHeight), @@ -927,13 +930,14 @@ InlineSpan buildContent( picList.add(content.pictures[i]['img_src']); } for (var i = 0; i < len; i++) { + String randomInt = Random().nextInt(101).toString(); list.add( LayoutBuilder( builder: (context, BoxConstraints box) { return Hero( - tag: picList[i], + tag: picList[i] + randomInt, child: GestureDetector( - onTap: () => onPreviewImg(picList, i), + onTap: () => onPreviewImg(picList, i, randomInt), child: NetworkImgLayer( src: picList[i], width: box.maxWidth, From b188675faf2607e9e5d3eb5c60da0ae3863203fa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Aug 2024 18:43:21 +0800 Subject: [PATCH 209/438] opt --- .../detail/reply/widgets/reply_item.dart | 43 +++++-- .../detail/reply/widgets/reply_save.dart | 106 ++++++++++-------- 2 files changed, 88 insertions(+), 61 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 9fc8b11e..ae691f97 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -17,6 +17,7 @@ import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -33,6 +34,7 @@ class ReplyItem extends StatelessWidget { this.showReplyRow = true, this.replyReply, this.replyType, + this.replySave = false, super.key, }); final ReplyItemModel? replyItem; @@ -41,6 +43,7 @@ class ReplyItem extends StatelessWidget { final bool? showReplyRow; final Function? replyReply; final ReplyType? replyType; + final bool? replySave; @override Widget build(BuildContext context) { @@ -48,12 +51,18 @@ class ReplyItem extends StatelessWidget { child: InkWell( // 点击整个评论区 评论详情/回复 onTap: () { + if (replySave!) { + return; + } feedBack(); if (replyReply != null) { replyReply!(replyItem, null, replyItem!.replies!.isNotEmpty); } }, onLongPress: () { + if (replySave!) { + return; + } feedBack(); showModalBottomSheet( context: context, @@ -236,7 +245,7 @@ class ReplyItem extends StatelessWidget { ), ), // 操作区域 - bottonAction(context, replyItem!.replyControl), + bottonAction(context, replyItem!.replyControl, replySave), // 一楼的评论 if ((replyItem!.replyControl!.isShow! || replyItem!.replies!.isNotEmpty) && @@ -257,7 +266,7 @@ class ReplyItem extends StatelessWidget { } // 感谢、回复、复制 - Widget bottonAction(BuildContext context, replyControl) { + Widget bottonAction(BuildContext context, replyControl, replySave) { ColorScheme colorScheme = Theme.of(context).colorScheme; TextTheme textTheme = Theme.of(context).textTheme; return Row( @@ -290,16 +299,26 @@ class ReplyItem extends StatelessWidget { }); }, child: Row(children: [ - Icon(Icons.reply, - size: 18, color: colorScheme.outline.withOpacity(0.8)), - const SizedBox(width: 3), - Text( - '回复', - style: TextStyle( - fontSize: textTheme.labelMedium!.fontSize, - color: colorScheme.outline, + if (!replySave!) ...[ + Icon(Icons.reply, + size: 18, color: colorScheme.outline.withOpacity(0.8)), + const SizedBox(width: 3), + Text( + '回复', + style: TextStyle( + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, + ), + ) + ], + if (replySave!) + Text( + IdUtils.av2bv(replyItem!.oid!), + style: TextStyle( + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, + ), ), - ), ]), ), ), @@ -1093,7 +1112,7 @@ class MorePanel extends StatelessWidget { leading: const Icon(Icons.copy_outlined, size: 19), title: Text('自由复制', style: textTheme.titleSmall), ), - if (mainFloor) + if (mainFloor && item.content.pictures.isEmpty) ListTile( onTap: () async => await menuActionHandler('save'), minLeadingWidth: 0, diff --git a/lib/pages/video/detail/reply/widgets/reply_save.dart b/lib/pages/video/detail/reply/widgets/reply_save.dart index c083806f..9eed4923 100644 --- a/lib/pages/video/detail/reply/widgets/reply_save.dart +++ b/lib/pages/video/detail/reply/widgets/reply_save.dart @@ -76,64 +76,72 @@ class _ReplySaveState extends State { @override Widget build(BuildContext context) { - return Container( - width: Get.width, - height: Get.height, - margin: EdgeInsets.fromLTRB( - 0, - MediaQuery.of(context).padding.top + 4, - 0, - MediaQuery.of(context).padding.bottom + 4, - ), - color: Colors.transparent, - child: Column( - children: [ - Expanded( - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - child: Center( - child: SingleChildScrollView( - child: RepaintBoundary( - key: _boundaryKey, - child: IntrinsicHeight( - child: Stack( - children: [ - ReplyItem( - replyItem: widget.replyItem, - showReplyRow: false, + return SafeArea( + top: false, + bottom: false, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0), + child: Container( + width: Get.width, + height: Get.height, + padding: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + color: Colors.black54, + child: Column( + children: [ + Expanded( + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: SingleChildScrollView( + child: RepaintBoundary( + key: _boundaryKey, + child: IntrinsicHeight( + child: Stack( + children: [ + ReplyItem( + replyItem: widget.replyItem, + showReplyRow: false, + replySave: true, + ), + Positioned.fill( + child: Column( + children: _createColumnWidgets(), + ), + ), + ], ), - Positioned.fill( - child: Column( - children: _createColumnWidgets(), - ), - ), - ], + ), ), ), ), ), ), - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FilledButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - const SizedBox(width: 40), - FilledButton( - onPressed: _generatePicWidget, - child: const Text('保存'), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () => Get.back(), + child: const Text('取消'), + ), + const SizedBox(width: 40), + FilledButton( + onPressed: _generatePicWidget, + child: const Text('保存'), + ), + ], ), ], ), - ], + ), ), ); } From ec30235421b13dfba26b3b7a7685d1402eff0709 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Aug 2024 23:16:33 +0800 Subject: [PATCH 210/438] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 1e8d97f1..760976ae 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -613,6 +613,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } _controller.reverse(); }, + onTapCancel: () { + _controller.reverse(); + }, borderRadius: StyleString.mdRadius, child: SizedBox( width: (Get.size.width - 24) / 5, From 1012b5d00907e05616833a4f71f53300913d31ed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 18 Aug 2024 23:30:51 +0800 Subject: [PATCH 211/438] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 + lib/http/init.dart | 3 + lib/http/live.dart | 19 ++ lib/models/live/message.dart | 101 +++++++++++ lib/pages/live_room/controller.dart | 78 ++++++++ lib/pages/live_room/view.dart | 269 ++++++++++++++++++++++++++-- lib/plugin/pl_socket/index.dart | 107 +++++++++++ lib/utils/binary_writer.dart | 117 ++++++++++++ lib/utils/live.dart | 196 ++++++++++++++++++++ pubspec.lock | 14 +- pubspec.yaml | 2 + 11 files changed, 890 insertions(+), 20 deletions(-) create mode 100644 lib/models/live/message.dart create mode 100644 lib/plugin/pl_socket/index.dart create mode 100644 lib/utils/binary_writer.dart create mode 100644 lib/utils/live.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 31e5a38b..08a20382 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -554,4 +554,8 @@ class Api { /// 系统通知 static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + /// 直播间弹幕信息 + static const String getDanmuInfo = + '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index cb9d6f39..faa57dd5 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -29,6 +29,7 @@ class Request { late String systemProxyPort; static final RegExp spmPrefixExp = RegExp(r''); + static late String buvid; /// 设置cookie static setCookie() async { @@ -70,6 +71,8 @@ class Request { final String cookieString = cookie .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); + + buvid = cookie.firstWhere((e) => e.name == 'buvid3').value; dio.options.headers['cookie'] = cookieString; } diff --git a/lib/http/live.dart b/lib/http/live.dart index e624120e..a405fd58 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -65,4 +65,23 @@ class LiveHttp { }; } } + + // 获取弹幕信息 + static Future liveDanmakuInfo({roomId}) async { + var res = await Request().get(Api.getDanmuInfo, data: { + 'id': roomId, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/live/message.dart b/lib/models/live/message.dart new file mode 100644 index 00000000..cd0f4b75 --- /dev/null +++ b/lib/models/live/message.dart @@ -0,0 +1,101 @@ +class LiveMessageModel { + // 消息类型 + final LiveMessageType type; + + // 用户名 + final String userName; + + // 信息 + final String? message; + + // 数据 + final dynamic data; + + final String? face; + final int? uid; + final Map? emots; + + // 颜色 + final LiveMessageColor color; + + LiveMessageModel({ + required this.type, + required this.userName, + required this.message, + required this.color, + this.data, + this.face, + this.uid, + this.emots, + }); +} + +class LiveSuperChatMessage { + final String backgroundBottomColor; + final String backgroundColor; + final DateTime endTime; + final String face; + final String message; + final String price; + final DateTime startTime; + final String userName; + + LiveSuperChatMessage({ + required this.backgroundBottomColor, + required this.backgroundColor, + required this.endTime, + required this.face, + required this.message, + required this.price, + required this.startTime, + required this.userName, + }); +} + +enum LiveMessageType { + // 普通留言 + chat, + // 醒目留言 + superChat, + // + online, + // 加入 + join, + // 关注 + follow, +} + +class LiveMessageColor { + final int r, g, b; + LiveMessageColor(this.r, this.g, this.b); + static LiveMessageColor get white => LiveMessageColor(255, 255, 255); + static LiveMessageColor numberToColor(int intColor) { + var obj = intColor.toRadixString(16); + + LiveMessageColor color = LiveMessageColor.white; + if (obj.length == 4) { + obj = "00$obj"; + } + if (obj.length == 6) { + var R = int.parse(obj.substring(0, 2), radix: 16); + var G = int.parse(obj.substring(2, 4), radix: 16); + var B = int.parse(obj.substring(4, 6), radix: 16); + + color = LiveMessageColor(R, G, B); + } + if (obj.length == 8) { + var R = int.parse(obj.substring(2, 4), radix: 16); + var G = int.parse(obj.substring(4, 6), radix: 16); + var B = int.parse(obj.substring(6, 8), radix: 16); + //var A = int.parse(obj.substring(0, 2), radix: 16); + color = LiveMessageColor(R, G, B); + } + + return color; + } + + @override + String toString() { + return "#${r.toRadixString(16).padLeft(2, '0')}${g.toRadixString(16).padLeft(2, '0')}${b.toRadixString(16).padLeft(2, '0')}"; + } +} diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 4e67fa2c..fa95ce63 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,10 +1,16 @@ +import 'dart:convert'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; +import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; +import 'package:pilipala/models/live/message.dart'; import 'package:pilipala/models/live/quality.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import 'package:pilipala/plugin/pl_socket/index.dart'; +import 'package:pilipala/utils/live.dart'; import '../../models/live/room_info_h5.dart'; import '../../utils/storage.dart'; import '../../utils/video_utils.dart'; @@ -24,6 +30,13 @@ class LiveRoomController extends GetxController { int? tempCurrentQn; late List> acceptQnList; RxString currentQnDesc = ''.obs; + Box userInfoCache = GStrorage.userInfo; + int userId = 0; + PlSocket? plSocket; + List danmuHostList = []; + String token = ''; + // 弹幕消息列表 + RxList messageList = [].obs; @override void onInit() { @@ -43,6 +56,11 @@ class LiveRoomController extends GetxController { } // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); + final userInfo = userInfoCache.get('userInfoCache'); + if (userInfo != null && userInfo.mid != null) { + userId = userInfo.mid; + } + liveDanmakuInfo().then((value) => initSocket()); } playerInit(source) async { @@ -127,4 +145,64 @@ class LiveRoomController extends GetxController { .description; await queryLiveInfo(); } + + Future liveDanmakuInfo() async { + var res = await LiveHttp.liveDanmakuInfo(roomId: roomId); + if (res['status']) { + danmuHostList = (res["data"]["host_list"] as List) + .map((e) => '${e["host"]}:${e['wss_port']}') + .toList(); + token = res["data"]["token"]; + return res; + } + } + + // 建立socket + void initSocket() async { + final wsUrl = danmuHostList.isNotEmpty + ? danmuHostList.first + : "broadcastlv.chat.bilibili.com"; + plSocket = PlSocket( + url: 'wss://$wsUrl/sub', + heartTime: 30, + onReadyCb: () { + joinRoom(); + }, + onMessageCb: (message) { + final List? liveMsg = + LiveUtils.decodeMessage(message); + if (liveMsg != null) { + messageList.addAll(liveMsg + .where((msg) => msg.type == LiveMessageType.chat) + .toList()); + } + }, + onErrorCb: (e) { + print('error: $e'); + }, + ); + await plSocket?.connect(); + } + + void joinRoom() async { + var joinData = LiveUtils.encodeData( + json.encode({ + "uid": userId, + "roomid": roomId, + "protover": 3, + "buvid": Request.buvid, + "platform": "web", + "type": 2, + "key": token, + }), + 7, + ); + plSocket?.sendMessage(joinData); + } + + @override + void onClose() { + plSocket?.onClose(); + super.onClose(); + } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 37981b1d..d9b316e9 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -1,9 +1,12 @@ import 'dart:io'; import 'package:floating/floating.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/live/message.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -16,7 +19,8 @@ class LiveRoomPage extends StatefulWidget { State createState() => _LiveRoomPageState(); } -class _LiveRoomPageState extends State { +class _LiveRoomPageState extends State + with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); PlPlayerController? plPlayerController; late Future? _futureBuilder; @@ -25,6 +29,9 @@ class _LiveRoomPageState extends State { bool isShowCover = true; bool isPlay = true; Floating? floating; + final ScrollController _scrollController = ScrollController(); + late AnimationController fabAnimationCtr; + bool _shouldAutoScroll = true; @override void initState() { @@ -34,6 +41,13 @@ class _LiveRoomPageState extends State { } videoSourceInit(); _futureBuilderFuture = _liveRoomController.queryLiveInfo(); + // 监听滚动事件 + _scrollController.addListener(_onScroll); + fabAnimationCtr = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + value: 0.0, + ); } Future videoSourceInit() async { @@ -41,12 +55,52 @@ class _LiveRoomPageState extends State { plPlayerController = _liveRoomController.plPlayerController; } + void _onScroll() { + // 反向时,展示按钮 + if (_scrollController.position.userScrollDirection == + ScrollDirection.forward) { + _shouldAutoScroll = false; + fabAnimationCtr.forward(); + } else { + _shouldAutoScroll = true; + fabAnimationCtr.reverse(); + } + } + + // 监听messageList的变化,自动滚动到底部 + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _liveRoomController.messageList.listen((_) { + if (_shouldAutoScroll) { + _scrollToBottom(); + } + }); + } + + void _scrollToBottom() { + if (_scrollController.hasClients) { + _scrollController + .animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ) + .then((value) { + _shouldAutoScroll = true; + // fabAnimationCtr.forward(); + }); + } + } + @override void dispose() { plPlayerController!.dispose(); if (floating != null) { floating!.dispose(); } + _scrollController.dispose(); + fabAnimationCtr.dispose(); super.dispose(); } @@ -80,20 +134,6 @@ class _LiveRoomPageState extends State { backgroundColor: Colors.black, body: Stack( children: [ - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Opacity( - opacity: 0.8, - child: Image.asset( - 'assets/images/live/default_bg.webp', - fit: BoxFit.cover, - // width: Get.width, - // height: Get.height, - ), - ), - ), Obx( () => Positioned( left: 0, @@ -106,7 +146,7 @@ class _LiveRoomPageState extends State { .roomInfoH5.value.roomInfo?.appBackground != null ? Opacity( - opacity: 0.8, + opacity: 0.6, child: NetworkImgLayer( width: Get.width, height: Get.height, @@ -116,7 +156,15 @@ class _LiveRoomPageState extends State { '', ), ) - : const SizedBox(), + : Opacity( + opacity: 0.6, + child: Image.asset( + 'assets/images/live/default_bg.webp', + fit: BoxFit.cover, + // width: Get.width, + // height: Get.height, + ), + ), ), ), Column( @@ -198,8 +246,45 @@ class _LiveRoomPageState extends State { child: videoPlayerPanel, ), ), + const SizedBox(height: 20), + // 显示消息的列表 + buildMessageListUI( + context, + _liveRoomController, + _scrollController, + ), + // 底部安全距离 + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20, + ) ], ), + // 定位 快速滑动到底部 + Positioned( + right: 20, + bottom: MediaQuery.of(context).padding.bottom + 20, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 2), + end: const Offset(0, 0), + ).animate(CurvedAnimation( + parent: fabAnimationCtr, + curve: Curves.easeInOut, + )), + child: ElevatedButton.icon( + onPressed: () { + _scrollToBottom(); + }, + icon: const Icon(Icons.keyboard_arrow_down), // 图标 + label: const Text('新消息'), // 文字 + style: ElevatedButton.styleFrom( + // primary: Colors.blue, // 按钮背景颜色 + // onPrimary: Colors.white, // 按钮文字颜色 + padding: const EdgeInsets.fromLTRB(14, 12, 20, 12), // 按钮内边距 + ), + ), + ), + ), ], ), ); @@ -214,3 +299,153 @@ class _LiveRoomPageState extends State { } } } + +Widget buildMessageListUI( + BuildContext context, + LiveRoomController liveRoomController, + ScrollController scrollController, +) { + return Expanded( + child: Obx( + () => MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ShaderMask( + shaderCallback: (Rect bounds) { + return const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black, + Colors.black, + ], + stops: [0.0, 0.1, 1.0], + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Padding( + padding: EdgeInsets.only( + top: index == 0 ? 40.0 : 4.0, + bottom: 4.0, + left: 20.0, + right: 20.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, + ), + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), + ), + ); + }, + ), + ), + ), + ), + ); +} + +List buildMessageTextSpan( + BuildContext context, + LiveMessageModel liveMsgItem, +) { + final List inlineSpanList = []; + + // 是否包含表情包 + if (liveMsgItem.emots == null) { + // 没有表情包的消息 + inlineSpanList.add( + TextSpan( + text: liveMsgItem.message ?? '', + style: const TextStyle( + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 3.0, + color: Colors.black, + ), + Shadow( + offset: Offset(-1.0, -1.0), + blurRadius: 3.0, + color: Colors.black, + ), + ], + ), + ), + ); + } else { + // 有表情包的消息 使用正则匹配 表情包用图片渲染 + final List emotsKeys = liveMsgItem.emots!.keys.toList(); + final RegExp pattern = RegExp(emotsKeys.map(RegExp.escape).join('|')); + + liveMsgItem.message?.splitMapJoin( + pattern, + onMatch: (Match match) { + final emoteItem = liveMsgItem.emots![match.group(0)]; + if (emoteItem != null) { + inlineSpanList.add( + WidgetSpan( + child: NetworkImgLayer( + width: emoteItem['width'].toDouble(), + height: emoteItem['height'].toDouble(), + type: 'emote', + src: emoteItem['url'], + ), + ), + ); + } + return ''; + }, + onNonMatch: (String nonMatch) { + inlineSpanList.add( + TextSpan( + text: nonMatch, + style: const TextStyle( + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 3.0, + color: Colors.black, + ), + Shadow( + offset: Offset(-1.0, -1.0), + blurRadius: 3.0, + color: Colors.black, + ), + ], + ), + ), + ); + return nonMatch; + }, + ); + } + + return inlineSpanList; +} diff --git a/lib/plugin/pl_socket/index.dart b/lib/plugin/pl_socket/index.dart new file mode 100644 index 00000000..1ad6af94 --- /dev/null +++ b/lib/plugin/pl_socket/index.dart @@ -0,0 +1,107 @@ +import 'dart:async'; + +import 'package:pilipala/utils/live.dart'; +import 'package:web_socket_channel/io.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +enum SocketStatus { + connected, + failed, + closed, +} + +class PlSocket { + SocketStatus status = SocketStatus.closed; + // 链接 + final String url; + // 心跳时间 + final int heartTime; + // 监听初始化完成 + final Function? onReadyCb; + // 监听关闭 + final Function? onCloseCb; + // 监听异常 + final Function? onErrorCb; + // 监听消息 + final Function? onMessageCb; + // 请求头 + final Map? headers; + + PlSocket({ + required this.url, + required this.heartTime, + this.onReadyCb, + this.onCloseCb, + this.onErrorCb, + this.onMessageCb, + this.headers, + }); + + WebSocketChannel? channel; + StreamSubscription? channelStreamSub; + + // 建立连接 + Future connect() async { + // 连接之前关闭上次连接 + onClose(); + try { + channel = IOWebSocketChannel.connect( + url, + connectTimeout: const Duration(seconds: 15), + headers: null, + ); + await channel?.ready; + onReady(); + } catch (err) { + connect(); + onError(err); + } + } + + // 初始化完成 + void onReady() { + status = SocketStatus.connected; + onReadyCb?.call(); + channelStreamSub = channel?.stream.listen((message) { + onMessageCb?.call(message); + }, onDone: () { + // 流被关闭 + print('结束了'); + }, onError: (err) { + onError(err); + }); + // 每30s发送心跳 + Timer.periodic(Duration(seconds: heartTime), (timer) { + if (status == SocketStatus.connected) { + sendMessage(LiveUtils.encodeData( + "", + 2, + )); + } else { + timer.cancel(); + } + }); + } + + // 连接关闭 + void onClose() { + status = SocketStatus.closed; + onCloseCb?.call(); + channelStreamSub?.cancel(); + channel?.sink.close(); + } + + // 连接异常 + void onError(err) { + onErrorCb?.call(err); + } + + // 接收消息 + void onMessage() {} + + void sendMessage(dynamic message) { + if (status == SocketStatus.connected) { + channel?.sink.add(message); + } + } +} diff --git a/lib/utils/binary_writer.dart b/lib/utils/binary_writer.dart new file mode 100644 index 00000000..929bc573 --- /dev/null +++ b/lib/utils/binary_writer.dart @@ -0,0 +1,117 @@ +import 'dart:typed_data'; + +class BinaryWriter { + List buffer; + int position = 0; + BinaryWriter(this.buffer); + int get length => buffer.length; + + void writeBytes(List list) { + buffer.addAll(list); + position += list.length; + } + + void writeInt(int value, int len, {Endian endian = Endian.big}) { + var bytes = _createByteData(len); + switch (len) { + case 1: + bytes.setUint8(0, value.toUnsigned(8)); + break; + case 2: + bytes.setInt16(0, value, endian); + break; + case 4: + bytes.setInt32(0, value, endian); + break; + case 8: + bytes.setInt64(0, value, endian); + break; + default: + throw ArgumentError('Invalid length for writeInt: $len'); + } + _addBytesToBuffer(bytes, len); + } + + void writeDouble(double value, int len, {Endian endian = Endian.big}) { + var bytes = _createByteData(len); + switch (len) { + case 4: + bytes.setFloat32(0, value, endian); + break; + case 8: + bytes.setFloat64(0, value, endian); + break; + default: + throw ArgumentError('Invalid length for writeDouble: $len'); + } + _addBytesToBuffer(bytes, len); + } + + ByteData _createByteData(int len) { + var b = Uint8List(len).buffer; + return ByteData.view(b); + } + + void _addBytesToBuffer(ByteData bytes, int len) { + buffer.addAll(bytes.buffer.asUint8List()); + position += len; + } +} + +class BinaryReader { + Uint8List buffer; + int position = 0; + BinaryReader(this.buffer); + int get length => buffer.length; + + int read() { + return buffer[position++]; + } + + int readInt(int len, {Endian endian = Endian.big}) { + var bytes = _getBytes(len); + var data = ByteData.view(bytes.buffer); + switch (len) { + case 1: + return data.getUint8(0); + case 2: + return data.getInt16(0, endian); + case 4: + return data.getInt32(0, endian); + case 8: + return data.getInt64(0, endian); + default: + throw ArgumentError('Invalid length for readInt: $len'); + } + } + + int readByte({Endian endian = Endian.big}) => readInt(1, endian: endian); + int readShort({Endian endian = Endian.big}) => readInt(2, endian: endian); + int readInt32({Endian endian = Endian.big}) => readInt(4, endian: endian); + int readLong({Endian endian = Endian.big}) => readInt(8, endian: endian); + + Uint8List readBytes(int len) { + var bytes = _getBytes(len); + return bytes; + } + + double readFloat(int len, {Endian endian = Endian.big}) { + var bytes = _getBytes(len); + var data = ByteData.view(bytes.buffer); + switch (len) { + case 4: + return data.getFloat32(0, endian); + case 8: + return data.getFloat64(0, endian); + default: + throw ArgumentError('Invalid length for readFloat: $len'); + } + } + + Uint8List _getBytes(int len) { + var bytes = + Uint8List.fromList(buffer.getRange(position, position + len).toList()); + position += len; + return bytes; + } +} diff --git a/lib/utils/live.dart b/lib/utils/live.dart new file mode 100644 index 00000000..dd56616e --- /dev/null +++ b/lib/utils/live.dart @@ -0,0 +1,196 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:brotli/brotli.dart'; +import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/utils/binary_writer.dart'; + +class LiveUtils { + static List encodeData(String msg, int action) { + var data = utf8.encode(msg); + //头部长度固定16 + var length = data.length + 16; + var buffer = Uint8List(length); + + var writer = BinaryWriter([]); + + //数据包长度 + writer.writeInt(buffer.length, 4); + //数据包头部长度,固定16 + writer.writeInt(16, 2); + + //协议版本,0=JSON,1=Int32,2=Buffer + writer.writeInt(0, 2); + + //操作类型 + writer.writeInt(action, 4); + + //数据包头部长度,固定1 + + writer.writeInt(1, 4); + + writer.writeBytes(data); + + return writer.buffer; + } + + static List? decodeMessage(List data) { + try { + //操作类型。3=心跳回应,内容为房间人气值;5=通知,弹幕、广播等全部信息;8=进房回应,空 + int operation = readInt(data, 8, 4); + //内容 + var body = data.skip(16).toList(); + if (operation == 3) { + var online = readInt(body, 0, 4); + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.online, + userName: '', + message: '', + color: LiveMessageColor.white, + data: online, + ); + return [liveMsg]; + } else if (operation == 5) { + //协议版本。0为JSON,可以直接解析;1为房间人气值,Body为4位Int32;2为压缩过Buffer,需要解压再处理 + int protocolVersion = readInt(data, 6, 2); + if (protocolVersion == 2) { + body = zlib.decode(body); + } else if (protocolVersion == 3) { + body = brotli.decode(body); + } + + var text = utf8.decode(body, allowMalformed: true); + + var group = + text.split(RegExp(r"[\x00-\x1f]+", unicode: true, multiLine: true)); + List messages = []; + for (var item + in group.where((x) => x.length > 2 && x.startsWith('{'))) { + if (parseMessage(item) is LiveMessageModel) { + messages.add(parseMessage(item)!); + } + } + return messages; + } + } catch (e) { + print(e); + } + return null; + } + + static LiveMessageModel? parseMessage(String jsonMessage) { + try { + var obj = json.decode(jsonMessage); + var cmd = obj["cmd"].toString(); + if (cmd.contains("DANMU_MSG")) { + if (obj["info"] != null && obj["info"].length != 0) { + var message = obj["info"][1].toString(); + var color = asT(obj["info"][0][3]) ?? 0; + if (obj["info"][2] != null && obj["info"][2].length != 0) { + var extra = obj["info"][0][15]['extra']; + var user = obj["info"][0][15]['user']['base']; + Map extraMap = jsonDecode(extra); + final int userId = obj["info"][2][0]; + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.chat, + userName: user['name'], + message: message, + color: color == 0 + ? LiveMessageColor.white + : LiveMessageColor.numberToColor(color), + face: user['face'], + uid: userId, + emots: extraMap['emots'], + ); + return liveMsg; + } + } + } else if (cmd == "SUPER_CHAT_MESSAGE") { + if (obj["data"] == null) { + return null; + } + final data = obj["data"]; + final userInfo = data["user_info"]; + final String backgroundBottomColor = + data["background_bottom_color"].toString(); + final String backgroundColor = data["background_color"].toString(); + final DateTime endTime = + DateTime.fromMillisecondsSinceEpoch(data["end_time"] * 1000); + final String face = "${userInfo["face"]}@200w.jpg"; + final String message = data["message"].toString(); + final String price = data["price"]; + final DateTime startTime = + DateTime.fromMillisecondsSinceEpoch(data["start_time"] * 1000); + final String userName = userInfo["uname"].toString(); + + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.superChat, + userName: "SUPER_CHAT_MESSAGE", + message: "SUPER_CHAT_MESSAGE", + color: LiveMessageColor.white, + data: { + "backgroundBottomColor": backgroundBottomColor, + "backgroundColor": backgroundColor, + "endTime": endTime, + "face": face, + "message": message, + "price": price, + "startTime": startTime, + "userName": userName, + }, + ); + return liveMsg; + } else if (cmd == 'INTERACT_WORD') { + if (obj["data"] == null) { + return null; + } + final data = obj["data"]; + final String userName = data['uname']; + final int msgType = data['msg_type']; + final LiveMessageModel liveMsg = LiveMessageModel( + type: msgType == 1 ? LiveMessageType.join : LiveMessageType.follow, + userName: userName, + message: msgType == 1 ? '进入直播间' : '关注了主播', + color: LiveMessageColor.white, + ); + return liveMsg; + } + } catch (e) { + print(e); + } + return null; + } + + static T? asT(dynamic value) { + if (value is T) { + return value; + } + return null; + } + + static int readInt(List buffer, int start, int len) { + var data = _getByteData(buffer, start, len); + return _readIntFromByteData(data, len); + } + + static ByteData _getByteData(List buffer, int start, int len) { + var bytes = + Uint8List.fromList(buffer.getRange(start, start + len).toList()); + return ByteData.view(bytes.buffer); + } + + static int _readIntFromByteData(ByteData data, int len) { + switch (len) { + case 1: + return data.getUint8(0); + case 2: + return data.getInt16(0, Endian.big); + case 4: + return data.getInt32(0, Endian.big); + case 8: + return data.getInt64(0, Endian.big); + default: + throw ArgumentError('Invalid length: $len'); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index a46127f9..2dfaa961 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + brotli: + dependency: "direct main" + description: + name: brotli + sha256: "7f891558ed779aab2bed874f0a36b8123f9ff3f19cf6efbee89e18ed294945ae" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" build: dependency: transitive description: @@ -1656,13 +1664,13 @@ packages: source: hosted version: "0.5.1" web_socket_channel: - dependency: transitive + dependency: "direct main" description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.0" + version: "2.4.5" webview_cookie_manager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b5d45714..23c1426c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,8 @@ dependencies: lottie: ^3.1.2 # 二维码 qr_flutter: ^4.1.0 + web_socket_channel: ^2.4.5 + brotli: ^0.6.0 dev_dependencies: flutter_test: From 869e49bec2369e85a3ade02768888a7620b90924 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 21 Aug 2024 00:09:26 +0800 Subject: [PATCH 212/438] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 3 ++- lib/pages/danmaku/view.dart | 32 +++++++++++++++++------------ lib/pages/live_room/controller.dart | 28 ++++++++++++++++++++++--- lib/pages/live_room/view.dart | 15 ++++++++++++-- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 52c423d7..ed259b96 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -2,8 +2,9 @@ import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/models/danmaku/dm.pb.dart'; class PlDanmakuController { - PlDanmakuController(this.cid); + PlDanmakuController(this.cid, this.type); final int cid; + final String type; Map> dmSegMap = {}; // 已请求的段落标记 List requestedSeg = []; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 109f0206..e669b881 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -12,11 +12,15 @@ import 'package:pilipala/utils/storage.dart'; class PlDanmaku extends StatefulWidget { final int cid; final PlPlayerController playerController; + final String type; + final Function(DanmakuController)? createdController; const PlDanmaku({ super.key, required this.cid, required this.playerController, + this.type = 'video', + this.createdController, }); @override @@ -43,9 +47,9 @@ class _PlDanmakuState extends State { super.initState(); enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); - _plDanmakuController = PlDanmakuController(widget.cid); - if (mounted) { - playerController = widget.playerController; + _plDanmakuController = PlDanmakuController(widget.cid, widget.type); + playerController = widget.playerController; + if (mounted && widget.type == 'video') { if (enableShowDanmaku || playerController.isOpenDanmu.value) { _plDanmakuController.initiate( playerController.duration.value.inMilliseconds, @@ -55,13 +59,15 @@ class _PlDanmakuState extends State { ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } - playerController.isOpenDanmu.listen((p0) { - if (p0 && !_plDanmakuController.initiated) { - _plDanmakuController.initiate( - playerController.duration.value.inMilliseconds, - playerController.position.value.inMilliseconds); - } - }); + if (widget.type == 'video') { + playerController.isOpenDanmu.listen((p0) { + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds); + } + }); + } blockTypes = playerController.blockTypes; showArea = playerController.showArea; opacityVal = playerController.opacityVal; @@ -123,11 +129,12 @@ class _PlDanmakuState extends State { // double initDuration = box.maxWidth / 12; return Obx( () => AnimatedOpacity( - opacity: playerController.isOpenDanmu.value ? 1 : 0, + opacity: playerController.isOpenDanmu.value ? 1 : 1, duration: const Duration(milliseconds: 100), child: DanmakuView( createdController: (DanmakuController e) async { playerController.danmakuController = _controller = e; + widget.createdController?.call(e); }, option: DanmakuOption( fontSize: 15 * fontSizeVal, @@ -136,8 +143,7 @@ class _PlDanmakuState extends State { hideTop: blockTypes.contains(5), hideScroll: blockTypes.contains(2), hideBottom: blockTypes.contains(4), - duration: - danmakuDurationVal / playerController.playbackSpeed, + duration: danmakuDurationVal / playerController.playbackSpeed, strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index fa95ce63..6a3525e3 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,7 +1,9 @@ import 'dart:convert'; +import 'dart:ui'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; @@ -37,6 +39,7 @@ class LiveRoomController extends GetxController { String token = ''; // 弹幕消息列表 RxList messageList = [].obs; + DanmakuController? danmakuController; @override void onInit() { @@ -172,9 +175,28 @@ class LiveRoomController extends GetxController { final List? liveMsg = LiveUtils.decodeMessage(message); if (liveMsg != null) { - messageList.addAll(liveMsg - .where((msg) => msg.type == LiveMessageType.chat) - .toList()); + // 过滤出聊天消息 + var chatMessages = + liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); + + // 添加到 messageList + messageList.addAll(chatMessages); + + // 将 chatMessages 转换为 danmakuItems 列表 + List danmakuItems = chatMessages.map((e) { + return DanmakuItem( + e.message ?? '', + color: Color.fromARGB( + 255, + e.color.r, + e.color.g, + e.color.b, + ), + ); + }).toList(); + + // 添加到 danmakuController + danmakuController?.addItems(danmakuItems); } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d9b316e9..d1fc6e07 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -22,7 +23,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - PlPlayerController? plPlayerController; + late PlPlayerController plPlayerController; late Future? _futureBuilder; late Future? _futureBuilderFuture; @@ -32,6 +33,7 @@ class _LiveRoomPageState extends State final ScrollController _scrollController = ScrollController(); late AnimationController fabAnimationCtr; bool _shouldAutoScroll = true; + final int roomId = int.parse(Get.parameters['roomid']!); @override void initState() { @@ -110,8 +112,9 @@ class _LiveRoomPageState extends State future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { + plPlayerController = _liveRoomController.plPlayerController; return PLVideoPlayer( - controller: plPlayerController!, + controller: plPlayerController, bottomControl: BottomControl( controller: plPlayerController, liveRoomCtr: _liveRoomController, @@ -122,6 +125,14 @@ class _LiveRoomPageState extends State }); }, ), + danmuWidget: PlDanmaku( + cid: roomId, + playerController: plPlayerController, + type: 'live', + createdController: (e) { + _liveRoomController.danmakuController = e; + }, + ), ); } else { return const SizedBox(); From a074a360f2f1370a554d9248aa72909b841b268c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 21 Aug 2024 00:14:45 +0800 Subject: [PATCH 213/438] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 3 ++- lib/pages/danmaku/view.dart | 30 +++++++++++++++++------------ lib/pages/live_room/controller.dart | 28 ++++++++++++++++++++++++--- lib/pages/live_room/view.dart | 15 +++++++++++++-- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 52c423d7..ed259b96 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -2,8 +2,9 @@ import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/models/danmaku/dm.pb.dart'; class PlDanmakuController { - PlDanmakuController(this.cid); + PlDanmakuController(this.cid, this.type); final int cid; + final String type; Map> dmSegMap = {}; // 已请求的段落标记 List requestedSeg = []; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 109f0206..3cf1ed8a 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -12,11 +12,15 @@ import 'package:pilipala/utils/storage.dart'; class PlDanmaku extends StatefulWidget { final int cid; final PlPlayerController playerController; + final String type; + final Function(DanmakuController)? createdController; const PlDanmaku({ super.key, required this.cid, required this.playerController, + this.type = 'video', + this.createdController, }); @override @@ -43,9 +47,9 @@ class _PlDanmakuState extends State { super.initState(); enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); - _plDanmakuController = PlDanmakuController(widget.cid); - if (mounted) { - playerController = widget.playerController; + _plDanmakuController = PlDanmakuController(widget.cid, widget.type); + playerController = widget.playerController; + if (mounted && widget.type == 'video') { if (enableShowDanmaku || playerController.isOpenDanmu.value) { _plDanmakuController.initiate( playerController.duration.value.inMilliseconds, @@ -55,13 +59,15 @@ class _PlDanmakuState extends State { ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } - playerController.isOpenDanmu.listen((p0) { - if (p0 && !_plDanmakuController.initiated) { - _plDanmakuController.initiate( - playerController.duration.value.inMilliseconds, - playerController.position.value.inMilliseconds); - } - }); + if (widget.type == 'video') { + playerController.isOpenDanmu.listen((p0) { + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds); + } + }); + } blockTypes = playerController.blockTypes; showArea = playerController.showArea; opacityVal = playerController.opacityVal; @@ -128,6 +134,7 @@ class _PlDanmakuState extends State { child: DanmakuView( createdController: (DanmakuController e) async { playerController.danmakuController = _controller = e; + widget.createdController?.call(e); }, option: DanmakuOption( fontSize: 15 * fontSizeVal, @@ -136,8 +143,7 @@ class _PlDanmakuState extends State { hideTop: blockTypes.contains(5), hideScroll: blockTypes.contains(2), hideBottom: blockTypes.contains(4), - duration: - danmakuDurationVal / playerController.playbackSpeed, + duration: danmakuDurationVal / playerController.playbackSpeed, strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index fa95ce63..6a3525e3 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,7 +1,9 @@ import 'dart:convert'; +import 'dart:ui'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; @@ -37,6 +39,7 @@ class LiveRoomController extends GetxController { String token = ''; // 弹幕消息列表 RxList messageList = [].obs; + DanmakuController? danmakuController; @override void onInit() { @@ -172,9 +175,28 @@ class LiveRoomController extends GetxController { final List? liveMsg = LiveUtils.decodeMessage(message); if (liveMsg != null) { - messageList.addAll(liveMsg - .where((msg) => msg.type == LiveMessageType.chat) - .toList()); + // 过滤出聊天消息 + var chatMessages = + liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); + + // 添加到 messageList + messageList.addAll(chatMessages); + + // 将 chatMessages 转换为 danmakuItems 列表 + List danmakuItems = chatMessages.map((e) { + return DanmakuItem( + e.message ?? '', + color: Color.fromARGB( + 255, + e.color.r, + e.color.g, + e.color.b, + ), + ); + }).toList(); + + // 添加到 danmakuController + danmakuController?.addItems(danmakuItems); } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d9b316e9..d1fc6e07 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -22,7 +23,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - PlPlayerController? plPlayerController; + late PlPlayerController plPlayerController; late Future? _futureBuilder; late Future? _futureBuilderFuture; @@ -32,6 +33,7 @@ class _LiveRoomPageState extends State final ScrollController _scrollController = ScrollController(); late AnimationController fabAnimationCtr; bool _shouldAutoScroll = true; + final int roomId = int.parse(Get.parameters['roomid']!); @override void initState() { @@ -110,8 +112,9 @@ class _LiveRoomPageState extends State future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { + plPlayerController = _liveRoomController.plPlayerController; return PLVideoPlayer( - controller: plPlayerController!, + controller: plPlayerController, bottomControl: BottomControl( controller: plPlayerController, liveRoomCtr: _liveRoomController, @@ -122,6 +125,14 @@ class _LiveRoomPageState extends State }); }, ), + danmuWidget: PlDanmaku( + cid: roomId, + playerController: plPlayerController, + type: 'live', + createdController: (e) { + _liveRoomController.danmakuController = e; + }, + ), ); } else { return const SizedBox(); From 44910b35d97fd29c7ad30fe60f497a50872f662c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Aug 2024 20:24:21 +0800 Subject: [PATCH 214/438] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/live.dart | 33 ++++++ lib/pages/live_room/controller.dart | 21 +++- lib/pages/live_room/view.dart | 163 +++++++++++++++------------- 4 files changed, 145 insertions(+), 75 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 08a20382..65b74d2d 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -558,4 +558,7 @@ class Api { /// 直播间弹幕信息 static const String getDanmuInfo = '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo'; + + /// 直播间发送弹幕 + static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send'; } diff --git a/lib/http/live.dart b/lib/http/live.dart index a405fd58..f6fc4ea4 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -84,4 +84,37 @@ class LiveHttp { }; } } + + // 发送弹幕 + static Future sendDanmaku({roomId, msg}) async { + var res = await Request().post(Api.sendLiveMsg, queryParameters: { + 'bubble': 0, + 'msg': msg, + 'color': 16777215, // 颜色 + 'mode': 1, // 模式 + 'room_type': 0, + 'jumpfrom': 71001, // 直播间来源 + 'reply_mid': 0, + 'reply_attr': 0, + 'replay_dmid': '', + 'statistics': {"appId": 100, "platform": 5}, + 'fontsize': 25, // 字体大小 + 'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳 + 'roomid': roomId, + 'csrf': await Request.getCsrf(), + 'csrf_token': await Request.getCsrf(), + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 6a3525e3..e8db54fb 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'dart:ui'; +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; @@ -40,6 +40,8 @@ class LiveRoomController extends GetxController { // 弹幕消息列表 RxList messageList = [].obs; DanmakuController? danmakuController; + // 输入控制器 + TextEditingController inputController = TextEditingController(); @override void onInit() { @@ -222,6 +224,23 @@ class LiveRoomController extends GetxController { plSocket?.sendMessage(joinData); } + // 发送弹幕 + void sendMsg() async { + final msg = inputController.text; + if (msg.isEmpty) { + return; + } + final res = await LiveHttp.sendDanmaku( + roomId: roomId, + msg: msg, + ); + if (res['status']) { + inputController.clear(); + } else { + SmartDialog.showToast(res['msg']); + } + } + @override void onClose() { plSocket?.onClose(); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d1fc6e07..92ea8fd8 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -97,7 +97,7 @@ class _LiveRoomPageState extends State @override void dispose() { - plPlayerController!.dispose(); + plPlayerController.dispose(); if (floating != null) { floating!.dispose(); } @@ -238,10 +238,10 @@ class _LiveRoomPageState extends State ), ), PopScope( - canPop: plPlayerController?.isFullScreen.value != true, + canPop: plPlayerController.isFullScreen.value != true, onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); + if (plPlayerController.isFullScreen.value == true) { + plPlayerController.triggerFullScreen(status: false); } if (MediaQuery.of(context).orientation == Orientation.landscape) { @@ -257,17 +257,53 @@ class _LiveRoomPageState extends State child: videoPlayerPanel, ), ), - const SizedBox(height: 20), // 显示消息的列表 buildMessageListUI( context, _liveRoomController, _scrollController, ), - // 底部安全距离 - SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, - ) + // 弹幕输入框 + Container( + padding: EdgeInsets.only( + left: 14, + right: 14, + top: 4, + bottom: MediaQuery.of(context).padding.bottom + 20), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + border: Border( + top: BorderSide( + color: Colors.white.withOpacity(0.1), + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _liveRoomController.inputController, + style: + const TextStyle(color: Colors.white, fontSize: 13), + decoration: InputDecoration( + hintText: '发送弹幕', + hintStyle: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + border: InputBorder.none, + ), + ), + ), + IconButton( + onPressed: () => _liveRoomController.sendMsg(), + icon: const Icon( + Icons.send, + color: Colors.white, + ), + ), + ], + ), + ), ], ), // 定位 快速滑动到底部 @@ -324,15 +360,15 @@ Widget buildMessageListUI( removeBottom: true, child: ShaderMask( shaderCallback: (Rect bounds) { - return const LinearGradient( + return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, - Colors.black, + Colors.black.withOpacity(0.5), Colors.black, ], - stops: [0.0, 0.1, 1.0], + stops: const [0.01, 0.05, 0.2], ).createShader(bounds); }, blendMode: BlendMode.dstIn, @@ -342,35 +378,46 @@ Widget buildMessageListUI( itemBuilder: (context, index) { final LiveMessageModel liveMsgItem = liveRoomController.messageList[index]; - return Padding( - padding: EdgeInsets.only( - top: index == 0 ? 40.0 : 4.0, - bottom: 4.0, - left: 20.0, - right: 20.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, - ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), ), ), ); @@ -392,23 +439,7 @@ List buildMessageTextSpan( if (liveMsgItem.emots == null) { // 没有表情包的消息 inlineSpanList.add( - TextSpan( - text: liveMsgItem.message ?? '', - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: liveMsgItem.message ?? ''), ); } else { // 有表情包的消息 使用正则匹配 表情包用图片渲染 @@ -435,23 +466,7 @@ List buildMessageTextSpan( }, onNonMatch: (String nonMatch) { inlineSpanList.add( - TextSpan( - text: nonMatch, - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: nonMatch), ); return nonMatch; }, From 844db213a56d480934aecf8b786ab5028d8e2cbc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Aug 2024 23:57:11 +0800 Subject: [PATCH 215/438] =?UTF-8?q?mod:=20=E8=BE=93=E5=85=A5=E6=A1=86?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 27 ++++++++++++++- lib/pages/live_room/view.dart | 52 +++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index e8db54fb..a56a5459 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -42,6 +43,8 @@ class LiveRoomController extends GetxController { DanmakuController? danmakuController; // 输入控制器 TextEditingController inputController = TextEditingController(); + // 加入直播间提示 + RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; @override void onInit() { @@ -176,7 +179,29 @@ class LiveRoomController extends GetxController { onMessageCb: (message) { final List? liveMsg = LiveUtils.decodeMessage(message); - if (liveMsg != null) { + if (liveMsg != null && liveMsg.isNotEmpty) { + if (liveMsg.first.type == LiveMessageType.online) { + print('当前直播间人气:${liveMsg.first.data}'); + } else if (liveMsg.first.type == LiveMessageType.join || + liveMsg.first.type == LiveMessageType.follow) { + // 每隔一秒依次liveMsg中的每一项赋给activeUserName + int index = 0; + Timer.periodic(const Duration(seconds: 2), (timer) { + if (index < liveMsg.length) { + if (liveMsg[index].type == LiveMessageType.join || + liveMsg[index].type == LiveMessageType.follow) { + joinRoomTip.value = { + 'userName': liveMsg[index].userName, + 'message': liveMsg[index].message!, + }; + } + index++; + } else { + timer.cancel(); + } + }); + return; + } // 过滤出聊天消息 var chatMessages = liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 92ea8fd8..3f563ad6 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -179,6 +179,7 @@ class _LiveRoomPageState extends State ), ), Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ AppBar( centerTitle: false, @@ -263,6 +264,49 @@ class _LiveRoomPageState extends State _liveRoomController, _scrollController, ), + // Container( + // padding: const EdgeInsets.only( + // left: 14, right: 14, top: 4, bottom: 4), + // margin: const EdgeInsets.only( + // bottom: 6, + // left: 14, + // ), + // decoration: BoxDecoration( + // color: Colors.grey.withOpacity(0.1), + // borderRadius: const BorderRadius.all(Radius.circular(20)), + // ), + // child: Obx( + // () => AnimatedSwitcher( + // duration: const Duration(milliseconds: 300), + // transitionBuilder: + // (Widget child, Animation animation) { + // return FadeTransition(opacity: animation, child: child); + // }, + // child: Text.rich( + // key: + // ValueKey(_liveRoomController.joinRoomTip['userName']), + // TextSpan( + // style: const TextStyle(color: Colors.white), + // children: [ + // TextSpan( + // text: + // '${_liveRoomController.joinRoomTip['userName']} ', + // style: TextStyle( + // color: Colors.white.withOpacity(0.6), + // ), + // ), + // TextSpan( + // text: + // '${_liveRoomController.joinRoomTip['message']}', + // style: const TextStyle(color: Colors.white), + // ), + // ], + // ), + // ), + // ), + // ), + // ), + const SizedBox(height: 10), // 弹幕输入框 Container( padding: EdgeInsets.only( @@ -271,7 +315,8 @@ class _LiveRoomPageState extends State top: 4, bottom: MediaQuery.of(context).padding.bottom + 20), decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), border: Border( top: BorderSide( color: Colors.white.withOpacity(0.1), @@ -280,6 +325,7 @@ class _LiveRoomPageState extends State ), child: Row( children: [ + const SizedBox(width: 4), Expanded( child: TextField( controller: _liveRoomController.inputController, @@ -309,10 +355,10 @@ class _LiveRoomPageState extends State // 定位 快速滑动到底部 Positioned( right: 20, - bottom: MediaQuery.of(context).padding.bottom + 20, + bottom: MediaQuery.of(context).padding.bottom + 80, child: SlideTransition( position: Tween( - begin: const Offset(0, 2), + begin: const Offset(0, 4), end: const Offset(0, 0), ).animate(CurvedAnimation( parent: fabAnimationCtr, From 61e019f458542e8f25a14d8153044df9e14f361f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Aug 2024 00:20:20 +0800 Subject: [PATCH 216/438] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A1=A5=E5=85=85=EF=BC=88=E7=9B=B4=E6=92=AD=E5=9B=9E?= =?UTF-8?q?=E6=94=BE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/widgets/seasons.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 1749ff45..615fc44c 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -35,7 +35,8 @@ class MemberSeasonsPanel extends StatelessWidget { 'seasonName': item.meta!.name!, }; } - if (category == 1) { + // 2为直播回放 + if (category == 1 || category == 2) { parameters = { 'category': '1', 'mid': item.meta!.mid.toString(), From fc22834e4a66e4d3fd803c3ec0455f13203bfcde Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Aug 2024 23:47:15 +0800 Subject: [PATCH 217/438] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/view.dart | 2 +- lib/pages/live_room/controller.dart | 12 ++- lib/pages/live_room/view.dart | 146 ++++++++++++++++++---------- 3 files changed, 105 insertions(+), 55 deletions(-) diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index e669b881..3cf1ed8a 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -129,7 +129,7 @@ class _PlDanmakuState extends State { // double initDuration = box.maxWidth / 12; return Obx( () => AnimatedOpacity( - opacity: playerController.isOpenDanmu.value ? 1 : 1, + opacity: playerController.isOpenDanmu.value ? 1 : 0, duration: const Duration(milliseconds: 100), child: DanmakuView( createdController: (DanmakuController e) async { diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index a56a5459..99025dce 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -45,6 +45,8 @@ class LiveRoomController extends GetxController { TextEditingController inputController = TextEditingController(); // 加入直播间提示 RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; + // 直播间弹幕开关 默认打开 + RxBool danmakuSwitch = true.obs; @override void onInit() { @@ -69,6 +71,9 @@ class LiveRoomController extends GetxController { userId = userInfo.mid; } liveDanmakuInfo().then((value) => initSocket()); + danmakuSwitch.listen((p0) { + plPlayerController.isOpenDanmu.value = p0; + }); } playerInit(source) async { @@ -87,6 +92,7 @@ class LiveRoomController extends GetxController { enableHA: true, autoplay: true, ); + plPlayerController.isOpenDanmu.value = danmakuSwitch.value; } Future queryLiveInfo() async { @@ -185,6 +191,7 @@ class LiveRoomController extends GetxController { } else if (liveMsg.first.type == LiveMessageType.join || liveMsg.first.type == LiveMessageType.follow) { // 每隔一秒依次liveMsg中的每一项赋给activeUserName + int index = 0; Timer.periodic(const Duration(seconds: 2), (timer) { if (index < liveMsg.length) { @@ -200,6 +207,7 @@ class LiveRoomController extends GetxController { timer.cancel(); } }); + return; } // 过滤出聊天消息 @@ -223,7 +231,9 @@ class LiveRoomController extends GetxController { }).toList(); // 添加到 danmakuController - danmakuController?.addItems(danmakuItems); + if (danmakuSwitch.value) { + danmakuController?.addItems(danmakuItems); + } } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 3f563ad6..abbcce13 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -325,7 +325,33 @@ class _LiveRoomPageState extends State ), child: Row( children: [ - const SizedBox(width: 4), + SizedBox( + width: 34, + height: 34, + child: Obx( + () => IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + return Colors.grey.withOpacity(0.1); + }), + ), + onPressed: () { + _liveRoomController.danmakuSwitch.value = + !_liveRoomController.danmakuSwitch.value; + }, + icon: Icon( + _liveRoomController.danmakuSwitch.value + ? Icons.subtitles_outlined + : Icons.subtitles_off_outlined, + size: 19, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(width: 8), Expanded( child: TextField( controller: _liveRoomController.inputController, @@ -340,11 +366,19 @@ class _LiveRoomPageState extends State ), ), ), - IconButton( - onPressed: () => _liveRoomController.sendMsg(), - icon: const Icon( - Icons.send, - color: Colors.white, + SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => _liveRoomController.sendMsg(), + icon: const Icon( + Icons.send, + color: Colors.white, + size: 20, + ), ), ), ], @@ -418,56 +452,62 @@ Widget buildMessageListUI( ).createShader(bounds); }, blendMode: BlendMode.dstIn, - child: ListView.builder( - controller: scrollController, - itemCount: liveRoomController.messageList.length, - itemBuilder: (context, index) { - final LiveMessageModel liveMsgItem = - liveRoomController.messageList[index]; - return Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - margin: EdgeInsets.only( - top: index == 0 ? 20.0 : 0.0, - bottom: 6.0, - left: 14.0, - right: 14.0, - ), - padding: const EdgeInsets.symmetric( - vertical: 3.0, - horizontal: 10.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), + child: GestureDetector( + onTap: () { + // 键盘失去焦点 + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, - ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ), ), From 15e1cb5d4763a3b63b4c44e861b1dd79df7e6c6e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 00:07:08 +0800 Subject: [PATCH 218/438] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E5=8F=AF?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 77e38073..94347aff 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -84,7 +84,7 @@ class ChatItem extends StatelessWidget { emojiMap[e['text']] = e['url']; } text.splitMapJoin( - RegExp(r"\[.+?\]"), + RegExp(r"\[[^\[\]]+\]"), onMatch: (Match match) { final String emojiKey = match[0]!; if (emojiMap.containsKey(emojiKey)) { @@ -95,6 +95,17 @@ class ChatItem extends StatelessWidget { src: emojiMap[emojiKey]!, ), )); + } else { + children.add( + TextSpan( + text: emojiKey, + style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ), + ), + ); } return ''; }, @@ -109,13 +120,13 @@ class ChatItem extends StatelessWidget { return ''; }, ); - return RichText( - text: TextSpan( + return SelectableText.rich( + TextSpan( children: children, ), ); } else { - return Text( + return SelectableText( text, style: TextStyle( letterSpacing: 0.6, @@ -133,7 +144,7 @@ class ChatItem extends StatelessWidget { case MsgType.pic_card: return SystemNotice2(item: item); case MsgType.notify_text: - return Text( + return SelectableText( jsonDecode(content['content']) .map((m) => m['text'] as String) .join("\n"), @@ -530,7 +541,7 @@ class SystemNotice extends StatelessWidget { Divider( color: Theme.of(context).colorScheme.primary.withOpacity(0.05), ), - Text( + SelectableText( content['text'], ) ], From b96f35bae2f2f3e1458a0fdb66e1976fddc02f91 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 00:28:07 +0800 Subject: [PATCH 219/438] =?UTF-8?q?mod:=20=E7=A7=BB=E9=99=A4up=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E5=88=86=E5=89=B2=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/view.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index c721d638..7d2e4bed 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -168,7 +168,6 @@ class _MemberPageState extends State const Icon(Icons.arrow_forward_outlined, size: 19), ), ), - const Divider(height: 1, thickness: 0.1), /// 视频 Obx(() => ListTile( @@ -178,7 +177,6 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), )), - const Divider(height: 1, thickness: 0.1), /// 他的收藏夹 Obx(() => ListTile( @@ -188,13 +186,11 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), )), - const Divider(height: 1, thickness: 0.1), /// 专栏 Obx(() => ListTile( title: Text( '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'))), - const Divider(height: 1, thickness: 0.1), /// 合集 Obx(() => ListTile( From 8810a74ebfcfa4cc9e52226089f0e6c79a70a399 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 23:33:19 +0800 Subject: [PATCH 220/438] =?UTF-8?q?fix:=20=E6=9C=AA=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E6=97=B6=E8=BF=94=E5=9B=9E=E5=BD=93=E5=89=8D=E9=A1=B5=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0388e962..91c5ae00 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -63,7 +63,7 @@ class _VideoDetailPageState extends State late bool autoPlayEnable; late bool autoPiP; late Floating floating; - bool isShowing = true; + RxBool isShowing = true.obs; // 生命周期监听 late final AppLifecycleListener _lifecycleListener; late double statusHeight; @@ -183,6 +183,7 @@ class _VideoDetailPageState extends State plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; vdCtr.isShowCover.value = false; + isShowing.value = true; autoEnterPip(status: PlayerStatus.playing); } @@ -258,7 +259,7 @@ class _VideoDetailPageState extends State plPlayerController!.pause(); vdCtr.clearSubtitleContent(); } - setState(() => isShowing = false); + isShowing.value = false; super.didPushNext(); } @@ -272,10 +273,8 @@ class _VideoDetailPageState extends State if (plPlayerController != null && plPlayerController!.videoPlayerController != null) { - setState(() { - vdCtr.setSubtitleContent(); - isShowing = true; - }); + vdCtr.setSubtitleContent(); + isShowing.value = true; } vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; @@ -652,7 +651,11 @@ class _VideoDetailPageState extends State tag: heroTag, child: Stack( children: [ - if (isShowing) buildVideoPlayerPanel(), + Obx( + () => isShowing.value + ? buildVideoPlayerPanel() + : const SizedBox(), + ), /// 关闭自动播放时 手动播放 Obx( From 8fd3bfae5f9a88a8ce72670b2499f035e51b4f0e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Aug 2024 22:07:42 +0800 Subject: [PATCH 221/438] =?UTF-8?q?fix:=20=E7=B3=BB=E7=BB=9F=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=A0=87=E8=AE=B0=E5=B7=B2=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++++ lib/http/msg.dart | 20 +++++++++++++++++++- lib/pages/message/system/controller.dart | 14 +++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 31e5a38b..d9286e47 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -554,4 +554,8 @@ class Api { /// 系统通知 static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + /// 系统通知标记已读 + static const String systemMarkRead = + '${HttpString.messageBaseUrl}/x/sys-msg/update_cursor'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 86789fd1..2de9cd49 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -298,7 +298,6 @@ class MsgHttp { }); if (res.data['code'] == 0) { try { - print(res.data['data']['system_notify_list']); return { 'status': true, 'data': res.data['data']['system_notify_list'] @@ -312,4 +311,23 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + // 系统消息标记已读 + static Future systemMarkRead(int cursor) async { + String csrf = await Request.getCsrf(); + var res = await Request().get(Api.systemMarkRead, data: { + 'csrf': csrf, + 'cursor': cursor, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart index bf31f6bc..f63a659a 100644 --- a/lib/pages/message/system/controller.dart +++ b/lib/pages/message/system/controller.dart @@ -8,8 +8,20 @@ class MessageSystemController extends GetxController { Future queryMessageSystem({String type = 'init'}) async { var res = await MsgHttp.messageSystem(); if (res['status']) { - systemItems.addAll(res['data']); + if (type == 'init') { + systemItems.value = res['data']; + } else { + systemItems.addAll(res['data']); + } + if (systemItems.isNotEmpty) { + systemMarkRead(systemItems.first.cursor!); + } } return res; } + + // 标记已读 + void systemMarkRead(int cursor) async { + await MsgHttp.systemMarkRead(cursor); + } } From d85e9446e3d634ad5c1cb3eecec45301a10a73e8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 00:12:35 +0800 Subject: [PATCH 222/438] opt: fav follow bottomSheet --- lib/pages/follow/widgets/follow_item.dart | 19 +++++-- .../video/detail/introduction/controller.dart | 19 +++++-- lib/pages/video/detail/introduction/view.dart | 49 ++++++++++--------- .../introduction/widgets/fav_panel.dart | 43 +++++++++------- .../introduction/widgets/group_panel.dart | 16 ++++-- pubspec.lock | 16 ++++++ pubspec.yaml | 1 + 7 files changed, 111 insertions(+), 52 deletions(-) diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index d21a89bc..3d393277 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -1,3 +1,4 @@ +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -47,9 +48,21 @@ class FollowItem extends StatelessWidget { height: 34, child: TextButton( onPressed: () async { - await Get.bottomSheet( - GroupPanel(mid: item.mid!), - isScrollControlled: true, + await showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 1, + initHeight: 1, + maxHeight: 1, + context: Get.context!, + builder: (BuildContext context, + ScrollController scrollController, double offset) { + return GroupPanel( + mid: item.mid!, + scrollController: scrollController, + ); + }, + anchors: [1], + isSafeArea: true, ); }, style: TextButton.styleFrom( diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 50aac4cd..d564aba4 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -523,9 +524,21 @@ class VideoIntroController extends GetxController { // 设置关注分组 void setFollowGroup() { - Get.bottomSheet( - GroupPanel(mid: videoDetail.value.owner!.mid!), - isScrollControlled: true, + showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 0.6, + initHeight: 0.6, + maxHeight: 1, + context: Get.context!, + builder: (BuildContext context, ScrollController scrollController, + double offset) { + return GroupPanel( + mid: videoDetail.value.owner!.mid!, + scrollController: scrollController, + ); + }, + anchors: [0.6, 1], + isSafeArea: true, ); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 760976ae..a89b4d28 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; @@ -215,37 +218,35 @@ class _VideoInfoState extends State with TickerProviderStateMixin { if (!videoIntroController.hasFav.value) { videoIntroController.actionFavVideo(type: 'default'); } else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } else if (type != 'longPress') { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } + void _showFavPanel() { + showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 0.6, + initHeight: 0.6, + maxHeight: 1, + context: context, + builder: (BuildContext context, ScrollController scrollController, + double offset) { + return FavPanel( + ctr: videoIntroController, + scrollController: scrollController, + ); + }, + anchors: [0.6, 1], + isSafeArea: true, + ); + } + // 视频介绍 showIntroDetail() { feedBack(); diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 5ef78967..a4e98df7 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -6,8 +6,9 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; class FavPanel extends StatefulWidget { - const FavPanel({super.key, this.ctr}); + const FavPanel({super.key, this.ctr, this.scrollController}); final dynamic ctr; + final ScrollController? scrollController; @override State createState() => _FavPanelState(); @@ -15,31 +16,39 @@ class FavPanel extends StatefulWidget { class _FavPanelState extends State { final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = widget.ctr!.queryVideoInFolder(); } @override Widget build(BuildContext context) { return Container( - height: sheetHeight, - color: Theme.of(context).colorScheme.surface, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), child: Column( children: [ AppBar( centerTitle: false, elevation: 0, - leading: IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.close_outlined)), - title: - Text('添加到收藏夹', style: Theme.of(context).textTheme.titleMedium), + automaticallyImplyLeading: false, + leadingWidth: 0, + title: Text( + '选择收藏夹', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), ), Expanded( child: Material( @@ -51,22 +60,22 @@ class _FavPanelState extends State { if (data['status']) { return Obx( () => ListView.builder( + controller: widget.scrollController, itemCount: widget.ctr!.favFolderData.value.list!.length, itemBuilder: (context, index) { + final item = + widget.ctr!.favFolderData.value.list![index]; return ListTile( - onTap: () => widget.ctr!.onChoose( - widget.ctr!.favFolderData.value.list![index] - .favState != - 1, - index), + onTap: () => widget.ctr! + .onChoose(item.favState != 1, index), dense: true, leading: const Icon(Icons.folder_outlined), minLeadingWidth: 0, title: Text(widget.ctr!.favFolderData.value .list![index].title!), subtitle: Text( - '${widget.ctr!.favFolderData.value.list![index].mediaCount}个内容', + '${item.mediaCount}个内容 ', ), trailing: Transform.scale( scale: 0.9, @@ -132,7 +141,7 @@ class _FavPanelState extends State { backgroundColor: Theme.of(context).colorScheme.primary, // 设置按钮背景色 ), - child: const Text('完成'), + child: const Text('确认选择'), ), ], ), diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index dcdaf9c5..4bb0980c 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -10,7 +10,8 @@ import 'package:pilipala/utils/storage.dart'; class GroupPanel extends StatefulWidget { final int? mid; - const GroupPanel({super.key, this.mid}); + final ScrollController scrollController; + const GroupPanel({super.key, this.mid, required this.scrollController}); @override State createState() => _GroupPanelState(); @@ -18,7 +19,6 @@ class GroupPanel extends StatefulWidget { class _GroupPanelState extends State { final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; late List tagsList; bool showDefault = true; @@ -26,7 +26,6 @@ class _GroupPanelState extends State { @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = MemberHttp.followUpTags(); } @@ -56,8 +55,14 @@ class _GroupPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: sheetHeight, - color: Theme.of(context).colorScheme.surface, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), child: Column( children: [ AppBar( @@ -79,6 +84,7 @@ class _GroupPanelState extends State { if (data['status']) { tagsList = data['data']; return ListView.builder( + controller: widget.scrollController, itemCount: data['data'].length, itemBuilder: (context, index) { return ListTile( diff --git a/pubspec.lock b/pubspec.lock index a46127f9..a42df7bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + bottom_inset_observer: + dependency: transitive + description: + name: bottom_inset_observer + sha256: cbfb01e0e07cc4922052701786d5e607765a6f54e1844f41061abf8744519a7d + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" + bottom_sheet: + dependency: "direct main" + description: + name: bottom_sheet + sha256: efd28f52357d23e1c01eaeb45466b407f1e29318305bd6d10baf814fda18bd7e + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.4" build: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b5d45714..fb4cc2c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,7 @@ dependencies: lottie: ^3.1.2 # 二维码 qr_flutter: ^4.1.0 + bottom_sheet: ^4.0.4 dev_dependencies: flutter_test: From f598b6adadf00c0d6f2f7a3d0e088f9faabd952d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:14:37 +0800 Subject: [PATCH 223/438] opt: liveRoom getBuvid --- lib/http/init.dart | 27 +++++++++++++++++++++++++-- lib/pages/live_room/controller.dart | 4 +++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/http/init.dart b/lib/http/init.dart index faa57dd5..6a90a87d 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -29,7 +29,7 @@ class Request { late String systemProxyPort; static final RegExp spmPrefixExp = RegExp(r''); - static late String buvid; + static String? buvid; /// 设置cookie static setCookie() async { @@ -72,7 +72,6 @@ class Request { .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); - buvid = cookie.firstWhere((e) => e.name == 'buvid3').value; dio.options.headers['cookie'] = cookieString; } @@ -87,6 +86,30 @@ class Request { return token; } + static Future getBuvid() async { + if (buvid != null) { + return buvid!; + } + + final List cookies = await cookieManager.cookieJar + .loadForRequest(Uri.parse(HttpString.baseUrl)); + buvid = cookies.firstWhere((cookie) => cookie.name == 'buvid3').value; + if (buvid == null) { + try { + var result = await Request().get( + "${HttpString.apiBaseUrl}/x/frontend/finger/spi", + ); + buvid = result["data"]["b_3"].toString(); + } catch (e) { + // 处理请求错误 + buvid = ''; + print("Error fetching buvid: $e"); + } + } + + return buvid!; + } + static setOptionsHeaders(userInfo, bool status) { if (status) { dio.options.headers['x-bili-mid'] = userInfo.mid.toString(); diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 99025dce..5d4e2b67 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -47,6 +47,7 @@ class LiveRoomController extends GetxController { RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; // 直播间弹幕开关 默认打开 RxBool danmakuSwitch = true.obs; + late String buvid; @override void onInit() { @@ -63,6 +64,7 @@ class LiveRoomController extends GetxController { if (liveItem != null && liveItem.cover != null && liveItem.cover != '') { cover = liveItem.cover; } + Request.getBuvid().then((value) => buvid = value); } // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); @@ -249,7 +251,7 @@ class LiveRoomController extends GetxController { "uid": userId, "roomid": roomId, "protover": 3, - "buvid": Request.buvid, + "buvid": buvid, "platform": "web", "type": 2, "key": token, From 2e329553d914d4203e776d27c48194fc32fb2fdd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:23:43 +0800 Subject: [PATCH 224/438] =?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 225/438] =?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 226/438] mod: GlobalDataCache init --- lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index 78747279..fe93da22 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -64,6 +64,7 @@ void main() async { } PiliSchame.init(); + await GlobalDataCache().initialize(); } class MyApp extends StatelessWidget { @@ -268,7 +269,6 @@ class BuildMainApp extends StatelessWidget { onReady: () async { RecommendFilter(); Data.init(); - await GlobalDataCache().initialize(); setupServiceLocator(); }, ); From 67aa95c027f1373322402e905dbbeb342f2ae87d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 27 Aug 2024 23:57:19 +0800 Subject: [PATCH 227/438] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=BF=BD=E7=95=A5error=20response=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/interceptor.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 10b5aee5..259a3bf9 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -46,7 +46,8 @@ class ApiInterceptor extends Interceptor { // 处理网络请求错误 // handler.next(err); String url = err.requestOptions.uri.toString(); - if (!url.contains('heartbeat')) { + final excludedPatterns = RegExp(r'heartbeat|seg\.so|online/total'); + if (!excludedPatterns.hasMatch(url)) { SmartDialog.showToast( await dioError(err), displayType: SmartToastType.onlyRefresh, From 5c7f477ad8d467f93a056794165ef30e15e4584a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 31 Aug 2024 14:26:03 +0800 Subject: [PATCH 228/438] =?UTF-8?q?mod=EF=BC=9A=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=B1=95=E7=A4=BA(=E5=85=AC=E5=BC=80/?= =?UTF-8?q?=E7=A7=81=E5=AF=86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/widgets/item.dart | 11 ++++++++++- .../detail/introduction/widgets/fav_panel.dart | 17 ++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 9d453fb5..25b92a5c 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -74,7 +74,7 @@ class VideoContent extends StatelessWidget { Widget build(BuildContext context) { return Expanded( child: Padding( - padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + padding: const EdgeInsets.fromLTRB(10, 2, 6, 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -94,6 +94,15 @@ class VideoContent extends StatelessWidget { color: Theme.of(context).colorScheme.outline, ), ), + const Spacer(), + Text( + [23, 1].contains(favFolderItem.attr) ? '私密' : '公开', + textAlign: TextAlign.start, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), ], ), ), diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index c8c71e40..2cf7d236 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/http_error.dart'; @@ -60,19 +61,21 @@ class _FavPanelState extends State { onTap: () => widget.ctr!.onChoose(item.favState != 1, index), dense: true, - leading: const Icon(Icons.folder_outlined), + leading: Icon([23, 1].contains(item.attr) + ? Icons.lock_outline + : Icons.folder_outlined), minLeadingWidth: 0, - title: Text(widget - .ctr!.favFolderData.value.list![index].title!), + title: Text(item.title!), subtitle: Text( - '${item.mediaCount}个内容 ', + '${item.mediaCount}个内容 - ${[ + 23, + 1 + ].contains(item.attr) ? '私密' : '公开'}', ), trailing: Transform.scale( scale: 0.9, child: Checkbox( - value: widget.ctr!.favFolderData.value - .list![index].favState == - 1, + value: item.favState == 1, onChanged: (bool? checkValue) => widget.ctr!.onChoose(checkValue!, index), ), From e4a820268a5068418d3fa39ecaa706ac07c1b7d6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 4 Sep 2024 09:42:08 +0800 Subject: [PATCH 229/438] =?UTF-8?q?fix:=20iOS=E9=9D=99=E9=9F=B3=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E6=97=A0=E5=A3=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/AppDelegate.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4a..dfa078c3 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,5 +1,6 @@ import UIKit import Flutter +import AVFoundation @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -8,6 +9,14 @@ import Flutter didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) + + // 设置音频会话类别,确保在静音模式下播放音频 + do { + try AVAudioSession.sharedInstance().setCategory(.playback, options: [.duckOthers]) + } catch { + print("Failed to set audio session category: \(error)") + } + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } -} +} \ No newline at end of file From c3595a0dcd91430a498ef660d162b001df684382 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 15:53:56 +0800 Subject: [PATCH 230/438] =?UTF-8?q?fix:=20=E6=90=9C=E7=B4=A2=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E7=AD=9B=E9=80=89=E9=9D=A2=E6=9D=BF=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search_panel/widgets/video_panel.dart | 163 +++++++++--------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index f43e2eec..7da6aaaa 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -261,90 +261,97 @@ class VideoPanelController extends GetxController { onShowFilterSheet(searchPanelCtr) { showModalBottomSheet( context: Get.context!, + isScrollControlled: true, builder: (context) { return StatefulBuilder( builder: (context, StateSetter setState) { - return Container( - color: Theme.of(Get.context!).colorScheme.surface, - padding: const EdgeInsets.only(top: 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Padding( + padding: EdgeInsets.only( + top: 12, bottom: MediaQuery.of(context).padding.bottom + 20), + child: Wrap( children: [ - const ListTile( - title: Text('内容时长'), - ), - Padding( - padding: const EdgeInsets.only( - left: 14, - right: 14, - bottom: 14, - ), - child: Wrap( - spacing: 10, - runSpacing: 10, - direction: Axis.horizontal, - textDirection: TextDirection.ltr, - children: [ - for (var i in timeFiltersList) - Obx( - () => SearchText( - searchText: i['label'], - searchTextIdx: i['value'], - isSelect: - currentTimeFilterval.value == i['value'], - onSelect: (value) async { - currentTimeFilterval.value = i['value']; - setState(() {}); - SmartDialog.showToast("「${i['label']}」的筛选结果"); - SearchPanelController ctr = - Get.find( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('内容时长'), + ), + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 14, + ), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in timeFiltersList) + Obx( + () => SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentTimeFilterval.value == i['value'], + onSelect: (value) async { + currentTimeFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast( + "「${i['label']}」的筛选结果"); + SearchPanelController ctr = Get.find< + SearchPanelController>( tag: 'video${searchPanelCtr.keyword!}'); - ctr.duration.value = i['value']; - Get.back(); - SmartDialog.showLoading(msg: '获取中'); - await ctr.onRefresh(); - SmartDialog.dismiss(); - }, - onLongSelect: (value) => {}, - ), - ) - ], - ), - ), - const ListTile( - title: Text('内容分区'), - ), - Padding( - padding: const EdgeInsets.only(left: 14, right: 14), - child: Wrap( - spacing: 10, - runSpacing: 10, - direction: Axis.horizontal, - textDirection: TextDirection.ltr, - children: [ - for (var i in partFiltersList) - SearchText( - searchText: i['label'], - searchTextIdx: i['value'], - isSelect: currentPartFilterval.value == i['value'], - onSelect: (value) async { - currentPartFilterval.value = i['value']; - setState(() {}); - SmartDialog.showToast("「${i['label']}」的筛选结果"); - SearchPanelController ctr = - Get.find( + ctr.duration.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ), + ) + ], + ), + ), + const ListTile( + title: Text('内容分区'), + ), + Padding( + padding: const EdgeInsets.only(left: 14, right: 14), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in partFiltersList) + SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentPartFilterval.value == i['value'], + onSelect: (value) async { + currentPartFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = Get.find< + SearchPanelController>( tag: 'video${searchPanelCtr.keyword!}'); - ctr.tids.value = i['value']; - Get.back(); - SmartDialog.showLoading(msg: '获取中'); - await ctr.onRefresh(); - SmartDialog.dismiss(); - }, - onLongSelect: (value) => {}, - ) - ], - ), - ) + ctr.tids.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ) + ], + ), + ) + ], + ), ], ), ); From df1866286314de2d890557415dd09a86ceb9dcd6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 17:46:08 +0800 Subject: [PATCH 231/438] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E5=90=88?= =?UTF-8?q?=E9=9B=86=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_seasons/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/member_seasons/controller.dart b/lib/pages/member_seasons/controller.dart index 58a9035f..4e7c9762 100644 --- a/lib/pages/member_seasons/controller.dart +++ b/lib/pages/member_seasons/controller.dart @@ -21,7 +21,7 @@ class MemberSeasonsController extends GetxController { mid = int.parse(Get.parameters['mid']!); category = Get.parameters['category']!; if (category == '0') { - seasonId = int.parse(Get.parameters['seriesId']!); + seasonId = int.parse(Get.parameters['seasonId']!); } if (category == '1') { seriesId = int.parse(Get.parameters['seriesId']!); From 1d87e66433fdfc04aab9bd3f9ba1a0eab55da45f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 17:46:23 +0800 Subject: [PATCH 232/438] =?UTF-8?q?fix:=20=E7=BF=BB=E9=A1=B5=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_archive/controller.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 667d16c5..20cf38d3 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -27,10 +27,13 @@ class MemberArchiveController extends GetxController { // 获取用户投稿 Future getMemberArchive(type) async { + if (isLoading.value) { + return; + } + isLoading.value = true; if (type == 'init') { pn = 1; archivesList.clear(); - isLoading.value = true; } var res = await MemberHttp.memberArchive( mid: mid, From 012b4152337faa9db1d7d6d1ecc48b4fc6dda010 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:05:22 +0800 Subject: [PATCH 233/438] =?UTF-8?q?fix:=20=E7=82=B9=E8=B5=9E=E8=AE=A1?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/message/like/view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index 80b20117..8b061505 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -203,9 +203,9 @@ class LikeItem extends StatelessWidget { Text.rich(TextSpan(children: [ TextSpan(text: nickNameList.join('、')), const TextSpan(text: ' '), - if (item.users!.length > 1) + if (item.counts! > 1) TextSpan( - text: '等总计${item.users!.length}人', + text: '等总计${item.counts}人', style: TextStyle(color: outline), ), TextSpan( From 5651466881cbffe0879e69d5da66a28f70bd454f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:12:38 +0800 Subject: [PATCH 234/438] =?UTF-8?q?fix:=20=E6=9A=97=E9=BB=91=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E4=B8=AA=E4=BA=BA=E8=AE=A4=E8=AF=81=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 7d2e4bed..be0ddedc 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -406,7 +406,7 @@ class _MemberPageState extends State ? '个人认证:' : '企业认证:', style: TextStyle( - color: Theme.of(context).primaryColor, + color: Theme.of(context).colorScheme.primary, ), children: [ TextSpan( From 10f464aab15885fba15bd9e9f1782a08bebf8344 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:47:16 +0800 Subject: [PATCH 235/438] =?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 236/438] =?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 237/438] =?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 238/438] =?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 239/438] =?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 240/438] 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 d2640f230c34ce58ceaebcfc3e1731e78757ce55 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 15 Sep 2024 02:30:35 +0800 Subject: [PATCH 241/438] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/reply.dart | 21 ++++++ .../detail/reply/widgets/reply_item.dart | 64 ++++++++++++++++--- 3 files changed, 79 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 93226946..ce8279d6 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 replyDel = '/x/v2/reply/del'; } diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 880f9072..8702abb7 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -115,4 +115,25 @@ class ReplyHttp { }; } } + + static Future replyDel({ + required int type, //replyType + required int oid, + required int rpid, + }) async { + var res = await Request().post( + Api.replyDel, + queryParameters: { + 'type': type, //type.index + 'oid': oid, + 'rpid': rpid, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true, 'msg': '删除成功'}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 26dc2e5a..c0d41069 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -10,6 +10,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/reply.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/main/index.dart'; @@ -19,6 +20,7 @@ import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; @@ -49,6 +51,8 @@ class ReplyItem extends StatelessWidget { @override Widget build(BuildContext context) { + final bool isOwner = int.parse(replyItem!.member!.mid!) == + (GlobalDataCache().userInfo?.mid ?? -1); return Material( child: InkWell( // 点击整个评论区 评论详情/回复 @@ -74,6 +78,7 @@ class ReplyItem extends StatelessWidget { return MorePanel( item: replyItem, mainFloor: true, + isOwner: isOwner, ); }, ); @@ -1033,10 +1038,12 @@ InlineSpan buildContent( class MorePanel extends StatelessWidget { final dynamic item; final bool mainFloor; + final bool isOwner; const MorePanel({ super.key, required this.item, this.mainFloor = false, + this.isOwner = false, }); Future menuActionHandler(String type) async { @@ -1072,9 +1079,45 @@ class MorePanel extends StatelessWidget { // case 'report': // SmartDialog.showToast('举报'); // break; - // case 'delete': - // SmartDialog.showToast('删除'); - // break; + case 'delete': + // 删除评论提示 + bool? isConfirm = await showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('删除评论'), + content: const Text('删除评论后,评论下所有回复将被删除,确定删除吗?'), + actions: [ + TextButton( + onPressed: () => Get.back(result: false), + child: Text('取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline)), + ), + TextButton( + onPressed: () { + Get.back(result: true); + SmartDialog.showToast('删除成功'); + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + if (isConfirm == null || !isConfirm) { + return; + } + SmartDialog.showLoading(msg: '删除中...'); + var result = await ReplyHttp.replyDel( + type: item.type!, + oid: item.oid!, + rpid: item.rpid!, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(result["msg"]); + + break; default: } } @@ -1083,6 +1126,7 @@ class MorePanel extends StatelessWidget { Widget build(BuildContext context) { ColorScheme colorScheme = Theme.of(context).colorScheme; TextTheme textTheme = Theme.of(context).textTheme; + Color errorColor = colorScheme.error; return Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), child: Column( @@ -1135,12 +1179,14 @@ class MorePanel extends StatelessWidget { // leading: Icon(Icons.report_outlined, color: errorColor), // title: Text('举报', style: TextStyle(color: errorColor)), // ), - // ListTile( - // onTap: () async => await menuActionHandler('del'), - // minLeadingWidth: 0, - // leading: Icon(Icons.delete_outline, color: errorColor), - // title: Text('删除', style: TextStyle(color: errorColor)), - // ), + if (isOwner) + ListTile( + onTap: () async => await menuActionHandler('delete'), + minLeadingWidth: 0, + leading: Icon(Icons.delete_outline, color: errorColor), + title: Text('删除评论', + style: textTheme.titleSmall!.copyWith(color: errorColor)), + ), ], ), ); From 8f6fe042cd23f167950c99b7434272684ec3082f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 18 Sep 2024 00:16:02 +0800 Subject: [PATCH 242/438] =?UTF-8?q?feat:=20=E6=88=91=E7=9A=84&=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E9=A1=B5=E9=9D=A2=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/nav_bar_config.dart | 13 +- lib/pages/home/view.dart | 21 +- lib/pages/home/widgets/app_bar.dart | 23 +- lib/pages/main/view.dart | 9 +- lib/pages/media/controller.dart | 65 -- lib/pages/media/index.dart | 4 - lib/pages/media/view.dart | 296 ------- lib/pages/mine/controller.dart | 66 +- lib/pages/mine/view.dart | 740 +++++++++++------- .../setting/pages/navigation_bar_set.dart | 3 +- lib/pages/webview/controller.dart | 1 - lib/router/app_pages.dart | 6 +- lib/utils/login.dart | 6 - 13 files changed, 524 insertions(+), 729 deletions(-) delete mode 100644 lib/pages/media/controller.dart delete mode 100644 lib/pages/media/index.dart delete mode 100644 lib/pages/media/view.dart diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart index 64cebafb..1c3a3438 100644 --- a/lib/models/common/nav_bar_config.dart +++ b/lib/models/common/nav_bar_config.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; - +import 'package:pilipala/pages/mine/index.dart'; import '../../pages/dynamics/index.dart'; import '../../pages/home/index.dart'; -import '../../pages/media/index.dart'; import '../../pages/rank/index.dart'; List defaultNavigationBars = [ @@ -51,15 +50,15 @@ List defaultNavigationBars = [ { 'id': 3, 'icon': const Icon( - Icons.video_collection_outlined, + Icons.person_outline, size: 20, ), 'selectIcon': const Icon( - Icons.video_collection, + Icons.person, size: 21, ), - 'label': "媒体库", + 'label': "我的", 'count': 0, - 'page': const MediaPage(), - } + 'page': const MinePage(), + }, ]; diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 187ed6c3..11d36c9b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -5,7 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/pages/mine/index.dart'; +import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import './controller.dart'; @@ -19,6 +19,8 @@ class HomePage extends StatefulWidget { class _HomePageState extends State with AutomaticKeepAliveClientMixin, TickerProviderStateMixin { final HomeController _homeController = Get.put(HomeController()); + final MainController mainController = Get.put(MainController()); + List videoList = []; late Stream stream; @@ -33,15 +35,14 @@ class _HomePageState extends State showUserBottomSheet() { feedBack(); - showModalBottomSheet( - context: context, - builder: (_) => const SizedBox( - height: 450, - child: MinePage(), - ), - clipBehavior: Clip.hardEdge, - isScrollControlled: true, - ); + final MainController mainController = Get.put(MainController()); + if (mainController.navigationBars + .where((item) => item['label'] == "我的") + .isNotEmpty) { + mainController.pageController.jumpToPage(2); + } else { + Get.toNamed('/mine'); + } } @override diff --git a/lib/pages/home/widgets/app_bar.dart b/lib/pages/home/widgets/app_bar.dart index 36920aef..35cb9d4e 100644 --- a/lib/pages/home/widgets/app_bar.dart +++ b/lib/pages/home/widgets/app_bar.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/pages/mine/view.dart'; import 'package:pilipala/utils/storage.dart'; Box userInfoCache = GStrorage.userInfo; @@ -54,19 +53,9 @@ class HomeAppBar extends StatelessWidget { // icon: const Icon(CupertinoIcons.bell, size: 22), // ), const SizedBox(width: 6), - - /// TODO if (userInfo != null) ...[ GestureDetector( - onTap: () => showModalBottomSheet( - context: context, - builder: (_) => const SizedBox( - height: 450, - child: MinePage(), - ), - clipBehavior: Clip.hardEdge, - isScrollControlled: true, - ), + onTap: () => Get.toNamed('/mine'), child: NetworkImgLayer( type: 'avatar', width: 32, @@ -77,15 +66,7 @@ class HomeAppBar extends StatelessWidget { const SizedBox(width: 10), ] else ...[ IconButton( - onPressed: () => showModalBottomSheet( - context: context, - builder: (_) => const SizedBox( - height: 450, - child: MinePage(), - ), - clipBehavior: Clip.hardEdge, - isScrollControlled: true, - ), + onPressed: () => Get.toNamed('/mine'), icon: const Icon(CupertinoIcons.person, size: 22), ), ], diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 3da667e8..825e65bc 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -6,7 +6,7 @@ import 'package:hive/hive.dart'; import 'package:pilipala/models/common/dynamic_badge_mode.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/media/index.dart'; +import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/feed_back.dart'; @@ -25,7 +25,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { final HomeController _homeController = Get.put(HomeController()); final RankController _rankController = Get.put(RankController()); final DynamicsController _dynamicController = Get.put(DynamicsController()); - final MediaController _mediaController = Get.put(MediaController()); + final MineController _mineController = Get.put(MineController()); int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; @@ -90,8 +90,9 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _dynamicController.flag = false; } - if (currentPage is MediaPage) { - _mediaController.queryFavFolder(); + if (currentPage is MinePage) { + _mineController.queryFavFolder(); + _mineController.queryUserInfo(); } } diff --git a/lib/pages/media/controller.dart b/lib/pages/media/controller.dart deleted file mode 100644 index 757d5ac9..00000000 --- a/lib/pages/media/controller.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:hive/hive.dart'; -import 'package:pilipala/http/user.dart'; -import 'package:pilipala/models/user/fav_folder.dart'; -import 'package:pilipala/utils/storage.dart'; - -class MediaController extends GetxController { - Rx favFolderData = FavFolderData().obs; - Box userInfoCache = GStrorage.userInfo; - RxBool userLogin = false.obs; - List list = [ - { - 'icon': Icons.file_download_outlined, - 'title': '离线缓存', - 'onTap': () { - SmartDialog.showToast('功能开发中'); - }, - }, - { - 'icon': Icons.history, - 'title': '观看记录', - 'onTap': () => Get.toNamed('/history'), - }, - { - 'icon': Icons.star_border, - 'title': '我的收藏', - 'onTap': () => Get.toNamed('/fav'), - }, - { - 'icon': Icons.subscriptions_outlined, - 'title': '我的订阅', - 'onTap': () => Get.toNamed('/subscription'), - }, - { - 'icon': Icons.watch_later_outlined, - 'title': '稍后再看', - 'onTap': () => Get.toNamed('/later'), - }, - ]; - var userInfo; - int? mid; - final ScrollController scrollController = ScrollController(); - - @override - void onInit() { - super.onInit(); - userInfo = userInfoCache.get('userInfoCache'); - userLogin.value = userInfo != null; - } - - Future queryFavFolder() async { - if (!userLogin.value) { - return {'status': false, 'data': [], 'msg': '未登录'}; - } - var res = await await UserHttp.userfavFolder( - pn: 1, - ps: 5, - mid: mid ?? GStrorage.userInfo.get('userInfoCache').mid, - ); - favFolderData.value = res['data']; - return res; - } -} diff --git a/lib/pages/media/index.dart b/lib/pages/media/index.dart deleted file mode 100644 index 8fae4891..00000000 --- a/lib/pages/media/index.dart +++ /dev/null @@ -1,4 +0,0 @@ -library media; - -export './controller.dart'; -export './view.dart'; diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart deleted file mode 100644 index 965628c4..00000000 --- a/lib/pages/media/view.dart +++ /dev/null @@ -1,296 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:get/get.dart'; -import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/models/user/fav_folder.dart'; -import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/utils/utils.dart'; - -class MediaPage extends StatefulWidget { - const MediaPage({super.key}); - - @override - State createState() => _MediaPageState(); -} - -class _MediaPageState extends State - with AutomaticKeepAliveClientMixin { - late MediaController mediaController; - late Future _futureBuilderFuture; - - @override - bool get wantKeepAlive => true; - - @override - void initState() { - super.initState(); - mediaController = Get.put(MediaController()); - _futureBuilderFuture = mediaController.queryFavFolder(); - mediaController.userLogin.listen((status) { - setState(() { - _futureBuilderFuture = mediaController.queryFavFolder(); - }); - }); - } - - @override - void dispose() { - mediaController.scrollController.removeListener(() {}); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - super.build(context); - Color primary = Theme.of(context).colorScheme.primary; - return Scaffold( - appBar: AppBar(toolbarHeight: 30), - body: SingleChildScrollView( - controller: mediaController.scrollController, - child: Column( - children: [ - ListTile( - leading: null, - title: Padding( - padding: const EdgeInsets.only(left: 20), - child: Text( - '媒体库', - style: TextStyle( - fontSize: Theme.of(context).textTheme.titleLarge!.fontSize, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - for (var i in mediaController.list) ...[ - ListTile( - onTap: () => i['onTap'](), - dense: true, - leading: Padding( - padding: const EdgeInsets.only(left: 15), - child: Icon( - i['icon'], - color: primary, - ), - ), - contentPadding: - const EdgeInsets.only(left: 15, top: 2, bottom: 2), - minLeadingWidth: 0, - title: Text( - i['title'], - style: const TextStyle(fontSize: 15), - ), - ), - ], - Obx(() => mediaController.userLogin.value - ? favFolder(mediaController, context) - : const SizedBox()), - SizedBox( - height: MediaQuery.of(context).padding.bottom + - kBottomNavigationBarHeight, - ) - ], - ), - ), - ); - } - - Widget favFolder(mediaController, context) { - return Column( - children: [ - Divider( - height: 35, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), - ListTile( - onTap: () => Get.toNamed('/fav'), - leading: null, - dense: true, - title: Padding( - padding: const EdgeInsets.only(left: 10), - child: Obx( - () => Text.rich( - TextSpan( - children: [ - TextSpan( - text: '收藏夹 ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.titleMedium!.fontSize, - fontWeight: FontWeight.bold), - ), - if (mediaController.favFolderData.value.count != null) - TextSpan( - text: mediaController.favFolderData.value.count - .toString(), - style: TextStyle( - fontSize: - Theme.of(context).textTheme.titleSmall!.fontSize, - color: Theme.of(context).colorScheme.primary, - ), - ), - ], - ), - ), - ), - ), - trailing: IconButton( - onPressed: () { - setState(() { - _futureBuilderFuture = mediaController.queryFavFolder(); - }); - }, - icon: const Icon( - Icons.refresh, - size: 20, - ), - ), - ), - // const SizedBox(height: 10), - SizedBox( - width: double.infinity, - height: MediaQuery.textScalerOf(context).scale(200), - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - Map data = snapshot.data as Map; - if (data['status']) { - List favFolderList = - mediaController.favFolderData.value.list!; - int favFolderCount = - mediaController.favFolderData.value.count!; - bool flag = favFolderCount > favFolderList.length; - return Obx(() => ListView.builder( - itemCount: - mediaController.favFolderData.value.list!.length + - (flag ? 1 : 0), - itemBuilder: (context, index) { - if (flag && index == favFolderList.length) { - return Padding( - padding: const EdgeInsets.only( - right: 14, bottom: 35), - child: Center( - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), - backgroundColor: - MaterialStateProperty.resolveWith( - (states) { - return Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.5); - }), - ), - onPressed: () => Get.toNamed('/fav'), - icon: Icon( - Icons.arrow_forward_ios, - size: 18, - color: Theme.of(context) - .colorScheme - .primary, - ), - ), - )); - } else { - return FavFolderItem( - item: mediaController - .favFolderData.value.list![index], - index: index); - } - }, - scrollDirection: Axis.horizontal, - )); - } else { - return SizedBox( - height: 160, - child: Center(child: Text(data['msg'])), - ); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }), - ), - ], - ); - } -} - -class FavFolderItem extends StatelessWidget { - const FavFolderItem({super.key, this.item, this.index}); - final FavFolderItemData? item; - final int? index; - @override - Widget build(BuildContext context) { - String heroTag = Utils.makeHeroTag(item!.fid); - - return Container( - margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), - child: GestureDetector( - onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { - 'mediaId': item!.id.toString(), - 'heroTag': heroTag, - 'isOwner': '1', - }), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 12), - Container( - width: 180, - height: 110, - margin: const EdgeInsets.only(bottom: 8), - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), - color: Theme.of(context).colorScheme.onInverseSurface, - boxShadow: [ - BoxShadow( - color: Theme.of(context).colorScheme.onInverseSurface, - offset: const Offset(4, -12), // 阴影与容器的距离 - blurRadius: 0.0, // 高斯的标准偏差与盒子的形状卷积。 - spreadRadius: 0.0, // 在应用模糊之前,框应该膨胀的量。 - ), - ], - ), - child: LayoutBuilder( - builder: (context, BoxConstraints box) { - return Hero( - tag: heroTag, - child: NetworkImgLayer( - src: item!.cover, - width: box.maxWidth, - height: box.maxHeight, - ), - ); - }, - ), - ), - Text( - ' ${item!.title}', - overflow: TextOverflow.fade, - maxLines: 1, - ), - Text( - ' 共${item!.mediaCount}条视频', - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith(color: Theme.of(context).colorScheme.outline), - ) - ], - ), - ), - ); - } -} diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index e682a7e5..24401711 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -4,22 +4,45 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; +import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { - // 用户信息 头像、昵称、lv - Rx userInfo = UserInfoData().obs; + RxBool userLogin = false.obs; // 用户状态 动态、关注、粉丝 Rx userStat = UserStat().obs; - RxBool userLogin = false.obs; - Box userInfoCache = GStrorage.userInfo; - Box setting = GStrorage.setting; + // 用户信息 头像、昵称、lv + Rx userInfo = UserInfoData().obs; Rx themeType = ThemeType.system.obs; - + Rx favFolderData = FavFolderData().obs; + Box setting = GStrorage.setting; + Box userInfoCache = GStrorage.userInfo; + List menuList = [ + { + 'icon': Icons.history, + 'title': '观看记录', + 'onTap': () => Get.toNamed('/history'), + }, + { + 'icon': Icons.star_border, + 'title': '我的收藏', + 'onTap': () => Get.toNamed('/fav'), + }, + { + 'icon': Icons.subscriptions_outlined, + 'title': '我的订阅', + 'onTap': () => Get.toNamed('/subscription'), + }, + { + 'icon': Icons.watch_later_outlined, + 'title': '稍后再看', + 'onTap': () => Get.toNamed('/later'), + }, + ]; @override - onInit() { + void onInit() { super.onInit(); if (userInfoCache.get('userInfoCache') != null) { @@ -109,7 +132,10 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/follow?mid=${userInfo.value.mid}', preventDuplicates: false); + Get.toNamed( + '/follow?mid=${userInfo.value.mid}', + preventDuplicates: false, + ); } pushFans() { @@ -117,7 +143,10 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/fan?mid=${userInfo.value.mid}', preventDuplicates: false); + Get.toNamed( + '/fan?mid=${userInfo.value.mid}', + preventDuplicates: false, + ); } pushDynamic() { @@ -125,7 +154,22 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/memberDynamics?mid=${userInfo.value.mid}', - preventDuplicates: false); + Get.toNamed( + '/memberDynamics?mid=${userInfo.value.mid}', + preventDuplicates: false, + ); + } + + Future queryFavFolder() async { + if (!userLogin.value) { + return {'status': false, 'data': [], 'msg': '未登录'}; + } + var res = await await UserHttp.userfavFolder( + pn: 1, + ps: 5, + mid: userInfo.value.mid!, + ); + favFolderData.value = res['data']; + return res; } } diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 091b2149..757fbbc5 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -1,12 +1,12 @@ -// ignore_for_file: no_leading_underscores_for_local_identifiers - -import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/models/common/theme_type.dart'; +import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/info.dart'; +import 'package:pilipala/models/user/stat.dart'; +import 'package:pilipala/utils/utils.dart'; import 'controller.dart'; class MinePage extends StatefulWidget { @@ -16,19 +16,23 @@ class MinePage extends StatefulWidget { State createState() => _MinePageState(); } -class _MinePageState extends State { - final MineController mineController = Get.put(MineController()); +class _MinePageState extends State + with AutomaticKeepAliveClientMixin { + final MineController ctr = Get.put(MineController()); late Future _futureBuilderFuture; + @override + bool get wantKeepAlive => true; + @override void initState() { super.initState(); - _futureBuilderFuture = mineController.queryUserInfo(); - - mineController.userLogin.listen((status) { + _futureBuilderFuture = ctr.queryUserInfo(); + ctr.queryFavFolder(); + ctr.userLogin.listen((status) { if (mounted) { setState(() { - _futureBuilderFuture = mineController.queryUserInfo(); + _futureBuilderFuture = ctr.queryUserInfo(); }); } }); @@ -36,71 +40,181 @@ class _MinePageState extends State { @override Widget build(BuildContext context) { + super.build(context); return Scaffold( appBar: AppBar( - automaticallyImplyLeading: false, - scrolledUnderElevation: 0, - elevation: 0, - toolbarHeight: kTextTabBarHeight + 20, - backgroundColor: Colors.transparent, - centerTitle: false, - title: const Text( - 'PLPL', - style: TextStyle( - height: 2.8, - fontSize: 17, - fontWeight: FontWeight.bold, - fontFamily: 'Jura-Bold', - ), - ), actions: [ IconButton( - onPressed: () => mineController.onChangeTheme(), - icon: Icon( - mineController.themeType.value == ThemeType.dark - ? CupertinoIcons.sun_max - : CupertinoIcons.moon, - size: 22, - ), + icon: const Icon(Icons.dark_mode_outlined), + onPressed: () => ctr.onChangeTheme(), ), IconButton( + icon: const Icon(Icons.settings_outlined), onPressed: () => Get.toNamed('/setting', preventDuplicates: false), - icon: const Icon( - CupertinoIcons.slider_horizontal_3, - ), ), - const SizedBox(width: 10), + const SizedBox(width: 22), ], ), - body: LayoutBuilder( - builder: (context, constraint) { - return SingleChildScrollView( - physics: const NeverScrollableScrollPhysics(), - child: SizedBox( - height: constraint.maxHeight, - child: Column( - children: [ - const SizedBox(height: 10), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - return Obx( - () => userInfoBuild(mineController, context)); - } else { - return userInfoBuild(mineController, context); - } - } else { - return userInfoBuild(mineController, context); - } - }, + body: SingleChildScrollView( + child: Column( + children: [ + Obx(() => _buildProfileSection(context, ctr.userInfo.value)), + const SizedBox(height: 10), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + return Obx( + () => _buildStatsSection( + context, + ctr.userStat.value, + ), + ); + } else { + return _buildStatsSection( + context, + ctr.userStat.value, + ); + } + } else { + return _buildStatsSection( + context, + ctr.userStat.value, + ); + } + }, + ), + _buildMenuSection(context), + Divider( + height: 25, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + Obx( + () => ctr.userLogin.value + ? _buildFavoritesSection(context) + : const SizedBox(), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + + kBottomNavigationBarHeight, + ) + ], + ), + ), + ); + } + + Widget _buildProfileSection(BuildContext context, UserInfoData userInfo) { + return InkWell( + onTap: () => ctr.onLogin(), + child: Padding( + padding: const EdgeInsets.fromLTRB(24, 10, 30, 10), + child: Row( + children: [ + userInfo.face != null + ? NetworkImgLayer( + src: userInfo.face, + width: 85, + height: 85, + type: 'avatar', + ) + : ClipOval( + child: SizedBox( + width: 85, + height: 85, + child: Image.asset('assets/images/noface.jpeg'), + ), ), + const SizedBox(width: 12), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Text( + userInfo.uname ?? '去登录', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 17, + fontWeight: FontWeight.bold, + color: userInfo.vipStatus == 1 + ? const Color.fromARGB(255, 251, 100, 163) + : null, + ), + ), + const SizedBox(width: 6), + Image.asset( + 'assets/images/lv/lv${userInfo.levelInfo != null ? userInfo.levelInfo!.currentLevel : '0'}.png', + height: 12, + ), + ], + ), + const SizedBox(height: 2), + if (userInfo.vipType != 0 && userInfo.vipStatus == 1) ...[ + Image.network( + userInfo.vipLabel!['img_label_uri_hans_static'], + height: 22, + ), + const SizedBox(height: 2), ], - ), + Text( + '硬币: ${userInfo.money ?? '-'}', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ), + ], + ), + const Spacer(), + Icon( + Icons.keyboard_arrow_right_rounded, + size: 28, + color: Theme.of(context).colorScheme.outline, + ), + ], + ), + ), + ); + } + + Widget _buildStatsSection(BuildContext context, UserStat userStat) { + return Padding( + padding: const EdgeInsets.only(left: 30, right: 30), + child: LayoutBuilder( + builder: (context, constraints) { + return SizedBox( + height: constraints.maxWidth / 3 * 0.6, + child: GridView.count( + primary: false, + padding: const EdgeInsets.all(0), + crossAxisCount: 3, + childAspectRatio: 1.67, + children: [ + _buildStatItem( + context, + (userStat.dynamicCount ?? '-').toString(), + '动态', + ctr.pushDynamic, + ), + _buildStatItem( + context, + (userStat.following ?? '-').toString(), + '关注', + ctr.pushFollow, + ), + _buildStatItem( + context, + (userStat.follower ?? '-').toString(), + '粉丝', + ctr.pushFans, + ), + ], ), ); }, @@ -108,258 +222,286 @@ class _MinePageState extends State { ); } - Widget userInfoBuild(_mineController, context) { - return Column( - children: [ - const SizedBox(height: 5), - GestureDetector( - onTap: () => _mineController.onLogin(), - child: ClipOval( - child: Container( - width: 85, - height: 85, - color: Theme.of(context).colorScheme.onInverseSurface, - child: Center( - child: _mineController.userInfo.value.face != null - ? NetworkImgLayer( - src: _mineController.userInfo.value.face, - width: 85, - height: 85) - : Image.asset('assets/images/noface.jpeg'), - ), - ), - ), - ), - const SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - _mineController.userInfo.value.uname ?? '点击头像登录', - style: Theme.of(context).textTheme.titleMedium, - ), - const SizedBox(width: 4), - Image.asset( - 'assets/images/lv/lv${_mineController.userInfo.value.levelInfo != null ? _mineController.userInfo.value.levelInfo!.currentLevel : '0'}.png', - height: 10, - ), - ], - ), - const SizedBox(height: 5), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text.rich(TextSpan(children: [ - TextSpan( - text: '硬币: ', - style: - TextStyle(color: Theme.of(context).colorScheme.outline)), - TextSpan( - text: (_mineController.userInfo.value.money ?? 'pilipala') - .toString(), - style: - TextStyle(color: Theme.of(context).colorScheme.primary)), - ])) - ], - ), - const SizedBox(height: 25), - if (_mineController.userInfo.value.levelInfo != null) ...[ - LayoutBuilder( - builder: (context, BoxConstraints box) { - LevelInfo levelInfo = _mineController.userInfo.value.levelInfo; - return SizedBox( - width: box.maxWidth, - height: 24, - child: Stack( - children: [ - Positioned( - top: 0, - right: 0, - bottom: 0, - child: Container( - color: Theme.of(context).colorScheme.primary, - height: 24, - constraints: - const BoxConstraints(minWidth: 100), // 设置最小宽度为100 - width: box.maxWidth * - (1 - (levelInfo.currentExp! / levelInfo.nextExp!)), - child: Center( - child: Text( - '${levelInfo.currentExp!}/${levelInfo.nextExp!}', - style: TextStyle( - color: Theme.of(context).colorScheme.onPrimary, - fontSize: 12, - ), - ), - ), - ), - ), - Positioned( - top: 23, - left: 0, - bottom: 0, - child: Container( - width: box.maxWidth * - (_mineController - .userInfo.value.levelInfo!.currentExp! / - _mineController - .userInfo.value.levelInfo!.nextExp!), - height: 1, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - ), - ), - ), - ], + Widget _buildStatItem( + BuildContext context, + String count, + String label, + Function onTap, + ) { + TextStyle style = TextStyle( + fontSize: Theme.of(context).textTheme.titleMedium!.fontSize, + fontWeight: FontWeight.bold); + return InkWell( + onTap: () => onTap(), + // onTap: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(count, style: style), + const SizedBox(height: 2), + Text( + label, + style: Theme.of(context).textTheme.labelMedium!.copyWith( + color: Theme.of(context).colorScheme.outline, ), - ); - }, ), ], - const SizedBox(height: 30), - Padding( - padding: const EdgeInsets.only(left: 12, right: 12), - child: LayoutBuilder( - builder: (context, constraints) { - TextStyle style = TextStyle( - fontSize: Theme.of(context).textTheme.titleMedium!.fontSize, - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold); - return SizedBox( - height: constraints.maxWidth / 3 * 0.6, - child: GridView.count( - primary: false, - padding: const EdgeInsets.all(0), - crossAxisCount: 3, - childAspectRatio: 1.67, - children: [ - InkWell( - onTap: () => _mineController.pushDynamic(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.dynamicCount ?? - '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.dynamicCount - .toString()), - style: style), + ), + ); + } + + Widget _buildMenuSection(BuildContext context) { + return Padding( + padding: const EdgeInsets.only(left: 12, right: 12), + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return SizedBox( + height: constraints.maxWidth / 4 * 0.85, + child: GridView.count( + primary: false, + crossAxisCount: 4, + padding: const EdgeInsets.all(0), + childAspectRatio: 1.2, + children: [ + ...ctr.menuList.map((element) { + return InkWell( + onTap: () { + if (!ctr.userLogin.value) { + SmartDialog.showToast('账号未登录'); + } else { + element['onTap'](); + } + }, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(10), + child: Icon( + element['icon'], + size: 21, + color: Theme.of(context).colorScheme.primary, ), - const SizedBox(height: 8), - Text( - '动态', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), + ), + const SizedBox(height: 4), + Text(element['title']) + ], ), - InkWell( - onTap: () => _mineController.pushFollow(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.following ?? - '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.following - .toString()), - style: style), - ), - const SizedBox(height: 8), - Text( - '关注', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), + ); + }).toList(), + ], + ), + ); + }, + ), + ); + } + + Widget _buildFavoritesSection(context) { + return Column( + children: [ + _buildFavoritesTitle(context, 'fav', '收藏夹'), + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: MediaQuery.textScalerOf(context).scale(110), + child: FutureBuilder( + future: ctr.queryFavFolder(), + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + List favFolderList = ctr.favFolderData.value.list!; + int favFolderCount = ctr.favFolderData.value.count!; + bool flag = favFolderCount > favFolderList.length; + return Obx( + () => ListView.builder( + itemCount: + ctr.favFolderData.value.list!.length + (flag ? 1 : 0), + itemBuilder: (context, index) { + if (flag && index == favFolderList.length) { + return Padding( + padding: const EdgeInsets.only(right: 14), + child: Center( + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.zero), + backgroundColor: + MaterialStateProperty.resolveWith( + (states) { + return Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.5); + }), + ), + onPressed: () => Get.toNamed('/fav'), + icon: Icon( + Icons.arrow_forward_ios, + size: 18, + color: Theme.of(context).colorScheme.primary, + ), + ), + ), + ); + } else { + return FavFolderItem( + item: ctr.favFolderData.value.list![index], + index: index, + ); + } + }, + scrollDirection: Axis.horizontal, ), - InkWell( - onTap: () => _mineController.pushFans(), - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - (_mineController.userStat.value.follower ?? '-') - .toString(), - key: ValueKey(_mineController - .userStat.value.follower - .toString()), - style: style), - ), - const SizedBox(height: 8), - Text( - '粉丝', - style: Theme.of(context).textTheme.labelMedium, - ), - ], - ), - ), - ], - ), - ); + ); + } else { + return SizedBox( + height: 110, + child: Center(child: Text(data['msg'])), + ); + } + } else { + // 骨架屏 + return Obx( + () => ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: ctr.favFolderData.value.list!.length, + itemBuilder: (context, index) { + return FavFolderItem( + item: ctr.favFolderData.value.list![index], + index: index, + ); + }, + ), + ); + } }, ), ), ], ); } + + Widget _buildFavoritesTitle( + BuildContext context, + String type, + String title, + ) { + return ListTile( + onTap: () {}, + leading: null, + dense: true, + title: Padding( + padding: const EdgeInsets.only(left: 10), + child: Text.rich( + TextSpan( + children: [ + TextSpan( + text: title, + style: TextStyle( + fontSize: Theme.of(context).textTheme.titleMedium!.fontSize, + fontWeight: FontWeight.bold), + ), + const TextSpan(text: ' '), + TextSpan( + text: '20', + style: TextStyle( + fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + ), + ), + // trailing: IconButton( + // onPressed: () {}, + // icon: const Icon( + // Icons.refresh, + // size: 20, + // ), + // ), + ); + } } -class ActionItem extends StatelessWidget { - final Icon? icon; - final Function? onTap; - final String? text; - - const ActionItem({ - Key? key, - this.icon, - this.onTap, - this.text, - }) : super(key: key); - +class FavFolderItem extends StatelessWidget { + const FavFolderItem({super.key, this.item, this.index}); + final FavFolderItemData? item; + final int? index; @override Widget build(BuildContext context) { - return InkWell( - onTap: () {}, - borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon(icon!.icon!), - const SizedBox(height: 8), - Text( - text!, - style: Theme.of(context).textTheme.labelMedium, - ), - ], + String heroTag = Utils.makeHeroTag(item!.fid); + return Container( + margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), + child: InkWell( + onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { + 'mediaId': item!.id.toString(), + 'heroTag': heroTag, + 'isOwner': '1', + }), + borderRadius: StyleString.mdRadius, + child: Stack( + children: [ + NetworkImgLayer( + src: item!.cover, + width: 175, + height: 110, + ), + // 渐变 + Positioned( + left: 0, + right: 0, + top: 60, + bottom: 0, + child: Container( + padding: const EdgeInsets.only(bottom: 8, left: 10, right: 2), + decoration: BoxDecoration( + borderRadius: StyleString.mdRadius, + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.black.withOpacity(0), + Colors.black.withOpacity(0.6), + ], + ), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + RichText( + maxLines: 1, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(color: Colors.white), + children: [ + TextSpan(text: item!.title!), + const TextSpan(text: ' '), + if (item!.mediaCount! > 0) + TextSpan( + text: item!.mediaCount!.toString(), + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ], + ), ), ); } diff --git a/lib/pages/setting/pages/navigation_bar_set.dart b/lib/pages/setting/pages/navigation_bar_set.dart index 09e92bc3..828500db 100644 --- a/lib/pages/setting/pages/navigation_bar_set.dart +++ b/lib/pages/setting/pages/navigation_bar_set.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; -import 'package:pilipala/models/common/tab_type.dart'; import 'package:pilipala/utils/storage.dart'; import '../../../models/common/nav_bar_config.dart'; @@ -23,7 +22,7 @@ class _NavigationbarSetPageState extends State { super.initState(); defaultNavTabs = defaultNavigationBars; navBarSort = settingStorage - .get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3]); + .get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3, 4]); // 对 tabData 进行排序 defaultNavTabs.sort((a, b) { int indexA = navBarSort.indexOf(a['id']); diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index e0ff113c..81105ce1 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -8,7 +8,6 @@ import 'package:hive/hive.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/utils/cookie.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/id_utils.dart'; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 7840c126..2f7dc782 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -8,6 +8,7 @@ import 'package:pilipala/pages/message/at/index.dart'; import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; import 'package:pilipala/pages/message/system/index.dart'; +import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -27,7 +28,6 @@ import '../pages/html/index.dart'; import '../pages/later/index.dart'; import '../pages/live_room/view.dart'; import '../pages/login/index.dart'; -import '../pages/media/index.dart'; import '../pages/member/index.dart'; import '../pages/member_archive/index.dart'; import '../pages/member_coin/index.dart'; @@ -75,8 +75,6 @@ class Routes { // 设置 CustomGetPage(name: '/setting', page: () => const SettingPage()), // - CustomGetPage(name: '/media', page: () => const MediaPage()), - // CustomGetPage(name: '/fav', page: () => const FavPage()), // CustomGetPage(name: '/favDetail', page: () => const FavDetailPage()), @@ -183,6 +181,8 @@ class Routes { // 系统通知 CustomGetPage( name: '/messageSystem', page: () => const MessageSystemPage()), + // 我的 + CustomGetPage(name: '/mine', page: () => const MinePage()), ]; } diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 2687a8c2..07afd2dc 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -10,7 +10,6 @@ import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/mine/index.dart'; import 'package:pilipala/utils/cookie.dart'; import 'package:pilipala/utils/storage.dart'; @@ -31,9 +30,6 @@ class LoginUtils { DynamicsController dynamicsCtr = Get.find(); dynamicsCtr.userLogin.value = status; - - MediaController mediaCtr = Get.find(); - mediaCtr.userLogin.value = status; } catch (err) { SmartDialog.showToast('refreshLoginStatus error: ${err.toString()}'); } @@ -84,8 +80,6 @@ class LoginUtils { final HomeController homeCtr = Get.find(); homeCtr.updateLoginStatus(true); homeCtr.userFace.value = result['data'].face; - final MediaController mediaCtr = Get.find(); - mediaCtr.mid = result['data'].mid; await LoginUtils.refreshLoginStatus(true); } catch (err) { SmartDialog.show(builder: (BuildContext context) { From 5f02c17a2d8d31c8999a6bc816f4ad79990fb84b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 18 Sep 2024 00:29:43 +0800 Subject: [PATCH 243/438] =?UTF-8?q?fix:=20=E5=85=B3=E6=B3=A8up=E4=B8=BB?= =?UTF-8?q?=E6=90=9C=E7=B4=A2ps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow_search/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/follow_search/controller.dart b/lib/pages/follow_search/controller.dart index 9fd1590d..7dc47a45 100644 --- a/lib/pages/follow_search/controller.dart +++ b/lib/pages/follow_search/controller.dart @@ -48,7 +48,7 @@ class FollowSearchController extends GetxController { return {'status': true, 'data': [].obs}; } if (type == 'init') { - ps = 1; + pn = 1; } var res = await MemberHttp.getfollowSearch( mid: mid, From 476fef887738bcc9925ad9d46859bc8931a3660a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 18 Sep 2024 22:17:06 +0800 Subject: [PATCH 244/438] mod --- lib/pages/mine/view.dart | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 757fbbc5..4246e43e 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -366,16 +366,18 @@ class _MinePageState extends State } else { // 骨架屏 return Obx( - () => ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: ctr.favFolderData.value.list!.length, - itemBuilder: (context, index) { - return FavFolderItem( - item: ctr.favFolderData.value.list![index], - index: index, - ); - }, - ), + () => ctr.favFolderData.value.list != null + ? ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: ctr.favFolderData.value.list!.length, + itemBuilder: (context, index) { + return FavFolderItem( + item: ctr.favFolderData.value.list![index], + index: index, + ); + }, + ) + : const SizedBox(), ); } }, @@ -391,7 +393,7 @@ class _MinePageState extends State String title, ) { return ListTile( - onTap: () {}, + onTap: () => Get.toNamed('/fav'), leading: null, dense: true, title: Padding( From cf66d3be4c04eb0810576eae0ead9b14e3d007dc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Sep 2024 15:14:38 +0800 Subject: [PATCH 245/438] =?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 265/438] =?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 266/438] =?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 267/438] =?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 166dceb1bd6a04ea92cb5ae8845d5805a9cf3436 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Sep 2024 23:21:17 +0800 Subject: [PATCH 268/438] typo --- lib/pages/setting/style_setting.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 5fca0c86..64e4bdb8 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -103,7 +103,7 @@ class _StyleSettingState extends State { needReboot: true, ), const SetSwitchItem( - title: '首页底栏背景渐变', + title: '首页顶部背景渐变', setKey: SettingBoxKey.enableGradientBg, defaultVal: true, needReboot: true, From f95bcb2e6cc3c05608c025a53fa49d7c65d67f2b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Sep 2024 23:52:50 +0800 Subject: [PATCH 269/438] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BA=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index c0d41069..3d6de86a 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1081,7 +1081,7 @@ class MorePanel extends StatelessWidget { // break; case 'delete': // 删除评论提示 - bool? isConfirm = await showDialog( + await showDialog( context: Get.context!, builder: (context) { return AlertDialog( @@ -1089,15 +1089,25 @@ class MorePanel extends StatelessWidget { content: const Text('删除评论后,评论下所有回复将被删除,确定删除吗?'), actions: [ TextButton( - onPressed: () => Get.back(result: false), + onPressed: () => Get.back(), child: Text('取消', style: TextStyle( color: Theme.of(context).colorScheme.outline)), ), TextButton( - onPressed: () { - Get.back(result: true); - SmartDialog.showToast('删除成功'); + onPressed: () async { + Get.back(); + var result = await ReplyHttp.replyDel( + type: item.type!, + oid: item.oid!, + rpid: item.rpid!, + ); + if (result['status']) { + SmartDialog.showToast('评论删除成功,需手动刷新'); + Get.back(); + } else { + SmartDialog.showToast(result['msg']); + } }, child: const Text('确定'), ), @@ -1105,18 +1115,6 @@ class MorePanel extends StatelessWidget { ); }, ); - if (isConfirm == null || !isConfirm) { - return; - } - SmartDialog.showLoading(msg: '删除中...'); - var result = await ReplyHttp.replyDel( - type: item.type!, - oid: item.oid!, - rpid: item.rpid!, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(result["msg"]); - break; default: } From ec5cc6ff91d42a89759a1d6b06568c7ee96ebaa4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 01:01:45 +0800 Subject: [PATCH 270/438] =?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 79c148adebce3dc4dff3512f75daf6e964420c55 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 01:12:43 +0800 Subject: [PATCH 271/438] =?UTF-8?q?fix:=20=E9=93=BE=E6=8E=A5=E5=85=9C?= =?UTF-8?q?=E5=BA=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/webview/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index e0ff113c..0fa24dea 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -95,6 +95,6 @@ class WebviewController extends GetxController { }, ), ) - ..loadRequest(Uri.parse(url)); + ..loadRequest(Uri.parse(url.startsWith('http') ? url : 'https://$url')); } } From 400b87395a1e8442765e83a922548e5d38758048 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 12:00:38 +0800 Subject: [PATCH 272/438] =?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 273/438] =?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 274/438] =?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, ), ), From c3d23dfdba23325ec3b93c79054bfc0f656580c3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 15:29:08 +0800 Subject: [PATCH 275/438] typo --- lib/pages/video/detail/controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c8be4f43..d7b1529a 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -115,7 +115,7 @@ class VideoDetailController extends GetxController ].obs; RxDouble sheetHeight = 0.0.obs; RxString archiveSourceType = 'dash'.obs; - ScrollController? replyScrillController; + ScrollController? replyScrollController; List mediaList = []; RxBool isWatchLaterVisible = false.obs; RxString watchLaterTitle = ''.obs; @@ -574,12 +574,12 @@ class VideoDetailController extends GetxController } void onControllerCreated(ScrollController controller) { - replyScrillController = controller; + replyScrollController = controller; } void onTapTabbar(int index) { if (index == 1 && tabCtr.index == 1) { - replyScrillController?.animateTo(0, + replyScrollController?.animateTo(0, duration: const Duration(milliseconds: 300), curve: Curves.ease); } } From ed9a75ecd3c46bf91af15fbe30b264f2f6ebcc5c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 30 Sep 2024 11:23:40 +0800 Subject: [PATCH 276/438] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E6=80=BB?= =?UTF-8?q?=E7=BB=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/ai.dart | 8 ++++---- .../video/detail/introduction/controller.dart | 4 ++-- lib/pages/video/detail/widgets/ai_detail.dart | 19 +++++++++++-------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/lib/models/video/ai.dart b/lib/models/video/ai.dart index a06fa79d..0816c09d 100644 --- a/lib/models/video/ai.dart +++ b/lib/models/video/ai.dart @@ -39,11 +39,11 @@ class ModelResult { ModelResult.fromJson(Map json) { resultType = json['result_type']; summary = json['summary']; - outline = json['result_type'] == 2 - ? json['outline'] + outline = json['result_type'] == 0 + ? [] + : json['outline'] .map((e) => OutlineItem.fromJson(e)) - .toList() - : []; + .toList(); } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 04a1249a..c0f6eebb 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -541,7 +541,7 @@ class VideoIntroController extends GetxController { // ai总结 Future aiConclusion() async { - SmartDialog.showLoading(msg: '正在生产ai总结'); + SmartDialog.showLoading(msg: '正在生成ai总结'); final res = await VideoHttp.aiConclusion( bvid: bvid, cid: lastPlayCid.value, @@ -551,7 +551,7 @@ class VideoIntroController extends GetxController { if (res['status']) { modelResult = res['data'].modelResult; } else { - SmartDialog.showToast("当前视频可能暂不支持AI视频总结"); + SmartDialog.showToast("当前视频暂不支持AI视频总结"); } return res; } diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 37d51106..b71b026d 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -49,15 +49,18 @@ class AiDetail extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - SelectableText( - modelResult!.summary!, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - height: 1.5, + if (modelResult!.resultType != 0 && + modelResult!.summary != '') ...[ + SelectableText( + modelResult!.summary!, + style: const TextStyle( + fontSize: 15, + fontWeight: FontWeight.bold, + height: 1.5, + ), ), - ), - const SizedBox(height: 20), + const SizedBox(height: 20), + ], ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), From 66d2ac9777bb3038e792d3d6e0ef7cc0c5eac921 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 30 Sep 2024 11:38:39 +0800 Subject: [PATCH 277/438] =?UTF-8?q?fix:=20=20=E7=B2=89=E4=B8=9D=E4=B8=8D?= =?UTF-8?q?=E5=8F=AF=E8=A7=81=E6=97=B6=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fan/controller.dart | 1 - lib/pages/fan/view.dart | 14 +++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart index c1c2a427..6661d9fe 100644 --- a/lib/pages/fan/controller.dart +++ b/lib/pages/fan/controller.dart @@ -49,7 +49,6 @@ class FansController extends GetxController { } else if (type == 'onLoad') { fansList.addAll(res['data'].list); } - print(total); if ((pn == 1 && total < ps) || res['data'].list.isEmpty) { loadingText.value = '没有更多了'; } diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart index 47372057..5d5c02a7 100644 --- a/lib/pages/fan/view.dart +++ b/lib/pages/fan/view.dart @@ -103,9 +103,17 @@ class _FansPageState extends State { ), ); } else { - return HttpError( - errMsg: data['msg'], - fn: () => _fansController.queryFans('init'), + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () { + _futureBuilderFuture = + _fansController.queryFans('init'); + }, + ) + ], ); } } else { From 19866fe08054fe16b418f49017b41dff27a5c82a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 30 Sep 2024 12:00:30 +0800 Subject: [PATCH 278/438] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E5=9B=9E=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index d7b1529a..a57ec1de 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -578,7 +578,7 @@ class VideoDetailController extends GetxController } void onTapTabbar(int index) { - if (index == 1 && tabCtr.index == 1) { + if (tabCtr.animation!.isCompleted && index == 1 && tabCtr.index == 1) { replyScrollController?.animateTo(0, duration: const Duration(milliseconds: 300), curve: Curves.ease); } From 877eeded37c0f0cc5d08db915a1933e3b687433c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 30 Sep 2024 13:09:40 +0800 Subject: [PATCH 279/438] =?UTF-8?q?fix:=20=E9=BB=91=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/blacklist/index.dart | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart index 402790f5..fb7e0891 100644 --- a/lib/pages/blacklist/index.dart +++ b/lib/pages/blacklist/index.dart @@ -61,7 +61,7 @@ class _BlackListPageState extends State { centerTitle: false, title: Obx( () => Text( - '黑名单管理 - ${_blackListController.total.value}', + '黑名单管理 ${_blackListController.total.value == 0 ? '' : '- ${_blackListController.total.value}'}', style: Theme.of(context).textTheme.titleMedium, ), ), @@ -76,8 +76,12 @@ class _BlackListPageState extends State { if (data['status']) { List list = _blackListController.blackList; return Obx( - () => list.length == 1 - ? const SizedBox() + () => list.isEmpty + ? CustomScrollView( + slivers: [ + HttpError(errMsg: '你没有拉黑任何人哦~_~', fn: () => {}) + ], + ) : ListView.builder( controller: scrollController, itemCount: list.length, From dd477247776839e04e09862257743ec506c25219 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 1 Oct 2024 18:48:01 +0800 Subject: [PATCH 280/438] =?UTF-8?q?feat:=20navBar=E5=B1=95=E7=A4=BA?= =?UTF-8?q?=E5=A4=B4=E5=83=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/controller.dart | 12 +++++++++++- lib/pages/main/view.dart | 29 +++++++++++++++-------------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index ad2a7781..8a0e4418 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -5,6 +5,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/common.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; @@ -24,6 +25,7 @@ class MainController extends GetxController { late PageController pageController; int selectedIndex = 0; Box userInfoCache = GStrorage.userInfo; + dynamic userInfo; RxBool userLogin = false.obs; late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; late bool enableGradientBg; @@ -37,7 +39,7 @@ class MainController extends GetxController { } hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: false); - var userInfo = userInfoCache.get('userInfoCache'); + userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( SettingBoxKey.dynamicBadgeMode, @@ -71,12 +73,20 @@ class MainController extends GetxController { } int dynamicItemIndex = navigationBars.indexWhere((item) => item['label'] == "动态"); + int mineItemIndex = + navigationBars.indexWhere((item) => item['label'] == "我的"); var res = await CommonHttp.unReadDynamic(); var data = res['data']; if (dynamicItemIndex != -1) { navigationBars[dynamicItemIndex]['count'] = data == null ? 0 : data.length; // 修改 count 属性为新的值 } + if (mineItemIndex != -1 && userInfo != null) { + Widget avatar = NetworkImgLayer( + width: 28, height: 28, src: userInfo.face, type: 'avatar'); + navigationBars[mineItemIndex]['icon'] = avatar; + navigationBars[mineItemIndex]['selectIcon'] = avatar; + } navigationBars.refresh(); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 825e65bc..93ee2d83 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -181,20 +181,21 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { destinations: [ ..._mainController.navigationBars.map((e) { return NavigationDestination( - icon: Badge( - label: _mainController - .dynamicBadgeType.value == - DynamicBadgeMode.number - ? Text(e['count'].toString()) - : null, - padding: - const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: _mainController - .dynamicBadgeType.value != - DynamicBadgeMode.hidden && - e['count'] > 0, - child: e['icon'], - ), + icon: _mainController + .dynamicBadgeType.value == + DynamicBadgeMode.number + ? Badge( + label: Text(e['count'].toString()), + padding: const EdgeInsets.fromLTRB( + 6, 0, 6, 0), + isLabelVisible: _mainController + .dynamicBadgeType + .value != + DynamicBadgeMode.hidden && + e['count'] > 0, + child: e['icon'], + ) + : e['icon'], selectedIcon: e['selectIcon'], label: e['label'], ); From fed1bf4e9dde465b8a1ef040320cd64c96dc9134 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 1 Oct 2024 22:17:47 +0800 Subject: [PATCH 281/438] =?UTF-8?q?feat:=20=E8=B5=84=E6=96=99=E7=BC=96?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 6 + lib/http/user.dart | 50 ++++++++ lib/pages/member/widgets/profile.dart | 3 +- lib/pages/mine_edit/controller.dart | 45 +++++++ lib/pages/mine_edit/index.dart | 4 + lib/pages/mine_edit/view.dart | 177 ++++++++++++++++++++++++++ lib/router/app_pages.dart | 3 + 7 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 lib/pages/mine_edit/controller.dart create mode 100644 lib/pages/mine_edit/index.dart create mode 100644 lib/pages/mine_edit/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index df0b9c85..0ad15f8f 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -592,4 +592,10 @@ class Api { /// 直播间记录 static const String liveRoomEntry = '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction'; + + /// 用户信息 + static const String accountInfo = '/x/member/web/account'; + + /// 更新用户信息 + static const String updateAccountInfo = '/x/member/web/update'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index f4535905..207274e1 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:developer'; +import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:html/parser.dart'; import 'package:pilipala/models/video/later.dart'; @@ -537,4 +538,53 @@ class UserHttp { .toList() }; } + + static Future getAccountInfo() async { + var res = await Request().get( + Api.accountInfo, + data: {'web_location': 333.33}, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': {}, + 'mag': res.data['message'], + }; + } + } + + static Future updateAccountInfo({ + required String uname, + required String sign, + required String sex, + required String birthday, + }) async { + var res = await Request().post( + Api.updateAccountInfo, + data: { + 'uname': uname, + 'usersign': sign, + 'sex': sex, + 'birthday': birthday, + 'csrf': await Request.getCsrf(), + }, + options: Options(contentType: Headers.formUrlEncodedContentType), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'msg': '更新成功', + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index 8c6385db..596ace46 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -1,5 +1,4 @@ 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/models/live/item.dart'; @@ -233,7 +232,7 @@ class ProfilePanel extends StatelessWidget { if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[ TextButton( onPressed: () { - SmartDialog.showToast('功能开发中 💪'); + Get.toNamed('/mineEdit'); }, style: TextButton.styleFrom( padding: const EdgeInsets.only(left: 80, right: 80), diff --git a/lib/pages/mine_edit/controller.dart b/lib/pages/mine_edit/controller.dart new file mode 100644 index 00000000..9bc43246 --- /dev/null +++ b/lib/pages/mine_edit/controller.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/http/user.dart'; +import 'package:pilipala/utils/storage.dart'; + +class MineEditController extends GetxController { + Box userInfoCache = GStrorage.userInfo; + final formKey = GlobalKey(); + final TextEditingController unameCtr = TextEditingController(); + final TextEditingController useridCtr = TextEditingController(); + final TextEditingController signCtr = TextEditingController(); + final TextEditingController birthdayCtr = TextEditingController(); + String? sex; + dynamic userInfo; + + @override + void onInit() { + super.onInit(); + userInfo = userInfoCache.get('userInfoCache'); + } + + Future getAccountInfo() async { + var res = await UserHttp.getAccountInfo(); + if (res['status']) { + unameCtr.text = res['data']['uname']; + useridCtr.text = res['data']['userid']; + signCtr.text = res['data']['sign']; + birthdayCtr.text = res['data']['birthday']; + sex = res['data']['sex']; + } + return res; + } + + Future updateAccountInfo() async { + var res = await UserHttp.updateAccountInfo( + uname: unameCtr.text, + sign: signCtr.text, + sex: sex!, + birthday: birthdayCtr.text, + ); + SmartDialog.showToast(res['status'] ? res['msg'] : "更新失败:${res['msg']}"); + } +} diff --git a/lib/pages/mine_edit/index.dart b/lib/pages/mine_edit/index.dart new file mode 100644 index 00000000..88806448 --- /dev/null +++ b/lib/pages/mine_edit/index.dart @@ -0,0 +1,4 @@ +library mine_edit; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/mine_edit/view.dart b/lib/pages/mine_edit/view.dart new file mode 100644 index 00000000..700c707e --- /dev/null +++ b/lib/pages/mine_edit/view.dart @@ -0,0 +1,177 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class MineEditPage extends StatefulWidget { + const MineEditPage({super.key}); + + @override + State createState() => _MineEditPageState(); +} + +class _MineEditPageState extends State { + final MineEditController ctr = Get.put(MineEditController()); + late Future _futureBuilderFuture; + + @override + void initState() { + super.initState(); + _futureBuilderFuture = ctr.getAccountInfo(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('编辑资料'), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: FutureBuilder( + future: _futureBuilderFuture, + builder: ((context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + return Form( + key: ctr.formKey, + child: Column( + children: [ + // 用户头像 + // InkWell( + // onTap: () {}, + // child: CircleAvatar( + // radius: 50, + // backgroundColor: Colors.transparent, + // backgroundImage: + // NetworkImage(ctr.userInfo.face), // 替换为实际的头像路径 + // ), + // ), + const SizedBox(height: 24.0), + // 昵称 + TextFormField( + controller: ctr.unameCtr, + decoration: const InputDecoration( + labelText: '昵称', + border: OutlineInputBorder(), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return '请输入昵称'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + // 用户名 + TextFormField( + controller: ctr.useridCtr, + decoration: const InputDecoration( + labelText: '用户名', + border: OutlineInputBorder(), + enabled: false, + ), + readOnly: true, + validator: (value) { + if (value == null || value.isEmpty) { + return '请输入用户名'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + // 签名 + TextFormField( + controller: ctr.signCtr, + decoration: const InputDecoration( + labelText: '签名', + border: OutlineInputBorder(), + ), + validator: (value) { + if (value == null || value.isEmpty) { + return '请输入签名'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + // 性别 + DropdownButtonFormField( + value: ctr.sex, + decoration: const InputDecoration( + labelText: '性别', + border: OutlineInputBorder(), + ), + items: ['男', '女', '保密'].map((String value) { + return DropdownMenuItem( + value: value, + child: Text(value), + ); + }).toList(), + onChanged: (newValue) { + ctr.sex = newValue; + }, + validator: (value) { + if (value == null || value.isEmpty) { + return '请选择性别'; + } + return null; + }, + ), + const SizedBox(height: 20.0), + // 出生日期 + TextFormField( + controller: ctr.birthdayCtr, + decoration: const InputDecoration( + labelText: '出生日期', + border: OutlineInputBorder(), + ), + enabled: false, + readOnly: true, + onTap: () async { + // DateTime? pickedDate = await showDatePicker( + // context: context, + // initialDate: DateTime(1995, 12, 23), + // firstDate: DateTime(1900), + // lastDate: DateTime(2100), + // ); + // if (pickedDate != null) { + // ctr.birthdayCtr.text = + // "${pickedDate.toLocal()}".split(' ')[0]; + // } + }, + validator: (value) { + if (value == null || value.isEmpty) { + return '请选择出生日期'; + } + return null; + }, + ), + const SizedBox(height: 30.0), + // 提交按钮 + ElevatedButton( + onPressed: () { + if (ctr.formKey.currentState!.validate()) { + // 处理表单提交 + ctr.updateAccountInfo(); + } + }, + child: const Text('提交'), + ), + ], + ), + ); + } else { + return const SizedBox(); + } + } else { + return const SizedBox(); + } + })), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index dec72b06..7a14b499 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -11,6 +11,7 @@ import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; import 'package:pilipala/pages/message/system/index.dart'; import 'package:pilipala/pages/mine/index.dart'; +import 'package:pilipala/pages/mine_edit/index.dart'; import 'package:pilipala/pages/opus/index.dart'; import 'package:pilipala/pages/read/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; @@ -196,6 +197,8 @@ class Routes { // 用户专栏 CustomGetPage( name: '/memberArticle', page: () => const MemberArticlePage()), + // 用户信息编辑 + CustomGetPage(name: '/mineEdit', page: () => const MineEditPage()), ]; } From 1f391cbfd08661aed2417e60d5567d9068b432bc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 2 Oct 2024 22:26:13 +0800 Subject: [PATCH 282/438] fix --- lib/pages/home/view.dart | 8 ++++---- lib/pages/mine/view.dart | 17 +++++++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 11d36c9b..2c32aa0d 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -36,10 +36,10 @@ class _HomePageState extends State showUserBottomSheet() { feedBack(); final MainController mainController = Get.put(MainController()); - if (mainController.navigationBars - .where((item) => item['label'] == "我的") - .isNotEmpty) { - mainController.pageController.jumpToPage(2); + int mineItemIndex = mainController.navigationBars + .indexWhere((item) => item['label'] == "我的"); + if (mineItemIndex != -1) { + mainController.pageController.jumpToPage(mineItemIndex); } else { Get.toNamed('/mine'); } diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 90fa4958..4fcfdcf4 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -98,9 +98,14 @@ class _MinePageState extends State }, ), _buildMenuSection(context), - Divider( - height: 25, - color: Theme.of(context).dividerColor.withOpacity(0.1), + Obx( + () => Visibility( + visible: ctr.userLogin.value, + child: Divider( + height: 25, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + ), ), Obx( () => ctr.userLogin.value @@ -320,8 +325,8 @@ class _MinePageState extends State future: ctr.queryFavFolder(), builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { List favFolderList = ctr.favFolderData.value.list!; int favFolderCount = ctr.favFolderData.value.count!; bool flag = favFolderCount > favFolderList.length; @@ -369,7 +374,7 @@ class _MinePageState extends State } else { return SizedBox( height: 110, - child: Center(child: Text(data['msg'])), + child: Center(child: Text(data?['msg'] ?? '')), ); } } else { From 31f87bd24cb8f6fff9860dde2ff0833a0412092e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 2 Oct 2024 22:27:20 +0800 Subject: [PATCH 283/438] =?UTF-8?q?fix:=20=E7=99=BB=E5=BD=95=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/login.dart | 23 ++++++++++--- lib/pages/login/controller.dart | 10 +++++- lib/pages/login/view.dart | 59 ++++++++++++++++++++++----------- 3 files changed, 67 insertions(+), 25 deletions(-) diff --git a/lib/http/login.dart b/lib/http/login.dart index 2437b72a..71d61b3f 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -244,12 +244,25 @@ class LoginHttp { ), ); if (res.data['code'] == 0) { - return { - 'status': true, - 'data': res.data['data'], - }; + if (res.data['data']['status'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'code': 1, + 'data': res.data['data'], + 'msg': res.data['data']['message'], + }; + } } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index fbb06e2f..9e7fb339 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -45,6 +45,7 @@ class LoginPageController extends GetxController { RxInt validSeconds = 180.obs; Timer? validTimer; late String qrcodeKey; + RxBool passwordVisible = false.obs; // 监听pageView切换 void onPageChange(int index) { @@ -128,7 +129,14 @@ class LoginPageController extends GetxController { if (res['status']) { await LoginUtils.confirmLogin('', null); } else { - SmartDialog.showToast(res['msg']); + await SmartDialog.showToast(res['msg']); + if (res.containsKey('code') && res['code'] == 1) { + Get.toNamed('/webview', parameters: { + 'url': res['data']['data']['url'], + 'type': 'url', + 'pageTitle': '登录验证', + }); + } } } else { SmartDialog.showToast(webKeyRes['msg']); diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 85a8adf0..4fe21792 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -269,25 +269,46 @@ class _LoginPageState extends State { ), Container( margin: const EdgeInsets.only(top: 38, bottom: 15), - child: TextFormField( - controller: _loginPageCtr.passwordTextController, - focusNode: _loginPageCtr.passwordTextFieldNode, - keyboardType: TextInputType.visiblePassword, - decoration: InputDecoration( - isDense: true, - labelText: '输入密码', - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(6.0), - ), - ), - // 校验用户名 - validator: (v) { - return v!.trim().isNotEmpty ? null : "密码不能为空"; - }, - onSaved: (val) { - print(val); - }, - ), + child: Obx(() => TextFormField( + controller: + _loginPageCtr.passwordTextController, + focusNode: + _loginPageCtr.passwordTextFieldNode, + keyboardType: TextInputType.visiblePassword, + obscureText: + _loginPageCtr.passwordVisible.value, + decoration: InputDecoration( + isDense: true, + labelText: '输入密码', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + suffixIcon: IconButton( + icon: Icon( + _loginPageCtr.passwordVisible.value + ? Icons.visibility + : Icons.visibility_off, + color: Theme.of(context) + .colorScheme + .primary, + ), + onPressed: () { + _loginPageCtr.passwordVisible.value = + !_loginPageCtr + .passwordVisible.value; + }, + ), + ), + // 校验用户名 + validator: (v) { + return v!.trim().isNotEmpty + ? null + : "密码不能为空"; + }, + onSaved: (val) { + print(val); + }, + )), ), const Spacer(), Row( From 2a436cfcfcb0efa0b82c57f443c6c6e2846a258a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 3 Oct 2024 16:30:47 +0800 Subject: [PATCH 284/438] =?UTF-8?q?feat:=20=E5=8E=9F=E7=BD=91=E9=A1=B5?= =?UTF-8?q?=E6=9F=A5=E7=9C=8B=E4=B8=93=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/opus/controller.dart | 10 ++++++++++ lib/pages/opus/view.dart | 11 ++++++++--- lib/pages/read/controller.dart | 10 ++++++++++ lib/pages/read/view.dart | 11 ++++++++--- lib/pages/webview/controller.dart | 11 ----------- lib/utils/app_scheme.dart | 2 +- 6 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/pages/opus/controller.dart b/lib/pages/opus/controller.dart index 86dd67a5..3cf844cc 100644 --- a/lib/pages/opus/controller.dart +++ b/lib/pages/opus/controller.dart @@ -39,6 +39,7 @@ class OpusController extends GetxController { 'articleType': 'cv', }); } else { + title.value = res['data'].detail!.basic!.title!; opusData.value = res['data']; } } @@ -91,6 +92,15 @@ class OpusController extends GetxController { ); } + // 跳转webview + void onJumpWebview() { + Get.toNamed('/webview', parameters: { + 'url': url, + 'type': 'webview', + 'pageTitle': title.value, + }); + } + @override void onClose() { scrollController.removeListener(_scrollListener); diff --git a/lib/pages/opus/view.dart b/lib/pages/opus/view.dart index 8535230f..434a9405 100644 --- a/lib/pages/opus/view.dart +++ b/lib/pages/opus/view.dart @@ -60,9 +60,14 @@ class _OpusPageState extends State { }, ), actions: [ - IconButton( - icon: const Icon(Icons.more_vert_rounded), - onPressed: () {}, + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: controller.onJumpWebview, + child: const Text('查看原网页'), + ) + ], ), const SizedBox(width: 16), ], diff --git a/lib/pages/read/controller.dart b/lib/pages/read/controller.dart index a0e4ef8e..efa43c98 100644 --- a/lib/pages/read/controller.dart +++ b/lib/pages/read/controller.dart @@ -22,6 +22,7 @@ class ReadPageController extends GetxController { title.value = Get.parameters['title'] ?? ''; id = Get.parameters['id']!; articleType = Get.parameters['articleType']!; + url = 'https://www.bilibili.com/read/cv$id'; scrollController.addListener(_scrollListener); fetchViewInfo(); } @@ -85,6 +86,15 @@ class ReadPageController extends GetxController { ReadHttp.getViewInfo(id: id); } + // 跳转webview + void onJumpWebview() { + Get.toNamed('/webview', parameters: { + 'url': url, + 'type': 'webview', + 'pageTitle': title.value, + }); + } + @override void onClose() { scrollController.removeListener(_scrollListener); diff --git a/lib/pages/read/view.dart b/lib/pages/read/view.dart index 7c1e0601..d37eeae5 100644 --- a/lib/pages/read/view.dart +++ b/lib/pages/read/view.dart @@ -72,9 +72,14 @@ class _ReadPageState extends State { }, ), actions: [ - IconButton( - icon: const Icon(Icons.more_vert_rounded), - onPressed: () {}, + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: controller.onJumpWebview, + child: const Text('查看原网页'), + ) + ], ), const SizedBox(width: 16), ], diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index e0ff113c..53e76231 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -1,19 +1,8 @@ -// ignore_for_file: avoid_print - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/http/init.dart'; -import 'package:pilipala/http/user.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/utils/cookie.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/login.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:webview_flutter/webview_flutter.dart'; class WebviewController extends GetxController { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 67b8a5d5..22184139 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -82,7 +82,7 @@ class PiliSchame { case 'opus': if (path.startsWith('/detail')) { var opusId = path.split('/').last; - Get.toNamed('/opus', arguments: { + Get.toNamed('/opus', parameters: { 'title': '', 'id': opusId, 'articleType': 'opus', From e949dd60ce30b448cb0d6888d12b21987d7c2f07 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 3 Oct 2024 20:35:33 +0800 Subject: [PATCH 285/438] =?UTF-8?q?opt:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/content_panel.dart | 26 ---- lib/pages/dynamics/widgets/pic_panel.dart | 26 ---- lib/pages/opus/controller.dart | 26 ---- lib/pages/read/controller.dart | 26 ---- .../detail/reply/widgets/reply_item.dart | 29 ---- .../whisper_detail/widget/chat_item.dart | 26 ---- .../pl_gallery/interactiveviewer_gallery.dart | 125 +++++++++++++++--- 7 files changed, 106 insertions(+), 178 deletions(-) diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index 28451dde..c1a6185e 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -1,5 +1,4 @@ // 内容 -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/badge.dart'; @@ -166,31 +165,6 @@ class _ContentState extends State { builder: (BuildContext context) => InteractiveviewerGallery( sources: picList, initIndex: initIndex, - itemBuilder: ( - BuildContext context, - int index, - bool isFocus, - bool enablePageView, - ) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (enablePageView) { - Navigator.of(context).pop(); - } - }, - child: Center( - child: Hero( - tag: picList[index], - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 0), - imageUrl: picList[index], - fit: BoxFit.contain, - ), - ), - ), - ); - }, onPageChanged: (int pageIndex) {}, ), ), diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 783fe89b..bd3f91f6 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,4 +1,3 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; @@ -12,31 +11,6 @@ void onPreviewImg(currentUrl, picList, initIndex, context) { builder: (BuildContext context) => InteractiveviewerGallery( sources: picList, initIndex: initIndex, - itemBuilder: ( - BuildContext context, - int index, - bool isFocus, - bool enablePageView, - ) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (enablePageView) { - Navigator.of(context).pop(); - } - }, - child: Center( - child: Hero( - tag: picList[index], - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 0), - imageUrl: picList[index], - fit: BoxFit.contain, - ), - ), - ), - ); - }, onPageChanged: (int pageIndex) {}, ), ), diff --git a/lib/pages/opus/controller.dart b/lib/pages/opus/controller.dart index 3cf844cc..f00c45b5 100644 --- a/lib/pages/opus/controller.dart +++ b/lib/pages/opus/controller.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/read.dart'; import 'package:pilipala/models/read/opus.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart'; import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart'; @@ -61,31 +60,6 @@ class OpusController extends GetxController { builder: (BuildContext context) => InteractiveviewerGallery( sources: picList, initIndex: initIndex, - itemBuilder: ( - BuildContext context, - int index, - bool isFocus, - bool enablePageView, - ) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (enablePageView) { - Navigator.of(context).pop(); - } - }, - child: Center( - child: Hero( - tag: picList[index], - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 0), - imageUrl: picList[index], - fit: BoxFit.contain, - ), - ), - ), - ); - }, onPageChanged: (int pageIndex) {}, ), ), diff --git a/lib/pages/read/controller.dart b/lib/pages/read/controller.dart index efa43c98..178ebfda 100644 --- a/lib/pages/read/controller.dart +++ b/lib/pages/read/controller.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/read.dart'; @@ -51,31 +50,6 @@ class ReadPageController extends GetxController { builder: (BuildContext context) => InteractiveviewerGallery( sources: picList, initIndex: initIndex, - itemBuilder: ( - BuildContext context, - int index, - bool isFocus, - bool enablePageView, - ) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (enablePageView) { - Navigator.of(context).pop(); - } - }, - child: Center( - child: Hero( - tag: picList[index], - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 0), - imageUrl: picList[index], - fit: BoxFit.contain, - ), - ), - ), - ); - }, onPageChanged: (int pageIndex) {}, ), ), diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 26dc2e5a..8bb6992a 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,7 +1,6 @@ import 'dart:math'; import 'package:appscheme/appscheme.dart'; -import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -583,34 +582,6 @@ InlineSpan buildContent( builder: (BuildContext context) => InteractiveviewerGallery( sources: picList, initIndex: initIndex, - itemBuilder: ( - BuildContext context, - int index, - bool isFocus, - bool enablePageView, - ) { - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: () { - if (enablePageView) { - Navigator.of(context).pop(); - final MainController mainController = - Get.find(); - mainController.imgPreviewStatus = false; - } - }, - child: Center( - child: Hero( - tag: picList[index] + randomInt, - child: CachedNetworkImage( - fadeInDuration: const Duration(milliseconds: 0), - imageUrl: picList[index], - fit: BoxFit.contain, - ), - ), - ), - ); - }, onPageChanged: (int pageIndex) {}, onDismissed: (int value) { print('onDismissed'); diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 7ddd1d83..01ede374 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -2,7 +2,6 @@ // 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'; @@ -170,31 +169,6 @@ class ChatItem extends StatelessWidget { 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) {}, ), ), diff --git a/lib/plugin/pl_gallery/interactiveviewer_gallery.dart b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart index 03ff4642..cd13194e 100644 --- a/lib/plugin/pl_gallery/interactiveviewer_gallery.dart +++ b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart @@ -2,10 +2,12 @@ library interactiveviewer_gallery; import 'dart:io'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pilipala/utils/download.dart'; import 'package:share_plus/share_plus.dart'; @@ -32,7 +34,7 @@ class InteractiveviewerGallery extends StatefulWidget { const InteractiveviewerGallery({ required this.sources, required this.initIndex, - required this.itemBuilder, + this.itemBuilder, this.maxScale = 4.5, this.minScale = 1.0, this.onPageChanged, @@ -47,7 +49,7 @@ class InteractiveviewerGallery extends StatefulWidget { final int initIndex; /// The item content - final IndexedFocusedWidgetBuilder itemBuilder; + final IndexedFocusedWidgetBuilder? itemBuilder; final double maxScale; @@ -246,12 +248,15 @@ class _InteractiveviewerGalleryState extends State _doubleTapLocalPosition = details.localPosition; }, onDoubleTap: onDoubleTap, - child: widget.itemBuilder( - context, - index, - index == currentIndex, - _enablePageView, - ), + onLongPress: onLongPress, + child: widget.itemBuilder != null + ? widget.itemBuilder!( + context, + index, + index == currentIndex, + _enablePageView, + ) + : _itemBuilder(widget.sources, index), ); }, ), @@ -302,17 +307,7 @@ class _InteractiveviewerGalleryState extends State PopupMenuItem( value: 1, onTap: () { - Clipboard.setData(ClipboardData( - text: - widget.sources[currentIndex!].toString())) - .then((value) { - SmartDialog.showToast('已复制到粘贴板'); - }).catchError((err) { - SmartDialog.showNotify( - msg: err.toString(), - notifyType: NotifyType.error, - ); - }); + onCopyImg(widget.sources[currentIndex!].toString()); }, child: const Text("复制图片"), ), @@ -350,6 +345,41 @@ class _InteractiveviewerGalleryState extends State Share.shareXFiles([XFile(path)], subject: imgUrl); } + // 复制图片 + void onCopyImg(String imgUrl) { + Clipboard.setData( + ClipboardData(text: widget.sources[currentIndex!].toString())) + .then((value) { + SmartDialog.showToast('已复制到粘贴板'); + }).catchError((err) { + SmartDialog.showNotify( + msg: err.toString(), + notifyType: NotifyType.error, + ); + }); + } + + Widget _itemBuilder(sources, index) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (_enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: sources[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: sources[index], + fit: BoxFit.contain, + ), + ), + ), + ); + } + onDoubleTap() { Matrix4 matrix = _transformationController!.value.clone(); double currentScale = matrix.row0.x; @@ -396,4 +426,61 @@ class _InteractiveviewerGalleryState extends State .forward(from: 0) .whenComplete(() => _onScaleChanged(targetScale)); } + + onLongPress() { + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return Container( + padding: + EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: + const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + ), + ListTile( + onTap: () { + onShareImg(widget.sources[currentIndex!]); + Navigator.of(context).pop(); + }, + title: const Text('分享图片'), + ), + ListTile( + onTap: () { + onCopyImg(widget.sources[currentIndex!].toString()); + Navigator.of(context).pop(); + }, + title: const Text('复制图片'), + ), + ListTile( + onTap: () { + DownloadUtils.downloadImg(widget.sources[currentIndex!]); + Navigator.of(context).pop(); + }, + title: const Text('保存图片'), + ), + ], + ), + ); + }, + ); + } } From 4cac02a4c0c6cd47ad78786dc5adde4fa37d5fb8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 3 Oct 2024 21:28:12 +0800 Subject: [PATCH 286/438] opt: Request().post --- lib/http/black.dart | 2 +- lib/http/danmaku.dart | 3 -- lib/http/dynamics.dart | 48 ++++++++++-------- lib/http/fav.dart | 4 +- lib/http/init.dart | 3 +- lib/http/live.dart | 54 ++++++++++++--------- lib/http/login.dart | 15 ------ lib/http/member.dart | 29 ++++++----- lib/http/msg.dart | 4 -- lib/http/reply.dart | 2 +- lib/http/user.dart | 16 +++--- lib/http/video.dart | 108 ++++++++++++++++++++++++----------------- 12 files changed, 151 insertions(+), 137 deletions(-) diff --git a/lib/http/black.dart b/lib/http/black.dart index 0c6a63ab..67356a92 100644 --- a/lib/http/black.dart +++ b/lib/http/black.dart @@ -28,7 +28,7 @@ class BlackHttp { static Future removeBlack({required int fid}) async { var res = await Request().post( Api.removeBlack, - queryParameters: { + data: { 'act': 6, 'csrf': await Request.getCsrf(), 'fid': fid, diff --git a/lib/http/danmaku.dart b/lib/http/danmaku.dart index 0b108755..2ed2a415 100644 --- a/lib/http/danmaku.dart +++ b/lib/http/danmaku.dart @@ -67,9 +67,6 @@ class DanmakaHttp { var response = await Request().post( Api.shootDanmaku, data: params, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); if (response.statusCode != 200) { return { diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 69619361..5ba5675e 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,4 +1,5 @@ import 'dart:math'; +import 'package:dio/dio.dart'; import '../models/dynamics/result.dart'; import '../models/dynamics/up.dart'; import 'index.dart'; @@ -69,7 +70,7 @@ class DynamicsHttp { }) async { var res = await Request().post( Api.likeDynamic, - queryParameters: { + data: { 'dynamic_id': dynamicId, 'up': up, 'csrf': await Request.getCsrf(), @@ -175,27 +176,32 @@ class DynamicsHttp { 'revs_id': {'dyn_type': 8, 'rid': oid} }; } - var res = await Request().post(Api.dynamicCreate, queryParameters: { - 'platform': 'web', - 'csrf': await Request.getCsrf(), - 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, - 'x-bili-web-req-json': {'spm_id': '333.999'}, - }, data: { - 'dyn_req': { - 'content': { - 'contents': [ - {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} - ] - }, - 'scene': scene, - 'attach_card': null, - 'upload_id': uploadId, - 'meta': { - 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} - } + var res = await Request().post( + Api.dynamicCreate, + queryParameters: { + 'platform': 'web', + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, }, - 'web_repost_src': webRepostSrc - }); + data: { + 'dyn_req': { + 'content': { + 'contents': [ + {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} + ] + }, + 'scene': scene, + 'attach_card': null, + 'upload_id': uploadId, + 'meta': { + 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} + } + }, + 'web_repost_src': webRepostSrc + }, + options: Options(contentType: 'application/json'), + ); if (res.data['code'] == 0) { return { 'status': true, diff --git a/lib/http/fav.dart b/lib/http/fav.dart index 6f49d68a..69577e7e 100644 --- a/lib/http/fav.dart +++ b/lib/http/fav.dart @@ -11,7 +11,7 @@ class FavHttp { }) async { var res = await Request().post( Api.editFavFolder, - queryParameters: { + data: { 'title': title, 'intro': intro, 'media_id': mediaId, @@ -43,7 +43,7 @@ class FavHttp { }) async { var res = await Request().post( Api.addFavFolder, - queryParameters: { + data: { 'title': title, 'intro': intro, 'cover': cover ?? '', diff --git a/lib/http/init.dart b/lib/http/init.dart index 6a90a87d..eae94ae4 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -260,7 +260,8 @@ class Request { url, data: data, queryParameters: queryParameters, - options: options, + options: + options ?? Options(contentType: Headers.formUrlEncodedContentType), cancelToken: cancelToken, ); // print('post success: ${response.data}'); diff --git a/lib/http/live.dart b/lib/http/live.dart index 1405e9ea..259f86fc 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -89,23 +89,26 @@ class LiveHttp { // 发送弹幕 static Future sendDanmaku({roomId, msg}) async { - var res = await Request().post(Api.sendLiveMsg, queryParameters: { - 'bubble': 0, - 'msg': msg, - 'color': 16777215, // 颜色 - 'mode': 1, // 模式 - 'room_type': 0, - 'jumpfrom': 71001, // 直播间来源 - 'reply_mid': 0, - 'reply_attr': 0, - 'replay_dmid': '', - 'statistics': {"appId": 100, "platform": 5}, - 'fontsize': 25, // 字体大小 - 'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳 - 'roomid': roomId, - 'csrf': await Request.getCsrf(), - 'csrf_token': await Request.getCsrf(), - }); + var res = await Request().post( + Api.sendLiveMsg, + data: { + 'bubble': 0, + 'msg': msg, + 'color': 16777215, // 颜色 + 'mode': 1, // 模式 + 'room_type': 0, + 'jumpfrom': 71001, // 直播间来源 + 'reply_mid': 0, + 'reply_attr': 0, + 'replay_dmid': '', + 'statistics': {"appId": 100, "platform": 5}, + 'fontsize': 25, // 字体大小 + 'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳 + 'roomid': roomId, + 'csrf': await Request.getCsrf(), + 'csrf_token': await Request.getCsrf(), + }, + ); if (res.data['code'] == 0) { return { 'status': true, @@ -145,12 +148,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': '', - }); + await Request().post( + Api.liveRoomEntry, + data: { + 'room_id': roomId, + 'platform': 'pc', + 'csrf_token': await Request.getCsrf(), + 'csrf': await Request.getCsrf(), + 'visit_id': '', + }, + ); } } diff --git a/lib/http/login.dart b/lib/http/login.dart index 2437b72a..32eda04d 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -71,9 +71,6 @@ class LoginHttp { var res = await Request().post( Api.webSmsCode, data: formData, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); if (res.data['code'] == 0) { return { @@ -106,9 +103,6 @@ class LoginHttp { var res = await Request().post( Api.webSmsLogin, data: formData, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); if (res.data['code'] == 0) { return { @@ -155,9 +149,6 @@ class LoginHttp { var res = await Request().post( Api.appSmsCode, data: data, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); print(res); } @@ -208,9 +199,6 @@ class LoginHttp { var res = await Request().post( Api.loginInByPwdApi, data: data, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); print(res); } @@ -239,9 +227,6 @@ class LoginHttp { var res = await Request().post( Api.loginInByWebPwd, data: formData, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); if (res.data['code'] == 0) { return { diff --git a/lib/http/member.dart b/lib/http/member.dart index 459d6747..c7b22359 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -198,13 +198,15 @@ class MemberHttp { // 设置分组 static Future addUsers(int? fids, String? tagids) async { - var res = await Request().post(Api.addUsers, queryParameters: { - 'fids': fids, - 'tagids': tagids ?? '0', - 'csrf': await Request.getCsrf(), - }, data: { - 'cross_domain': true - }); + var res = await Request().post( + Api.addUsers, + data: { + 'fids': fids, + 'tagids': tagids ?? '0', + 'csrf': await Request.getCsrf(), + }, + queryParameters: {'cross_domain': true}, + ); if (res.data['code'] == 0) { return {'status': true, 'data': [], 'msg': '操作成功'}; } else { @@ -422,11 +424,14 @@ class MemberHttp { static Future cookieToKey() async { var authCodeRes = await getTVCode(); if (authCodeRes['status']) { - var res = await Request().post(Api.cookieToKey, queryParameters: { - 'auth_code': authCodeRes['data'], - 'build': 708200, - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.cookieToKey, + data: { + 'auth_code': authCodeRes['data'], + 'build': 708200, + 'csrf': await Request.getCsrf(), + }, + ); await Future.delayed(const Duration(milliseconds: 300)); await qrcodePoll(authCodeRes['data']); if (res.data['code'] == 0) { diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 869b5a28..6426a6f2 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,5 @@ import 'dart:convert'; import 'dart:math'; -import 'package:dio/dio.dart'; import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; import 'package:pilipala/models/msg/system.dart'; @@ -158,9 +157,6 @@ class MsgHttp { 'csrf_token': csrf, 'csrf': csrf, }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - ), ); if (res.data['code'] == 0) { return { diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 880f9072..fc00f06b 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -78,7 +78,7 @@ class ReplyHttp { }) async { var res = await Request().post( Api.likeReply, - queryParameters: { + data: { 'type': type, 'oid': oid, 'rpid': rpid, diff --git a/lib/http/user.dart b/lib/http/user.dart index f4535905..26b79523 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -153,7 +153,7 @@ class UserHttp { // 暂停switchStatus传true 否则false var res = await Request().post( Api.pauseHistory, - queryParameters: { + data: { 'switch': switchStatus, 'jsonp': 'jsonp', 'csrf': await Request.getCsrf(), @@ -172,7 +172,7 @@ class UserHttp { static Future clearHistory() async { var res = await Request().post( Api.clearHistory, - queryParameters: { + data: { 'jsonp': 'jsonp', 'csrf': await Request.getCsrf(), }, @@ -190,7 +190,7 @@ class UserHttp { } var res = await Request().post( Api.toViewLater, - queryParameters: data, + data: data, ); if (res.data['code'] == 0) { return {'status': true, 'msg': 'yeah!稍后再看'}; @@ -209,7 +209,7 @@ class UserHttp { params[aid != null ? 'aid' : 'viewed'] = aid ?? true; var res = await Request().post( Api.toViewDel, - queryParameters: params, + data: params, ); if (res.data['code'] == 0) { return {'status': true, 'msg': 'yeah!成功移除'}; @@ -241,7 +241,7 @@ class UserHttp { static Future toViewClear() async { var res = await Request().post( Api.toViewClear, - queryParameters: { + data: { 'jsonp': 'jsonp', 'csrf': await Request.getCsrf(), }, @@ -257,7 +257,7 @@ class UserHttp { static Future delHistory(kid) async { var res = await Request().post( Api.delHistory, - queryParameters: { + data: { 'kid': kid, 'jsonp': 'jsonp', 'csrf': await Request.getCsrf(), @@ -406,7 +406,7 @@ class UserHttp { static Future cancelSub({required int seasonId}) async { var res = await Request().post( Api.cancelSub, - queryParameters: { + data: { 'platform': 'web', 'season_id': seasonId, 'csrf': await Request.getCsrf(), @@ -423,7 +423,7 @@ class UserHttp { static Future delFavFolder({required int mediaIds}) async { var res = await Request().post( Api.delFavFolder, - queryParameters: { + data: { 'media_ids': mediaIds, 'platform': 'web', 'csrf': await Request.getCsrf(), diff --git a/lib/http/video.dart b/lib/http/video.dart index 160f5db2..95ea6782 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -243,7 +243,7 @@ class VideoHttp { static Future coinVideo({required String bvid, required int multiply}) async { var res = await Request().post( Api.coinVideo, - queryParameters: { + data: { 'bvid': bvid, 'multiply': multiply, 'select_like': 0, @@ -271,7 +271,7 @@ class VideoHttp { static Future oneThree({required String bvid}) async { var res = await Request().post( Api.oneThree, - queryParameters: { + data: { 'bvid': bvid, 'csrf': await Request.getCsrf(), }, @@ -287,7 +287,7 @@ class VideoHttp { static Future likeVideo({required String bvid, required bool type}) async { var res = await Request().post( Api.likeVideo, - queryParameters: { + data: { 'bvid': bvid, 'like': type ? 1 : 2, 'csrf': await Request.getCsrf(), @@ -303,13 +303,16 @@ class VideoHttp { // (取消)收藏 static Future favVideo( {required int aid, String? addIds, String? delIds}) async { - var res = await Request().post(Api.favVideo, queryParameters: { - 'rid': aid, - 'type': 2, - 'add_media_ids': addIds ?? '', - 'del_media_ids': delIds ?? '', - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.favVideo, + data: { + 'rid': aid, + 'type': 2, + 'add_media_ids': addIds ?? '', + 'del_media_ids': delIds ?? '', + 'csrf': await Request.getCsrf(), + }, + ); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { @@ -347,14 +350,17 @@ class VideoHttp { if (message == '') { return {'status': false, 'data': [], 'msg': '请输入评论内容'}; } - var res = await Request().post(Api.replyAdd, queryParameters: { - 'type': type.index, - 'oid': oid, - 'root': root == null || root == 0 ? '' : root, - 'parent': parent == null || parent == 0 ? '' : parent, - 'message': message, - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.replyAdd, + data: { + 'type': type.index, + 'oid': oid, + 'root': root == null || root == 0 ? '' : root, + 'parent': parent == null || parent == 0 ? '' : parent, + 'message': message, + 'csrf': await Request.getCsrf(), + }, + ); log(res.toString()); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; @@ -376,12 +382,15 @@ class VideoHttp { // 操作用户关系 static Future relationMod( {required int mid, required int act, required int reSrc}) async { - var res = await Request().post(Api.relationMod, queryParameters: { - 'fid': mid, - 'act': act, - 're_src': reSrc, - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.relationMod, + data: { + 'fid': mid, + 'act': act, + 're_src': reSrc, + 'csrf': await Request.getCsrf(), + }, + ); if (res.data['code'] == 0) { if (act == 5) { List blackMidsList = @@ -397,27 +406,33 @@ class VideoHttp { // 视频播放进度 static Future heartBeat({bvid, cid, progress, realtime}) async { - await Request().post(Api.heartBeat, queryParameters: { - // 'aid': aid, - 'bvid': bvid, - 'cid': cid, - // 'epid': '', - // 'sid': '', - // 'mid': '', - 'played_time': progress, - // 'realtime': realtime, - // 'type': '', - // 'sub_type': '', - 'csrf': await Request.getCsrf(), - }); + await Request().post( + Api.heartBeat, + data: { + // 'aid': aid, + 'bvid': bvid, + 'cid': cid, + // 'epid': '', + // 'sid': '', + // 'mid': '', + 'played_time': progress, + // 'realtime': realtime, + // 'type': '', + // 'sub_type': '', + 'csrf': await Request.getCsrf(), + }, + ); } // 添加追番 static Future bangumiAdd({int? seasonId}) async { - var res = await Request().post(Api.bangumiAdd, queryParameters: { - 'season_id': seasonId, - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.bangumiAdd, + data: { + 'season_id': seasonId, + 'csrf': await Request.getCsrf(), + }, + ); if (res.data['code'] == 0) { return {'status': true, 'msg': res.data['result']['toast']}; } else { @@ -427,10 +442,13 @@ class VideoHttp { // 取消追番 static Future bangumiDel({int? seasonId}) async { - var res = await Request().post(Api.bangumiDel, queryParameters: { - 'season_id': seasonId, - 'csrf': await Request.getCsrf(), - }); + var res = await Request().post( + Api.bangumiDel, + data: { + 'season_id': seasonId, + 'csrf': await Request.getCsrf(), + }, + ); if (res.data['code'] == 0) { return {'status': true, 'msg': res.data['result']['toast']}; } else { From f1441ac97e19e8c4b52b079fd8275e005985564d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 4 Oct 2024 11:36:17 +0800 Subject: [PATCH 287/438] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/view.dart | 108 +++++++++++++++------------ lib/pages/fav_detail/controller.dart | 8 +- lib/pages/fav_detail/view.dart | 26 ++++--- lib/pages/fav_edit/controller.dart | 2 +- 4 files changed, 82 insertions(+), 62 deletions(-) diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index f6164609..317ba4d5 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -79,56 +79,68 @@ class _FavPageState extends State { const SizedBox(width: 14), ], ), - body: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - return Obx( - () => ListView.builder( - controller: scrollController, - itemCount: _favController.favFolderList.length, - itemBuilder: (context, index) { - return FavItem( - favFolderItem: _favController.favFolderList[index], - isOwner: _favController.isOwner.value, - ); - }, - ), - ); - } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data?['msg'] ?? '请求异常', - btnText: data?['code'] == -101 ? '去登录' : null, - fn: () { - if (data?['code'] == -101) { - RoutePush.loginRedirectPush(); - } else { - setState(() { - _futureBuilderFuture = - _favController.queryFavFolder(); - }); - } - }, - ), - ], - ); - } - } else { - // 骨架屏 - return ListView.builder( - itemBuilder: (context, index) { - return const VideoCardHSkeleton(); - }, - itemCount: 10, - ); - } + body: RefreshIndicator( + onRefresh: () async { + _favController.hasMore.value = true; + _favController.currentPage = 1; + setState(() { + _futureBuilderFuture = _favController.queryFavFolder(type: 'init'); + }); }, + child: _buildBody(), ), ); } + + Widget _buildBody() { + return FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + return Obx( + () => ListView.builder( + controller: scrollController, + itemCount: _favController.favFolderList.length, + itemBuilder: (context, index) { + return FavItem( + favFolderItem: _favController.favFolderList[index], + isOwner: _favController.isOwner.value, + ); + }, + ), + ); + } else { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = _favController.queryFavFolder(); + }); + } + }, + ), + ], + ); + } + } else { + // 骨架屏 + return ListView.builder( + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + itemCount: 10, + ); + } + }, + ); + } } diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 4d639bff..ba722481 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -10,6 +10,7 @@ import 'package:pilipala/utils/utils.dart'; class FavDetailController extends GetxController { FavFolderItemData? item; + RxString title = ''.obs; int? mediaId; late String heroTag; @@ -24,6 +25,7 @@ class FavDetailController extends GetxController { @override void onInit() { item = Get.arguments; + title.value = item!.title!; if (Get.parameters.keys.isNotEmpty) { mediaId = int.parse(Get.parameters['mediaId']!); heroTag = Get.parameters['heroTag']!; @@ -117,16 +119,18 @@ class FavDetailController extends GetxController { } onEditFavFolder() async { - Get.toNamed( + var res = await Get.toNamed( '/favEdit', arguments: { 'mediaId': mediaId.toString(), 'title': item!.title, 'intro': item!.intro, 'cover': item!.cover, - 'privacy': item!.attr, + 'privacy': [23, 1].contains(item!.attr) ? 1 : 0, }, ); + title.value = res['title']; + print(title); } Future toViewPlayAll() async { diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 8f0d3cd1..d4c10d31 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -80,9 +80,11 @@ class _FavDetailPageState extends State { Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - _favDetailController.item!.title!, - style: Theme.of(context).textTheme.titleMedium, + Obx( + () => Text( + _favDetailController.title.value, + style: Theme.of(context).textTheme.titleMedium, + ), ), Text( '共${_favDetailController.mediaCount}条视频', @@ -156,14 +158,16 @@ class _FavDetailPageState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), - Text( - _favDetailController.item!.title!, - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .titleMedium! - .fontSize, - fontWeight: FontWeight.bold), + Obx( + () => Text( + _favDetailController.title.value, + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + fontWeight: FontWeight.bold), + ), ), const SizedBox(height: 4), Text( diff --git a/lib/pages/fav_edit/controller.dart b/lib/pages/fav_edit/controller.dart index 4772caee..bf310389 100644 --- a/lib/pages/fav_edit/controller.dart +++ b/lib/pages/fav_edit/controller.dart @@ -56,7 +56,7 @@ class FavEditController extends GetxController { ); if (res['status']) { SmartDialog.showToast('编辑成功'); - Get.back(); + Get.back(result: {'title': title}); } else { SmartDialog.showToast(res['msg']); } From 722ba06787d9013dfdb6c70a267d101649ab7d00 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 4 Oct 2024 23:07:34 +0800 Subject: [PATCH 288/438] =?UTF-8?q?feat:=20=E4=B8=93=E6=A0=8F=E5=88=86?= =?UTF-8?q?=E7=B1=BB=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/search_type.dart | 20 +- lib/pages/search_panel/controller.dart | 4 +- lib/pages/search_panel/view.dart | 13 +- .../search_panel/widgets/article_panel.dart | 317 +++++++++++++----- 4 files changed, 263 insertions(+), 91 deletions(-) diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart index d7d13aec..843e7954 100644 --- a/lib/models/common/search_type.dart +++ b/lib/models/common/search_type.dart @@ -28,7 +28,7 @@ extension SearchTypeExtension on SearchType { String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index]; } -// 搜索类型为视频、专栏及相簿时 +// 搜索类型为视频时 enum ArchiveFilterType { totalrank, click, @@ -44,3 +44,21 @@ extension ArchiveFilterTypeExtension on ArchiveFilterType { String get description => ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index]; } + +// 搜索类型为专栏时 +enum ArticleFilterType { + // 综合排序 + totalrank, + // 最新发布 + pubdate, + // 最多点击 + click, + // 最多喜欢 + attention, + // 最多评论 + scores, +} + +extension ArticleFilterTypeExtension on ArticleFilterType { + String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index]; +} diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index dc0b2bac..2d1aa228 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -24,7 +24,9 @@ class SearchPanelController extends GetxController { searchType: searchType!, keyword: keyword!, page: page.value, - order: searchType!.type != 'video' ? null : order.value, + order: !['video', 'article'].contains(searchType!.type) + ? null + : (order.value == '' ? null : order.value), duration: searchType!.type != 'video' ? null : duration.value, tids: searchType!.type != 'video' ? null : tids.value, ); diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index c5824d70..fa669489 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_use_of_protected_member + import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -45,6 +47,11 @@ class _SearchPanelState extends State ), tag: widget.searchType!.type + widget.keyword!, ); + + /// 专栏默认排序 + if (widget.searchType == SearchType.article) { + _searchPanelController.order.value = 'totalrank'; + } scrollController = _searchPanelController.scrollController; scrollController.addListener(() async { if (scrollController.position.pixels >= @@ -84,7 +91,6 @@ class _SearchPanelState extends State case SearchType.video: return SearchVideoPanel( ctr: _searchPanelController, - // ignore: invalid_use_of_protected_member list: list.value, ); case SearchType.media_bangumi: @@ -94,7 +100,10 @@ class _SearchPanelState extends State case SearchType.live_room: return searchLivePanel(context, ctr, list); case SearchType.article: - return searchArticlePanel(context, ctr, list); + return SearchArticlePanel( + ctr: _searchPanelController, + list: list.value, + ); default: return const SizedBox(); } diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index dd53de66..be08ed56 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -1,106 +1,249 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/search_panel/index.dart'; import 'package:pilipala/utils/utils.dart'; +class SearchArticlePanel extends StatelessWidget { + SearchArticlePanel({ + required this.ctr, + this.list, + Key? key, + }) : super(key: key); + + final SearchPanelController ctr; + final List? list; + + final ArticlePanelController controller = Get.put(ArticlePanelController()); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.topCenter, + children: [ + searchArticlePanel(context, ctr, list), + Container( + width: double.infinity, + height: 36, + padding: const EdgeInsets.only(left: 8, top: 0, right: 8), + child: Row( + children: [ + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Obx( + () => Wrap( + // spacing: , + children: [ + for (var i in controller.filterList) ...[ + CustomFilterChip( + label: i['label'], + type: i['type'], + selectedType: controller.selectedType.value, + callFn: (bool selected) async { + controller.selectedType.value = i['type']; + ctr.order.value = + i['type'].toString().split('.').last; + SmartDialog.showLoading(msg: 'loading'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + ), + ] + ], + ), + ), + ), + ), + ], + ), + ), + ], + ); + } +} + Widget searchArticlePanel(BuildContext context, ctr, list) { TextStyle textStyle = TextStyle( fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, color: Theme.of(context).colorScheme.outline); - return ListView.builder( - controller: ctr!.scrollController, - itemCount: list.length, - itemBuilder: (context, index) { - return InkWell( - onTap: () { - Get.toNamed('/read', parameters: { - 'title': list[index].subTitle, - 'id': list[index].id.toString(), - 'articleType': 'read' - }); - }, - child: Padding( - padding: const EdgeInsets.fromLTRB( - StyleString.safeSpace, 5, StyleString.safeSpace, 5), - child: LayoutBuilder(builder: (context, boxConstraints) { - final double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(1.0)) / - 2; - return Container( - constraints: const BoxConstraints(minHeight: 88), - height: width / StyleString.aspectRatio, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (list[index].imageUrls != null && - list[index].imageUrls.isNotEmpty) - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return NetworkImgLayer( - width: maxWidth, - height: maxHeight, - src: list[index].imageUrls.first, - ); - }), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), - child: Column( - mainAxisSize: MainAxisSize.min, + return Padding( + padding: const EdgeInsets.only(top: 36), + child: list!.isNotEmpty + ? ListView.builder( + controller: ctr!.scrollController, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: list.length, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + Get.toNamed('/read', parameters: { + 'title': list[index].subTitle, + 'id': list[index].id.toString(), + 'articleType': 'read' + }); + }, + child: Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder(builder: (context, boxConstraints) { + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; + return Container( + constraints: const BoxConstraints(minHeight: 88), + height: width / StyleString.aspectRatio, + child: Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - maxLines: 2, - text: TextSpan( - children: [ - for (var i in list[index].title) ...[ - TextSpan( - text: i['text'], - style: TextStyle( - fontWeight: FontWeight.w500, - letterSpacing: 0.3, - color: i['type'] == 'em' - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) - .colorScheme - .onSurface, + children: [ + if (list[index].imageUrls != null && + list[index].imageUrls.isNotEmpty) + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return NetworkImgLayer( + width: maxWidth, + height: maxHeight, + src: list[index].imageUrls.first, + ); + }), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + maxLines: 2, + text: TextSpan( + children: [ + for (var i in list[index].title) ...[ + TextSpan( + text: i['text'], + style: TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + color: i['type'] == 'em' + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .onSurface, + ), + ), + ] + ], ), ), - ] - ], + const Spacer(), + Text( + Utils.dateFormat(list[index].pubTime, + formatType: 'detail'), + style: textStyle), + Row( + children: [ + Text('${list[index].view}浏览', + style: textStyle), + Text(' • ', style: textStyle), + Text('${list[index].reply}评论', + style: textStyle), + ], + ), + ], + ), ), ), - const Spacer(), - Text( - Utils.dateFormat(list[index].pubTime, - formatType: 'detail'), - style: textStyle), - Row( - children: [ - Text('${list[index].view}浏览', style: textStyle), - Text(' • ', style: textStyle), - Text('${list[index].reply}评论', style: textStyle), - ], - ), ], ), - ), - ), - ], - ), - ); - }), - ), - ); - }, + ); + }), + ), + ); + }, + ) + : CustomScrollView( + slivers: [ + HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + ) + ], + ), ); } + +class CustomFilterChip extends StatelessWidget { + const CustomFilterChip({ + this.label, + this.type, + this.selectedType, + this.callFn, + Key? key, + }) : super(key: key); + + final String? label; + final ArticleFilterType? type; + final ArticleFilterType? selectedType; + final Function? callFn; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 34, + child: FilterChip( + padding: const EdgeInsets.only(left: 11, right: 11), + labelPadding: EdgeInsets.zero, + label: Text( + label!, + style: const TextStyle(fontSize: 13), + ), + labelStyle: TextStyle( + color: type == selectedType + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline), + selected: type == selectedType, + showCheckmark: false, + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + selectedColor: Colors.transparent, + // backgroundColor: + // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + backgroundColor: Colors.transparent, + side: BorderSide.none, + onSelected: (bool selected) => callFn!(selected), + ), + ); + } +} + +class ArticlePanelController extends GetxController { + RxList filterList = [{}].obs; + Rx selectedType = ArticleFilterType.values.first.obs; + + @override + void onInit() { + List> list = ArticleFilterType.values + .map((type) => { + 'label': type.description, + 'type': type, + }) + .toList(); + filterList.value = list; + super.onInit(); + } +} From c08c3f4c7c61933e7b2157ffccc23e02e0f9d805 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 5 Oct 2024 11:39:09 +0800 Subject: [PATCH 289/438] mod --- lib/pages/mine/view.dart | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 4fcfdcf4..5bf7a536 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -177,12 +177,18 @@ class _MinePageState extends State ), const SizedBox(height: 2), ], - Text( - '硬币: ${userInfo.money ?? '-'}', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, - ), - ), + Text.rich( + TextSpan(children: [ + TextSpan( + text: '硬币: ', + style: TextStyle( + color: Theme.of(context).colorScheme.outline)), + TextSpan( + text: (userInfo.money ?? '-').toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.primary)), + ]), + ) ], ), const Spacer(), From 00423be5a951a3c88f2d1331cf7c12c07f48f1ff Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 5 Oct 2024 14:18:24 +0800 Subject: [PATCH 290/438] =?UTF-8?q?mod:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 34 +++- lib/pages/video/detail/introduction/view.dart | 169 +++--------------- 2 files changed, 52 insertions(+), 151 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index c0f6eebb..75e08145 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; @@ -154,11 +153,10 @@ class VideoIntroController extends GetxController { } if (hasLike.value && hasCoin.value && hasFav.value) { // 已点赞、投币、收藏 - SmartDialog.showToast('🙏 UP已经收到了~'); + SmartDialog.showToast('UP已经收到了~'); return false; } var result = await VideoHttp.oneThree(bvid: bvid); - print('🤣🦴:${result["data"]}'); if (result['status']) { hasLike.value = result["data"]["like"]; hasCoin.value = result["data"]["coin"]; @@ -604,4 +602,34 @@ class VideoIntroController extends GetxController { ).buildShowContent(Get.context!), ); } + + // + oneThreeDialog() { + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('是否一键三连'), + actions: [ + TextButton( + onPressed: () => navigator!.pop(), + child: Text( + '取消', + style: TextStyle( + color: Theme.of(Get.context!).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + actionOneThree(); + navigator!.pop(); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 04cf92fe..31edab2f 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,7 +1,4 @@ -import 'dart:ffi'; - import 'package:bottom_sheet/bottom_sheet.dart'; -import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -151,11 +148,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - // 一键三连动画 - late AnimationController _controller; - late Animation _scaleTransition; - final RxDouble _progress = 0.0.obs; - void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -178,26 +170,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { owner = widget.videoDetail!.owner; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); - - /// 一键三连动画 - _controller = AnimationController( - duration: const Duration(milliseconds: 1500), - reverseDuration: const Duration(milliseconds: 300), - vsync: this, - ); - _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) - ..addListener(() async { - _progress.value = - double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3)); - if (_progress.value == 1) { - if (_controller.status == AnimationStatus.completed) { - await videoIntroController.actionOneThree(); - } - _progress.value = 0; - _scaleTransition.removeListener(() {}); - _controller.stop(); - } - }); } // 收藏 @@ -279,8 +251,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); - _controller.dispose(); - _scaleTransition.removeListener(() {}); super.dispose(); } @@ -573,131 +543,34 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget actionGrid(BuildContext context, videoIntroController) { final actionTypeSort = GlobalDataCache().actionTypeSort; - Widget progressWidget(progress) { - return SizedBox( - width: const IconThemeData.fallback().size! + 5, - height: const IconThemeData.fallback().size! + 5, - child: CircularProgressIndicator( - value: progress.value, - strokeWidth: 2, - ), - ); - } - Map menuListWidgets = { 'like': Obx( - () { - bool likeStatus = videoIntroController.hasLike.value; - ColorScheme colorScheme = Theme.of(context).colorScheme; - return Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - InkWell( - onTapDown: (details) { - feedBack(); - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } - _controller.forward(); - }, - onTapUp: (TapUpDetails details) { - if (_progress.value == 0) { - feedBack(); - EasyThrottle.throttle( - 'my-throttler', const Duration(milliseconds: 200), () { - videoIntroController.actionLikeVideo(); - }); - } - _controller.reverse(); - }, - onTapCancel: () { - _controller.reverse(); - }, - borderRadius: StyleString.mdRadius, - child: SizedBox( - width: (Get.size.width - 24) / 5, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Icon( - key: ValueKey(likeStatus), - likeStatus - ? FontAwesomeIcons.solidThumbsUp - : FontAwesomeIcons.thumbsUp, - color: likeStatus - ? colorScheme.primary - : colorScheme.outline, - size: 21, - ), - ), - const SizedBox(height: 6), - Text( - widget.videoDetail!.stat!.like!.toString(), - style: TextStyle( - color: likeStatus ? colorScheme.primary : null, - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - ), - ) - ], - ), - ), - ), - ], - ); - }, + () => ActionItem( + icon: const Icon(FontAwesomeIcons.thumbsUp), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + onTap: handleState(videoIntroController.actionLikeVideo), + onLongPress: () => videoIntroController.oneThreeDialog(), + selectStatus: videoIntroController.hasLike.value, + text: widget.videoDetail!.stat!.like!.toString(), + ), ), 'coin': Obx( - () => Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ], + () => ActionItem( + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), ), ), 'collect': Obx( - () => Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ], + () => ActionItem( + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), ), ), 'watchLater': ActionItem( From 2dca3759011beed7e4e145b285458295b05b4fb5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 6 Oct 2024 16:38:50 +0800 Subject: [PATCH 291/438] mod --- lib/pages/video/detail/view.dart | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 5476adc9..d155509f 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -68,10 +68,6 @@ class _VideoDetailPageState extends State late final AppLifecycleListener _lifecycleListener; late double statusHeight; - // 稍后再看控制器 - // late AnimationController _laterCtr; - // late Animation _laterOffsetAni; - @override void initState() { super.initState(); @@ -108,7 +104,6 @@ class _VideoDetailPageState extends State } WidgetsBinding.instance.addObserver(this); lifecycleListener(); - // watchLaterControllerInit(); } // 获取视频资源,初始化播放器 @@ -242,8 +237,6 @@ class _VideoDetailPageState extends State appbarStream.close(); WidgetsBinding.instance.removeObserver(this); _lifecycleListener.dispose(); - // _laterCtr.dispose(); - // _laterOffsetAni.removeListener(() {}); super.dispose(); } @@ -490,21 +483,6 @@ class _VideoDetailPageState extends State ); } - /// 稍后再看控制器初始化 - // void watchLaterControllerInit() { - // _laterCtr = AnimationController( - // duration: const Duration(milliseconds: 300), - // vsync: this, - // ); - // _laterOffsetAni = Tween( - // begin: const Offset(0.0, 1.0), - // end: Offset.zero, - // ).animate(CurvedAnimation( - // parent: _laterCtr, - // curve: Curves.easeInOut, - // )); - // } - @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); From fa53c1214cc198ade7dd0b155949bde38ee2113f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 6 Oct 2024 16:53:06 +0800 Subject: [PATCH 292/438] =?UTF-8?q?mod:=20=E8=BF=94=E5=9B=9E=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=97=B6stream=E9=87=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index d155509f..7693dee0 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -290,6 +290,7 @@ class _VideoDetailPageState extends State plPlayerController?.play(); } plPlayerController?.addStatusLister(playerListener); + appbarStream.add(0); super.didPopNext(); } From 676b2f18eb309f8153a8038828d9e3045387fd16 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 6 Oct 2024 21:11:00 +0800 Subject: [PATCH 293/438] mod --- lib/pages/mine/view.dart | 212 ++++++++++++++++++--------------------- 1 file changed, 98 insertions(+), 114 deletions(-) diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 5bf7a536..b96c095a 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -44,6 +44,8 @@ class _MinePageState extends State super.build(context); return Scaffold( appBar: AppBar( + scrolledUnderElevation: 0, + elevation: 0, actions: [ IconButton( icon: const Icon(Icons.search_outlined), @@ -64,59 +66,71 @@ class _MinePageState extends State const SizedBox(width: 22), ], ), - body: SingleChildScrollView( - child: Column( - children: [ - Obx(() => _buildProfileSection(context, ctr.userInfo.value)), - const SizedBox(height: 10), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - return Obx( - () => _buildStatsSection( - context, - ctr.userStat.value, + body: RefreshIndicator( + onRefresh: () async { + await ctr.queryUserInfo(); + }, + child: SingleChildScrollView( + physics: const AlwaysScrollableScrollPhysics( + parent: BouncingScrollPhysics()), + child: Padding( + padding: const EdgeInsets.only(bottom: 110), + child: Expanded( + child: Column( + children: [ + Obx(() => _buildProfileSection(context, ctr.userInfo.value)), + const SizedBox(height: 10), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + return Obx( + () => _buildStatsSection( + context, + ctr.userStat.value, + ), + ); + } else { + return _buildStatsSection( + context, + ctr.userStat.value, + ); + } + } else { + return _buildStatsSection( + context, + ctr.userStat.value, + ); + } + }, + ), + _buildMenuSection(context), + Obx( + () => Visibility( + visible: ctr.userLogin.value, + child: Divider( + height: 25, + color: Theme.of(context).dividerColor.withOpacity(0.1), ), - ); - } else { - return _buildStatsSection( - context, - ctr.userStat.value, - ); - } - } else { - return _buildStatsSection( - context, - ctr.userStat.value, - ); - } - }, - ), - _buildMenuSection(context), - Obx( - () => Visibility( - visible: ctr.userLogin.value, - child: Divider( - height: 25, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), + ), + ), + Obx( + () => ctr.userLogin.value + ? _buildFavoritesSection(context) + : const SizedBox(), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + + kBottomNavigationBarHeight, + ) + ], ), ), - Obx( - () => ctr.userLogin.value - ? _buildFavoritesSection(context) - : const SizedBox(), - ), - SizedBox( - height: MediaQuery.of(context).padding.bottom + - kBottomNavigationBarHeight, - ) - ], + ), ), ), ); @@ -326,7 +340,7 @@ class _MinePageState extends State const SizedBox(height: 4), SizedBox( width: double.infinity, - height: MediaQuery.textScalerOf(context).scale(110), + height: MediaQuery.textScalerOf(context).scale(180), child: FutureBuilder( future: ctr.queryFavFolder(), builder: (context, snapshot) { @@ -459,71 +473,41 @@ class FavFolderItem extends StatelessWidget { String heroTag = Utils.makeHeroTag(item!.fid); return Container( margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), - child: InkWell( - onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { - 'mediaId': item!.id.toString(), - 'heroTag': heroTag, - 'isOwner': '1', - }), - borderRadius: StyleString.mdRadius, - child: Stack( - children: [ - NetworkImgLayer( - src: item!.cover, - width: 175, - height: 110, - ), - // 渐变 - Positioned( - left: 0, - right: 0, - top: 60, - bottom: 0, - child: Container( - padding: const EdgeInsets.only(bottom: 8, left: 10, right: 2), - decoration: BoxDecoration( - borderRadius: StyleString.mdRadius, - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.black.withOpacity(0), - Colors.black.withOpacity(0.6), - ], - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.end, - children: [ - RichText( - maxLines: 1, - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: Theme.of(context) - .textTheme - .titleSmall! - .copyWith(color: Colors.white), - children: [ - TextSpan(text: item!.title!), - const TextSpan(text: ' '), - if (item!.mediaCount! > 0) - TextSpan( - text: item!.mediaCount!.toString(), - style: const TextStyle( - fontSize: 11, - fontWeight: FontWeight.bold, - ), - ), - ], - ), - ), - ], - ), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InkWell( + onTap: () => + Get.toNamed('/favDetail', arguments: item, parameters: { + 'mediaId': item!.id.toString(), + 'heroTag': heroTag, + 'isOwner': '1', + }), + borderRadius: StyleString.mdRadius, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + src: item!.cover, + width: 180, + height: 110, ), ), - ], - ), + ), + const SizedBox(height: 8), + Text( + ' ${item!.title}', + overflow: TextOverflow.fade, + maxLines: 1, + ), + Text( + ' 共${item!.mediaCount}条视频', + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline), + ) + ], ), ); } From 64c2bcc6502e8b7117e9bee5aea040bab2d1db84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 17:55:39 +0800 Subject: [PATCH 294/438] =?UTF-8?q?opt:=20=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/controller.dart | 5 +++- lib/pages/main/view.dart | 44 ++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index ad2a7781..fd18d728 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -13,6 +13,7 @@ import '../../models/common/nav_bar_config.dart'; class MainController extends GetxController { List pages = []; + List pagesIds = []; RxList navigationBars = [].obs; late List defaultNavTabs; late List navBarSort; @@ -43,7 +44,8 @@ class MainController extends GetxController { SettingBoxKey.dynamicBadgeMode, defaultValue: DynamicBadgeMode.number.code)]; setNavBarConfig(); - if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { + if (dynamicBadgeType.value != DynamicBadgeMode.hidden && + pagesIds.contains(2)) { getUnreadDynamic(); } enableGradientBg = @@ -104,6 +106,7 @@ class MainController extends GetxController { // 如果找不到匹配项,默认索引设置为0或其他合适的值 selectedIndex = defaultIndex != -1 ? defaultIndex : 0; pages = navigationBars.map((e) => e['page']).toList(); + pagesIds = navigationBars.map((e) => e['id']).toList(); } @override diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 3da667e8..4399bf30 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -22,10 +22,10 @@ class MainApp extends StatefulWidget { class _MainAppState extends State with SingleTickerProviderStateMixin { final MainController _mainController = Get.put(MainController()); - final HomeController _homeController = Get.put(HomeController()); - final RankController _rankController = Get.put(RankController()); - final DynamicsController _dynamicController = Get.put(DynamicsController()); - final MediaController _mediaController = Get.put(MediaController()); + late HomeController _homeController; + RankController? _rankController; + DynamicsController? _dynamicController; + MediaController? _mediaController; int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; @@ -38,6 +38,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _mainController.pageController = PageController(initialPage: _mainController.selectedIndex); enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); + controllerInit(); } void setIndex(int value) async { @@ -60,38 +61,51 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { } if (currentPage is RankPage) { - if (_rankController.flag) { + if (_rankController!.flag) { // 单击返回顶部 双击并刷新 if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) { - _rankController.onRefresh(); + _rankController!.onRefresh(); } else { - _rankController.animateToTop(); + _rankController!.animateToTop(); } _lastSelectTime = DateTime.now().millisecondsSinceEpoch; } - _rankController.flag = true; + _rankController!.flag = true; } else { - _rankController.flag = false; + _rankController?.flag = false; } if (currentPage is DynamicsPage) { - if (_dynamicController.flag) { + if (_dynamicController!.flag) { // 单击返回顶部 双击并刷新 if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) { - _dynamicController.onRefresh(); + _dynamicController!.onRefresh(); } else { - _dynamicController.animateToTop(); + _dynamicController!.animateToTop(); } _lastSelectTime = DateTime.now().millisecondsSinceEpoch; } - _dynamicController.flag = true; + _dynamicController!.flag = true; _mainController.clearUnread(); } else { - _dynamicController.flag = false; + _dynamicController?.flag = false; } if (currentPage is MediaPage) { - _mediaController.queryFavFolder(); + _mediaController!.queryFavFolder(); + } + } + + void controllerInit() { + _homeController = Get.put(HomeController()); + if (_mainController.pagesIds.contains(1)) { + _rankController = Get.put(RankController()); + } + if (_mainController.pagesIds.contains(2)) { + _dynamicController = Get.put(DynamicsController()); + } + if (_mainController.pagesIds.contains(3)) { + _mediaController = Get.put(MediaController()); } } From e94f0081683344d0b22e8636e8adde141f400086 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 21:46:00 +0800 Subject: [PATCH 295/438] =?UTF-8?q?opt:=20appScheme=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 22184139..f26eff77 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -175,6 +175,11 @@ class PiliSchame { if (lastPathSegment.contains('ep')) { RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first); } + } else if (path.startsWith('/BV')) { + final String bvid = path.split('?').first.split('/').last; + _videoPush(null, bvid); + } else if (path.startsWith('/av')) { + _videoPush(Utils.matchNum(path.split('?').first).first, null); } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); From 16847b8a92c3d41bd727439989b2fe62a2ef73ed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 22:39:48 +0800 Subject: [PATCH 296/438] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BA=E8=B7=B3?= =?UTF-8?q?=E8=BD=ACcv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/reply/widgets/reply_item.dart | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 26dc2e5a..2ba97a20 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -727,14 +727,11 @@ InlineSpan buildContent( '', ); } else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) { - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://www.bilibili.com/read/$matchStr', - 'type': 'url', - 'pageTitle': title - }, - ); + Get.toNamed('/read', parameters: { + 'title': title, + 'id': Utils.matchNum(matchStr).first.toString(), + 'articleType': 'read', + }); } else { Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( From a366f5d3ce06552632a28f3a4f296a7a43039793 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 22:51:02 +0800 Subject: [PATCH 297/438] =?UTF-8?q?opt:=20=E4=B8=93=E6=A0=8F=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E5=8F=AF=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/opus/view.dart | 2 +- lib/pages/read/controller.dart | 2 +- lib/pages/read/view.dart | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/pages/opus/view.dart b/lib/pages/opus/view.dart index 434a9405..42c0c419 100644 --- a/lib/pages/opus/view.dart +++ b/lib/pages/opus/view.dart @@ -157,7 +157,7 @@ class _OpusPageState extends State { Container( alignment: TextHelper.getAlignment(paragraph.align), margin: const EdgeInsets.only(bottom: 10), - child: Text.rich( + child: SelectableText.rich( TextSpan( children: paragraph.text?.nodes?.map((node) { return TextHelper.buildTextSpan( diff --git a/lib/pages/read/controller.dart b/lib/pages/read/controller.dart index efa43c98..ac81aca8 100644 --- a/lib/pages/read/controller.dart +++ b/lib/pages/read/controller.dart @@ -21,7 +21,7 @@ class ReadPageController extends GetxController { super.onInit(); title.value = Get.parameters['title'] ?? ''; id = Get.parameters['id']!; - articleType = Get.parameters['articleType']!; + articleType = Get.parameters['articleType'] ?? 'read'; url = 'https://www.bilibili.com/read/cv$id'; scrollController.addListener(_scrollListener); fetchViewInfo(); diff --git a/lib/pages/read/view.dart b/lib/pages/read/view.dart index d37eeae5..710934eb 100644 --- a/lib/pages/read/view.dart +++ b/lib/pages/read/view.dart @@ -126,7 +126,6 @@ class _ReadPageState extends State { Widget _buildContent(ReadDataModel cvData) { final List picList = _extractPicList(cvData); final List imgList = extractDataSrc(cvData.readInfo!.content!); - return Padding( padding: EdgeInsets.fromLTRB( 16, 0, 16, MediaQuery.of(context).padding.bottom + 40), @@ -163,9 +162,11 @@ class _ReadPageState extends State { padding: const EdgeInsets.only(bottom: 20), child: _buildAuthorWidget(cvData), ), - HtmlRender( - htmlContent: cvData.readInfo!.content!, - imgList: imgList, + SelectionArea( + child: HtmlRender( + htmlContent: cvData.readInfo!.content!, + imgList: imgList, + ), ), ], ); @@ -206,7 +207,7 @@ class _ReadPageState extends State { return Container( alignment: TextHelper.getAlignment(paragraph.align), margin: const EdgeInsets.only(bottom: 10), - child: Text.rich( + child: SelectableText.rich( TextSpan( children: paragraph.text?.nodes?.map((node) { return TextHelper.buildTextSpan(node, paragraph.align, context); From 9d965aae2f8916ec1db7b15c4124cee30a3393ce Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 23:15:17 +0800 Subject: [PATCH 298/438] =?UTF-8?q?mod:=20=E7=95=AA=E5=89=A7=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E6=AF=94=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/view.dart | 6 +++--- lib/pages/bangumi/view.dart | 8 ++++---- lib/pages/bangumi/widgets/bangumu_card_v.dart | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 94ee24de..188debef 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -189,8 +189,8 @@ class _BangumiInfoState extends State { Stack( children: [ NetworkImgLayer( - width: 105, - height: 160, + width: 115, + height: 115 / 0.75, src: widget.bangumiDetail!.cover!, ), PBadge( @@ -208,7 +208,7 @@ class _BangumiInfoState extends State { child: InkWell( onTap: () => showIntroDetail(), child: SizedBox( - height: 158, + height: 115 / 0.75, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 3adfdc1f..9ec72350 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -96,7 +96,8 @@ class _BangumiPageState extends State ), ), SizedBox( - height: 268, + height: Get.size.width / 3 / 0.75 + + MediaQuery.textScalerOf(context).scale(50.0), child: FutureBuilder( future: _futureBuilderFutureFollow, builder: @@ -117,7 +118,6 @@ class _BangumiPageState extends State itemBuilder: (context, index) { return Container( width: Get.size.width / 3, - height: 254, margin: EdgeInsets.only( left: StyleString.safeSpace, right: index == @@ -208,8 +208,8 @@ class _BangumiPageState extends State crossAxisSpacing: StyleString.cardSpace, // 列数 crossAxisCount: 3, - mainAxisExtent: Get.size.width / 3 / 0.65 + - MediaQuery.textScalerOf(context).scale(32.0), + mainAxisExtent: Get.size.width / 3 / 0.75 + + MediaQuery.textScalerOf(context).scale(42.0), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index a1d7a931..10d95a1c 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -37,7 +37,7 @@ class BangumiCardV extends StatelessWidget { StyleString.imgRadius, ), child: AspectRatio( - aspectRatio: 0.65, + aspectRatio: 0.75, child: LayoutBuilder(builder: (context, boxConstraints) { final double maxWidth = boxConstraints.maxWidth; final double maxHeight = boxConstraints.maxHeight; From 48d869a6c0d2a255691e02efa1d199f86afb3297 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 7 Oct 2024 23:46:55 +0800 Subject: [PATCH 299/438] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E9=A1=B5=E7=8A=B6=E6=80=81=E6=A0=8F=E5=89=8D=E6=99=AF?= =?UTF-8?q?=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7693dee0..50e94cb1 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -594,10 +595,21 @@ class _VideoDetailPageState extends State key: vdCtr.scaffoldKey, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), - child: AppBar( - backgroundColor: Colors.black, - elevation: 0, - scrolledUnderElevation: 0, + child: StreamBuilder( + stream: appbarStream.stream.distinct(), + initialData: 0, + builder: ((context, snapshot) { + return AppBar( + backgroundColor: Colors.black, + elevation: 0, + scrolledUnderElevation: 0, + systemOverlayStyle: Get.isDarkMode + ? SystemUiOverlayStyle.light + : snapshot.data!.toDouble() > kToolbarHeight + ? SystemUiOverlayStyle.dark + : SystemUiOverlayStyle.light, + ); + }), ), ), body: ExtendedNestedScrollView( From 3830380172c26c299b1132a33a0633fb7c3432f6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 8 Oct 2024 23:46:40 +0800 Subject: [PATCH 300/438] =?UTF-8?q?feat:=20=E9=A1=BA=E5=BA=8F=E6=92=AD?= =?UTF-8?q?=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/later.dart | 12 ++++-- .../video/detail/introduction/controller.dart | 40 +++++++++++++------ .../detail/widgets/watch_later_list.dart | 4 +- 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/lib/models/video/later.dart b/lib/models/video/later.dart index f722a231..7829e1c7 100644 --- a/lib/models/video/later.dart +++ b/lib/models/video/later.dart @@ -1,6 +1,7 @@ class MediaVideoItemModel { MediaVideoItemModel({ this.id, + this.aid, this.offset, this.index, this.intro, @@ -14,12 +15,13 @@ class MediaVideoItemModel { this.likeState, this.favState, this.page, + this.cid, this.pages, this.title, this.type, this.upper, this.link, - this.bvId, + this.bvid, this.shortLink, this.rights, this.elecInfo, @@ -32,6 +34,7 @@ class MediaVideoItemModel { }); int? id; + int? aid; int? offset; int? index; String? intro; @@ -45,12 +48,13 @@ class MediaVideoItemModel { int? likeState; int? favState; int? page; + int? cid; List? pages; String? title; int? type; Upper? upper; String? link; - String? bvId; + String? bvid; String? shortLink; Rights? rights; dynamic elecInfo; @@ -64,6 +68,7 @@ class MediaVideoItemModel { factory MediaVideoItemModel.fromJson(Map json) => MediaVideoItemModel( id: json["id"], + aid: json["id"], offset: json["offset"], index: json["index"], intro: json["intro"], @@ -77,6 +82,7 @@ class MediaVideoItemModel { likeState: json["like_state"], favState: json["fav_state"], page: json["page"], + cid: json["pages"] == null ? -1 : json["pages"].first['id'], // json["pages"] 可能为null pages: json["pages"] == null ? [] @@ -85,7 +91,7 @@ class MediaVideoItemModel { type: json["type"], upper: Upper.fromJson(json["upper"]), link: json["link"], - bvId: json["bv_id"], + bvid: json["bv_id"], shortLink: json["short_link"], rights: Rights.fromJson(json["rights"]), elecInfo: json["elec_info"], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 786d8172..83e5b012 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -413,7 +413,12 @@ class VideoIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid, cover) async { + Future changeSeasonOrbangu( + String bvid, + int cid, + int? aid, + String? cover, + ) async { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); @@ -424,13 +429,14 @@ class VideoIntroController extends GetxController { releatedCtr.queryRelatedVideo(); } - videoDetailCtr.bvid = bvid; - videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); - videoDetailCtr.cid.value = cid; - videoDetailCtr.danmakuCid.value = cid; - videoDetailCtr.cover.value = cover; - videoDetailCtr.queryVideoUrl(); - videoDetailCtr.clearSubtitleContent(); + videoDetailCtr + ..bvid = bvid + ..oid.value = aid ?? IdUtils.bv2av(bvid) + ..cid.value = cid + ..danmakuCid.value = cid + ..cover.value = cover ?? '' + ..queryVideoUrl() + ..clearSubtitleContent(); await videoDetailCtr.getSubtitle(); videoDetailCtr.setSubtitleContent(); // 重新请求评论 @@ -480,7 +486,13 @@ class VideoIntroController extends GetxController { final List episodes = []; bool isPages = false; late String cover; - if (videoDetail.value.ugcSeason != null) { + final VideoDetailController videoDetailCtr = + Get.find(tag: heroTag); + + /// 优先稍后再看、收藏夹 + if (videoDetailCtr.isWatchLaterVisible.value) { + episodes.addAll(videoDetailCtr.mediaList); + } else if (videoDetail.value.ugcSeason != null) { final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final List sections = ugcSeason.sections!; for (int i = 0; i < sections.length; i++) { @@ -497,10 +509,15 @@ class VideoIntroController extends GetxController { episodes.indexWhere((e) => e.cid == lastPlayCid.value); int nextIndex = currentIndex + 1; cover = episodes[nextIndex].cover; - final VideoDetailController videoDetailCtr = - Get.find(tag: heroTag); final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + int cid = episodes[nextIndex].cid!; + while (cid == -1) { + nextIndex += 1; + SmartDialog.showToast('当前视频暂不支持播放,自动跳过'); + cid = episodes[nextIndex].cid!; + } + // 列表循环 if (nextIndex >= episodes.length) { if (platRepeat == PlayRepeat.listCycle) { @@ -510,7 +527,6 @@ class VideoIntroController extends GetxController { return; } } - final int cid = episodes[nextIndex].cid!; final String rBvid = isPages ? bvid : episodes[nextIndex].bvid; final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; changeSeasonOrbangu(rBvid, cid, rAid, cover); diff --git a/lib/pages/video/detail/widgets/watch_later_list.dart b/lib/pages/video/detail/widgets/watch_later_list.dart index b367ae40..8e83af4e 100644 --- a/lib/pages/video/detail/widgets/watch_later_list.dart +++ b/lib/pages/video/detail/widgets/watch_later_list.dart @@ -109,7 +109,7 @@ class _MediaListPanelState extends State { var item = mediaList[index]; return InkWell( onTap: () async { - String bvid = item.bvId!; + String bvid = item.bvid!; int? aid = item.id; String cover = item.cover ?? ''; final int cid = @@ -173,7 +173,7 @@ class _MediaListPanelState extends State { overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: FontWeight.w500, - color: item.bvId == widget.bvid + color: item.bvid == widget.bvid ? Theme.of(context) .colorScheme .primary From 51654ac54e554e0cfe579483d5e26ca7802e8f35 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 9 Oct 2024 22:03:58 +0800 Subject: [PATCH 301/438] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=A0=8F=E9=97=B4=E9=9A=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/view.dart | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index f91ef625..1b70015c 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -153,7 +153,17 @@ class _VideoReplyPanelState extends State child: Container( height: 40, padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), - color: Theme.of(context).colorScheme.surface, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + boxShadow: [ + BoxShadow( + color: Theme.of(context).colorScheme.surface, + blurRadius: 0.0, + spreadRadius: 0.0, + offset: const Offset(2, 0), + ), + ], + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ From a44c0ae51ecb3758ecb8bb9c7f840920eb34cf89 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 9 Oct 2024 22:41:12 +0800 Subject: [PATCH 302/438] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 18347a30..18242fea 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -200,25 +200,36 @@ class ReplyItem extends StatelessWidget { ), ], ), - Row( - children: [ - Text( - Utils.dateFormat(replyItem!.ctime), - style: TextStyle( - fontSize: textTheme.labelSmall!.fontSize, - color: colorScheme.outline, - ), - ), - if (replyItem!.replyControl != null && - replyItem!.replyControl!.location != '') - Text( - ' • ${replyItem!.replyControl!.location!}', + RichText( + text: TextSpan( + children: [ + TextSpan( + text: Utils.dateFormat(replyItem!.ctime), style: TextStyle( - fontSize: textTheme.labelSmall!.fontSize, - color: colorScheme.outline), + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, + ), ), - ], - ) + if (replyItem!.replyControl != null && + replyItem!.replyControl!.location != '') + TextSpan( + text: ' • ${replyItem!.replyControl!.location!}', + style: TextStyle( + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, + ), + ), + if (replyItem!.invisible!) + TextSpan( + text: ' • 隐藏的评论', + style: TextStyle( + color: colorScheme.outline, + fontSize: textTheme.labelSmall!.fontSize, + ), + ), + ], + ), + ), ], ), ], From c634e602b9ba88f55b726d75fde186914e2656d0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 9 Oct 2024 23:33:52 +0800 Subject: [PATCH 303/438] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=BF=87=E5=A4=A7=E6=97=B6=E9=A1=B5=E9=9D=A2=E5=8D=A1?= =?UTF-8?q?=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 3 ++- lib/utils/subtitle.dart | 56 +++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 95ea6782..406e59f6 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -536,7 +536,8 @@ class VideoHttp { // 获取字幕内容 static Future> getSubtitleContent(url) async { var res = await Request().get('https:$url'); - final String content = SubTitleUtils.convertToWebVTT(res.data['body']); + final String content = + await SubTitleUtils.convertToWebVTT(res.data['body']); final List body = res.data['body']; return {'content': content, 'body': body}; } diff --git a/lib/utils/subtitle.dart b/lib/utils/subtitle.dart index 1b4088f3..9743711c 100644 --- a/lib/utils/subtitle.dart +++ b/lib/utils/subtitle.dart @@ -1,21 +1,51 @@ +import 'dart:async'; +import 'dart:isolate'; + class SubTitleUtils { // 格式整理 - static String convertToWebVTT(List jsonData) { - String webVTTContent = 'WEBVTT FILE\n\n'; + static Future convertToWebVTT(List jsonData) async { + final receivePort = ReceivePort(); + await Isolate.spawn(_convertToWebVTTIsolate, receivePort.sendPort); - for (int i = 0; i < jsonData.length; i++) { - final item = jsonData[i]; - double from = double.parse(item['from'].toString()); - double to = double.parse(item['to'].toString()); - int sid = (item['sid'] ?? 0) as int; - String content = item['content'] as String; + final sendPort = await receivePort.first as SendPort; + final response = ReceivePort(); + sendPort.send([jsonData, response.sendPort]); - webVTTContent += '$sid\n'; - webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n'; - webVTTContent += '$content\n\n'; + return await response.first as String; + } + + static void _convertToWebVTTIsolate(SendPort sendPort) async { + final port = ReceivePort(); + sendPort.send(port.sendPort); + + await for (final message in port) { + final List jsonData = message[0]; + final SendPort replyTo = message[1]; + + String webVTTContent = 'WEBVTT FILE\n\n'; + int chunkSize = 100; // 每次处理100条数据 + int totalChunks = (jsonData.length / chunkSize).ceil(); + + for (int chunk = 0; chunk < totalChunks; chunk++) { + int start = chunk * chunkSize; + int end = start + chunkSize; + if (end > jsonData.length) end = jsonData.length; + + for (int i = start; i < end; i++) { + final item = jsonData[i]; + double from = double.parse(item['from'].toString()); + double to = double.parse(item['to'].toString()); + int sid = (item['sid'] ?? 0) as int; + String content = item['content'] as String; + + webVTTContent += '$sid\n'; + webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n'; + webVTTContent += '$content\n\n'; + } + } + + replyTo.send(webVTTContent); } - - return webVTTContent; } static String formatTime(num seconds) { From fadefa2ddc75024db345e3e29748cff0828e3018 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 10 Oct 2024 22:26:46 +0800 Subject: [PATCH 304/438] =?UTF-8?q?opt:=20bangumiIntroController=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/pages/bangumi/widgets/bangumi_panel.dart | 8 +++++--- lib/pages/video/detail/view.dart | 18 ++++++++++-------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3df7ce25..3a589db6 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -86,9 +86,11 @@ class _BangumiPanelState extends State { item.aid, item.cover, ); - if (_bottomSheetController != null) { - _bottomSheetController?.close(); - } + try { + if (_bottomSheetController != null) { + _bottomSheetController?.close(); + } + } catch (_) {} currentIndex.value = i; scrollToIndex(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 50e94cb1..d3afdf1d 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -82,14 +82,16 @@ class _VideoDetailPageState extends State videoIntroController.videoDetail.listen((value) { videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); - bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); - bangumiIntroController.bangumiDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); - }); - vdCtr.cid.listen((p0) { - videoPlayerServiceHandler.onVideoDetailChange( - bangumiIntroController.bangumiDetail.value, p0); - }); + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); + bangumiIntroController.bangumiDetail.listen((value) { + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); + }); + vdCtr.cid.listen((p0) { + videoPlayerServiceHandler.onVideoDetailChange( + bangumiIntroController.bangumiDetail.value, p0); + }); + } statusBarHeight = localCache.get('statusBarHeight'); autoExitFullcreen = setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); From 2e206f20cc3e5ce77ccb1f31ed875de23bfc1b46 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 10 Oct 2024 23:02:16 +0800 Subject: [PATCH 305/438] Update yml --- .github/workflows/beta_ci.yml | 3 +-- .github/workflows/release_ci.yml | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index 14b51780..9c40de6b 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -12,7 +12,6 @@ on: - ".idea/**" - "!.github/workflows/**" - jobs: update_version: name: Read and update version @@ -96,7 +95,7 @@ jobs: if: steps.cache-flutter.outputs.cache-hit != 'true' uses: subosito/flutter-action@v2 with: - flutter-version: 3.16.5 + flutter-version: 3.19.6 channel: any - name: 下载项目依赖 diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml index 78230645..f7c06d29 100644 --- a/.github/workflows/release_ci.yml +++ b/.github/workflows/release_ci.yml @@ -36,7 +36,7 @@ jobs: if: steps.cache-flutter.outputs.cache-hit != 'true' uses: subosito/flutter-action@v2 with: - flutter-version: 3.16.5 + flutter-version: 3.19.6 channel: any - name: 下载项目依赖 @@ -98,7 +98,7 @@ jobs: uses: subosito/flutter-action@v2.10.0 with: cache: true - flutter-version: 3.16.5 + flutter-version: 3.19.6 - name: flutter build ipa run: | From caa066237d87591652a6648a79a98faaa415b104 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 10 Oct 2024 23:04:59 +0800 Subject: [PATCH 306/438] =?UTF-8?q?v1.0.25=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.25.1010.md | 39 +++++++++++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.25.1010.md diff --git a/change_log/1.0.25.1010.md b/change_log/1.0.25.1010.md new file mode 100644 index 00000000..951efcb1 --- /dev/null +++ b/change_log/1.0.25.1010.md @@ -0,0 +1,39 @@ +## 1.0.25 + +### 功能 ++ 直播弹幕 ++ 稍后再看、收藏夹播放全部 ++ 收藏夹新建、编辑 ++ 评论删除 ++ 评论保存为图片 ++ 动态页滑动切换up ++ up投稿筛选充电视频 ++ 直播tab展示关注up ++ up主页专栏展示 + +### 优化 ++ 视频详情页一键三连 ++ 动态页标识充电视频 ++ 播放器亮度、音量调整百分比展示 ++ 封面预览时视频标题可复制 ++ 竖屏直播布局 ++ 图片预览 ++ 专栏渲染优化 ++ 私信图片查看 + +### 修复 ++ 收藏夹点击异常 ++ 搜索up异常 ++ 系统通知已读异常 ++ [赞了我的]展示错误 ++ 部分up合集无法打开 ++ 切换合集视频投币个数未重置 ++ 搜索条件筛选面板无法滚动 ++ 部分机型导航条未沉浸 ++ 专栏图片渲染问题 ++ 专栏浏览历史记录 ++ 直播间历史记录 + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 617b0c7d..ac04a1b0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.24+1024 +version: 1.0.25+1025 environment: sdk: ">=3.0.0 <4.0.0" From fb84ba04815edb98633e0515d464c5726c2a7dfd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 11 Oct 2024 00:23:27 +0800 Subject: [PATCH 307/438] =?UTF-8?q?fix:=20=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E5=BC=82=E5=B8=B8=E3=80=81=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E5=99=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E5=BC=82=E5=B8=B8=E3=80=81?= =?UTF-8?q?imgQuality/cover=E5=8F=96=E5=80=BC=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 6 +- lib/pages/live_room/controller.dart | 2 +- lib/pages/live_room/view.dart | 214 +++++++++++----------- lib/pages/main/view.dart | 18 +- 4 files changed, 123 insertions(+), 117 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 0b715a89..fbedfbba 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -33,7 +33,11 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { - final int defaultImgQuality = GlobalDataCache().imgQuality; + int defaultImgQuality = 10; + try { + defaultImgQuality = GlobalDataCache().imgQuality; + } catch (_) {} + if (src == '' || src == null) { return placeholder(context); } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 34379f64..0d347bf2 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -64,7 +64,7 @@ class LiveRoomController extends GetxController { ? liveItem.pic : (liveItem.cover != null && liveItem.cover != '') ? liveItem.cover - : null; + : ''; } Request.getBuvid().then((value) => buvid = value); } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index cff0c410..1e0814e9 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -108,6 +108,12 @@ class _LiveRoomPageState extends State @override Widget build(BuildContext context) { + final mediaQuery = MediaQuery.of(context); + final isPortrait = mediaQuery.orientation == Orientation.portrait; + final isLandscape = mediaQuery.orientation == Orientation.landscape; + + final padding = mediaQuery.padding; + Widget videoPlayerPanel = FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { @@ -187,10 +193,8 @@ class _LiveRoomPageState extends State children: [ Obx( () => SizedBox( - height: MediaQuery.of(context).padding.top + - (_liveRoomController.isPortrait.value || - MediaQuery.of(context).orientation == - Orientation.landscape + height: padding.top + + (_liveRoomController.isPortrait.value || isLandscape ? 0 : kToolbarHeight), ), @@ -201,21 +205,18 @@ class _LiveRoomPageState extends State if (plPlayerController.isFullScreen.value == true) { plPlayerController.triggerFullScreen(status: false); } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { + if (isLandscape) { verticalScreen(); } }, child: Obx( () => Container( width: Get.size.width, - height: MediaQuery.of(context).orientation == - Orientation.landscape + height: isLandscape ? Get.size.height : !_liveRoomController.isPortrait.value ? Get.size.width * 9 / 16 - : Get.size.height - - MediaQuery.of(context).padding.top, + : Get.size.height - padding.top, clipBehavior: Clip.hardEdge, decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(6)), @@ -229,7 +230,7 @@ class _LiveRoomPageState extends State // 定位 快速滑动到底部 Positioned( right: 20, - bottom: MediaQuery.of(context).padding.bottom + 80, + bottom: padding.bottom + 80, child: SlideTransition( position: Tween( begin: const Offset(0, 4), @@ -262,10 +263,7 @@ class _LiveRoomPageState extends State titleSpacing: 0, backgroundColor: Colors.transparent, foregroundColor: Colors.white, - toolbarHeight: - MediaQuery.of(context).orientation == Orientation.portrait - ? 56 - : 0, + toolbarHeight: isPortrait ? 56 : 0, title: FutureBuilder( future: _futureBuilder, builder: (context, snapshot) { @@ -317,35 +315,38 @@ class _LiveRoomPageState extends State ), // 消息列表 Obx( - () => Positioned( - top: MediaQuery.of(context).padding.top + - kToolbarHeight + - (_liveRoomController.isPortrait.value - ? Get.size.width - : Get.size.width * 9 / 16), - bottom: 90 + MediaQuery.of(context).padding.bottom, - left: 0, - right: 0, - child: buildMessageListUI( - context, - _liveRoomController, - _scrollController, + () => Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only( + bottom: 90 + padding.bottom, + ), + height: Get.size.height - + (padding.top + + kToolbarHeight + + (_liveRoomController.isPortrait.value + ? Get.size.width + : Get.size.width * 9 / 16) + + 100 + + padding.bottom), + child: buildMessageListUI( + context, + _liveRoomController, + _scrollController, + ), ), ), ), // 消息输入框 Visibility( - visible: MediaQuery.of(context).orientation == Orientation.portrait, + visible: isPortrait, child: Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: EdgeInsets.only( - left: 14, - right: 14, - top: 4, - bottom: MediaQuery.of(context).padding.bottom + 20), + left: 14, right: 14, top: 4, bottom: padding.bottom + 20), decoration: BoxDecoration( color: Colors.grey.withOpacity(0.1), borderRadius: const BorderRadius.all(Radius.circular(20)), @@ -421,6 +422,7 @@ class _LiveRoomPageState extends State ], ), ); + if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, @@ -438,84 +440,82 @@ Widget buildMessageListUI( LiveRoomController liveRoomController, ScrollController scrollController, ) { - return Expanded( - child: Obx( - () => MediaQuery.removePadding( - context: context, - removeTop: true, - removeBottom: true, - child: ShaderMask( - shaderCallback: (Rect bounds) { - return LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black.withOpacity(0.5), - Colors.black, - ], - stops: const [0.01, 0.05, 0.2], - ).createShader(bounds); + return Obx( + () => MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.5), + Colors.black, + ], + stops: const [0.01, 0.05, 0.2], + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, + child: GestureDetector( + onTap: () { + // 键盘失去焦点 + FocusScope.of(context).requestFocus(FocusNode()); }, - blendMode: BlendMode.dstIn, - child: GestureDetector( - onTap: () { - // 键盘失去焦点 - FocusScope.of(context).requestFocus(FocusNode()); - }, - child: ListView.builder( - controller: scrollController, - itemCount: liveRoomController.messageList.length, - itemBuilder: (context, index) { - final LiveMessageModel liveMsgItem = - liveRoomController.messageList[index]; - return Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: liveRoomController.isPortrait.value - ? Colors.black.withOpacity(0.3) - : Colors.grey.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - margin: EdgeInsets.only( - top: index == 0 ? 20.0 : 0.0, - bottom: 6.0, - left: 14.0, - right: 14.0, - ), - padding: const EdgeInsets.symmetric( - vertical: 3.0, - horizontal: 10.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: liveRoomController.isPortrait.value + ? Colors.black.withOpacity(0.3) + : Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], - ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, + ), + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], ), ), - ); - }, - ), + ), + ); + }, ), ), ), diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 4399bf30..f829b0c1 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -24,8 +24,8 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { final MainController _mainController = Get.put(MainController()); late HomeController _homeController; RankController? _rankController; - DynamicsController? _dynamicController; - MediaController? _mediaController; + late DynamicsController _dynamicController; + late MediaController _mediaController; int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; @@ -76,28 +76,30 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { } if (currentPage is DynamicsPage) { - if (_dynamicController!.flag) { + if (_dynamicController.flag) { // 单击返回顶部 双击并刷新 if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) { - _dynamicController!.onRefresh(); + _dynamicController.onRefresh(); } else { - _dynamicController!.animateToTop(); + _dynamicController.animateToTop(); } _lastSelectTime = DateTime.now().millisecondsSinceEpoch; } - _dynamicController!.flag = true; + _dynamicController.flag = true; _mainController.clearUnread(); } else { - _dynamicController?.flag = false; + _dynamicController.flag = false; } if (currentPage is MediaPage) { - _mediaController!.queryFavFolder(); + _mediaController.queryFavFolder(); } } void controllerInit() { _homeController = Get.put(HomeController()); + _dynamicController = Get.put(DynamicsController()); + _mediaController = Get.put(MediaController()); if (_mainController.pagesIds.contains(1)) { _rankController = Get.put(RankController()); } From fffbd62f4fda91a53464225ec00012c4ba6af578 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 12 Oct 2024 11:35:49 +0800 Subject: [PATCH 308/438] opt: Request().get --- lib/http/init.dart | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/http/init.dart b/lib/http/init.dart index eae94ae4..abe8d019 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -217,12 +217,13 @@ class Request { /* * get请求 */ - get(url, {data, options, cancelToken, extra}) async { + get(url, {data, Options? options, cancelToken, extra}) async { Response response; - final Options options = Options(); + options ??= Options(); // 如果 options 为 null,则初始化一个新的 Options 对象 ResponseType resType = ResponseType.json; + if (extra != null) { - resType = extra!['resType'] ?? ResponseType.json; + resType = extra['resType'] ?? ResponseType.json; if (extra['ua'] != null) { options.headers = {'user-agent': headerUa(type: extra['ua'])}; } @@ -238,14 +239,11 @@ class Request { ); return response; } on DioException catch (e) { - Response errResponse = Response( - data: { - 'message': await ApiInterceptor.dioError(e) - }, // 将自定义 Map 数据赋值给 Response 的 data 属性 + return Response( + data: {'message': await ApiInterceptor.dioError(e)}, statusCode: 200, requestOptions: RequestOptions(), ); - return errResponse; } } From cf29a62a524cb3da1bb5457361b55a2ca4b474d6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 12 Oct 2024 14:16:53 +0800 Subject: [PATCH 309/438] =?UTF-8?q?fix:=20=E7=BC=93=E5=AD=98=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 8dff954a..0bd7950c 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1045,6 +1045,14 @@ class PlPlayerController { /// 缓存本次弹幕选项 cacheDanmakuOption() { + final cache = GlobalDataCache(); + cache.blockTypes = blockTypes; + cache.showArea = showArea; + cache.opacityVal = opacityVal; + cache.fontSizeVal = fontSizeVal; + cache.danmakuDurationVal = danmakuDurationVal; + cache.strokeWidth = strokeWidth; + localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); localCache.put(LocalCacheKey.danmakuShowArea, showArea); localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); From e7bbd3d32aa50fd321f02e2ac62f8dff290daae3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 12 Oct 2024 14:27:16 +0800 Subject: [PATCH 310/438] =?UTF-8?q?mod:=20=E5=AE=98=E7=BD=91&=E7=BD=91?= =?UTF-8?q?=E7=9B=98=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 4 ++-- lib/utils/utils.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 81d3c3f4..9164d4e9 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -295,7 +295,7 @@ class AboutController extends GetxController { displayTime: const Duration(milliseconds: 500), ).then( (value) => launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'), mode: LaunchMode.externalApplication, ), ); @@ -349,7 +349,7 @@ class AboutController extends GetxController { // 官网 webSiteUrl() { launchUrl( - Uri.parse('https://pilipalanet.mysxl.cn'), + Uri.parse('https://pilipala.life'), mode: LaunchMode.externalApplication, ); } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index e50c295c..7a254f7d 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -306,7 +306,7 @@ class Utils { onPressed: () async { await SmartDialog.dismiss(); launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'), mode: LaunchMode.externalApplication, ); }, From 85f77ed93349e6f7f15cb45395a87d6cb45dd8e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 12 Oct 2024 22:05:04 +0800 Subject: [PATCH 311/438] =?UTF-8?q?mod:=20up=E4=B8=BB=E9=A1=B5wWebid?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 31 +++++++++++++++--------- lib/pages/member_article/controller.dart | 16 ------------ lib/utils/global_data_cache.dart | 1 + 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index c7b22359..fc99c987 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -4,6 +4,7 @@ import 'package:hive/hive.dart'; import 'package:html/parser.dart'; import 'package:pilipala/models/member/article.dart'; import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import '../common/constants.dart'; import '../models/dynamics/result.dart'; import '../models/follow/result.dart'; @@ -19,14 +20,20 @@ import 'index.dart'; class MemberHttp { static Future memberInfo({ - int? mid, + required int mid, String token = '', }) async { + String? wWebid; + if ((await getWWebid(mid: mid))['status']) { + wWebid = GlobalDataCache().wWebid; + } + Map params = await WbiSign().makSign({ 'mid': mid, 'token': token, 'platform': 'web', 'web_location': 1550101, + ...wWebid != null ? {'w_webid': wWebid} : {}, }); var res = await Request().get( Api.memberInfo, @@ -566,6 +573,10 @@ class MemberHttp { } static Future getWWebid({required int mid}) async { + String? wWebid = GlobalDataCache().wWebid; + if (wWebid != null) { + return {'status': true, 'data': wWebid}; + } var res = await Request().get('https://space.bilibili.com/$mid/article'); String? headContent = parse(res.data).head?.outerHtml; final regex = RegExp( @@ -576,6 +587,7 @@ class MemberHttp { final content = match.group(1); String decodedString = Uri.decodeComponent(content!); Map map = jsonDecode(decodedString); + GlobalDataCache().wWebid = map['access_id']; return {'status': true, 'data': map['access_id']}; } else { return {'status': false, 'data': '请检查登录状态'}; @@ -588,25 +600,20 @@ class MemberHttp { static Future getMemberArticle({ required int mid, required int pn, - required String wWebid, String? offset, }) async { + String? wWebid; + if ((await getWWebid(mid: mid))['status']) { + wWebid = GlobalDataCache().wWebid; + } 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'], + ...wWebid != null ? {'w_webid': wWebid} : {}, }); + var res = await Request().get(Api.opusList, data: params); if (res.data['code'] == 0) { return { 'status': true, diff --git a/lib/pages/member_article/controller.dart b/lib/pages/member_article/controller.dart index cffce2fe..d79fb4a6 100644 --- a/lib/pages/member_article/controller.dart +++ b/lib/pages/member_article/controller.dart @@ -10,7 +10,6 @@ class MemberArticleController extends GetxController { int pn = 1; String? offset; bool hasMore = true; - String? wWebid; RxBool isLoading = false.obs; RxList articleList = [].obs; @@ -20,25 +19,11 @@ class MemberArticleController extends GetxController { 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(); @@ -47,7 +32,6 @@ class MemberArticleController extends GetxController { mid: mid, pn: pn, offset: offset, - wWebid: wWebid!, ); if (res['status']) { offset = res['data'].offset; diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart index 9d925a0f..a2b454a9 100644 --- a/lib/utils/global_data_cache.dart +++ b/lib/utils/global_data_cache.dart @@ -15,6 +15,7 @@ class GlobalDataCache { late FullScreenGestureMode fullScreenGestureMode; late bool enablePlayerControlAnimation; late List actionTypeSort; + String? wWebid; /// 播放器相关 // 弹幕开关 From c1cd024db6ffcee4e8c3192990b9c4360328239b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 01:11:48 +0800 Subject: [PATCH 312/438] =?UTF-8?q?opt:=20up=E4=B8=BB=E9=A1=B5=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/controller.dart | 118 ++-- lib/pages/member/view.dart | 651 +++++++++----------- lib/pages/member/widgets/commen_widget.dart | 24 + lib/pages/member/widgets/profile.dart | 476 +++++++------- 4 files changed, 601 insertions(+), 668 deletions(-) create mode 100644 lib/pages/member/widgets/commen_widget.dart diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 3b7f24a4..8454cebe 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -49,6 +49,8 @@ class MemberController extends GetxController { if (res['status']) { memberInfo.value = res['data']; face.value = res['data'].face; + } else { + SmartDialog.showToast('用户信息请求异常:${res['msg']}'); } return res; } @@ -78,42 +80,10 @@ class MemberController extends GetxController { return; } if (attribute.value == 128) { - blockUser(); - return; + modifyRelation('block'); + } else { + modifyRelation('follow'); } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - await VideoHttp.relationMod( - mid: mid, - act: memberInfo.value.isFollowed! ? 2 : 1, - reSrc: 11, - ); - memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; - relationSearch(); - SmartDialog.dismiss(); - memberInfo.update((val) {}); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); } // 关系查询 @@ -123,24 +93,15 @@ class MemberController extends GetxController { var res = await UserHttp.hasFollow(mid); if (res['status']) { attribute.value = res['data']['attribute']; - switch (attribute.value) { - case 1: - attributeText.value = '悄悄关注'; - break; - case 2: - attributeText.value = '已关注'; - break; - case 6: - attributeText.value = '已互关'; - break; - case 128: - attributeText.value = '已拉黑'; - break; - default: - attributeText.value = '关注'; - } + final Map attributeTextMap = { + 1: '悄悄关注', + 2: '已关注', + 6: '已互关', + 128: '已拉黑', + }; + attributeText.value = attributeTextMap[attribute.value] ?? '关注'; if (res['data']['special'] == 1) { - attributeText.value += 'SP'; + attributeText.value = '特别关注'; } } } @@ -151,16 +112,37 @@ class MemberController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, + modifyRelation('block'); + } + +// 合并关注/取关和拉黑逻辑 + Future modifyRelation(String actionType) async { + if (userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } + + String contentText; + int act; + if (actionType == 'follow') { + contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?'; + act = memberInfo.value.isFollowed! ? 2 : 1; + } else if (actionType == 'block') { + contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主?'; + act = attribute.value != 128 ? 5 : 6; + } else { + return; + } + + showDialog( + context: Get.context!, builder: (BuildContext context) { return AlertDialog( title: const Text('提示'), - content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), + content: Text(contentText), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), + onPressed: () => Navigator.of(context).pop(), child: Text( '点错了', style: TextStyle(color: Theme.of(context).colorScheme.outline), @@ -170,19 +152,26 @@ class MemberController extends GetxController { onPressed: () async { var res = await VideoHttp.relationMod( mid: mid, - act: attribute.value != 128 ? 5 : 6, + act: act, reSrc: 11, ); SmartDialog.dismiss(); if (res['status']) { - attribute.value = attribute.value != 128 ? 128 : 0; - attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; - memberInfo.value.isFollowed = false; + if (actionType == 'follow') { + memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; + } else if (actionType == 'block') { + attribute.value = attribute.value != 128 ? 128 : 0; + attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; + memberInfo.value.isFollowed = false; + } relationSearch(); + if (context.mounted) { + Navigator.of(context).pop(); + } memberInfo.update((val) {}); } }, - child: const Text('确认'), + child: const Text('确定'), ) ], ); @@ -228,17 +217,14 @@ class MemberController extends GetxController { // 跳转查看动态 void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); - // 跳转查看投稿 void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid'); - - // 跳转查看专栏 - void pushSeasonsPage() {} // 跳转查看最近投币 void pushRecentCoinsPage() async { if (recentCoinsList.isNotEmpty) {} } + // 跳转查看收藏夹 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 fafba9dc..df501253 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -1,13 +1,13 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/member/info.dart'; import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/utils/utils.dart'; - +import 'widgets/commen_widget.dart'; import 'widgets/conis.dart'; import 'widgets/like.dart'; import 'widgets/profile.dart'; @@ -65,259 +65,233 @@ class _MemberPageState extends State @override Widget build(BuildContext context) { return Scaffold( - primary: true, - body: Column( - children: [ - AppBar( - title: StreamBuilder( - stream: appbarStream.stream.distinct(), - initialData: false, - builder: (BuildContext context, AsyncSnapshot snapshot) { - return AnimatedOpacity( - opacity: snapshot.data ? 1 : 0, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 500), - child: Row( - children: [ - Row( - children: [ - Obx( - () => NetworkImgLayer( - width: 35, - height: 35, - type: 'avatar', - src: _memberController.face.value, - ), - ), - const SizedBox(width: 10), - Obx( - () => Text( - _memberController.memberInfo.value.name ?? '', - style: TextStyle( - color: - Theme.of(context).colorScheme.onSurface, - fontSize: 14), - ), - ), - ], - ) - ], + appBar: AppBar( + title: StreamBuilder( + stream: appbarStream.stream.distinct(), + initialData: false, + builder: (BuildContext context, AsyncSnapshot snapshot) { + return AnimatedOpacity( + opacity: snapshot.data ? 1 : 0, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 500), + child: Row( + children: [ + Obx( + () => NetworkImgLayer( + width: 35, + height: 35, + type: 'avatar', + src: _memberController.face.value, + ), ), - ); - }, - ), - actions: [ - IconButton( - onPressed: () => Get.toNamed( - '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), - icon: const Icon(Icons.search_outlined), - ), - PopupMenuButton( - icon: const Icon(Icons.more_vert), - itemBuilder: (BuildContext context) => [ - if (_memberController.ownerMid != _memberController.mid) ...[ - PopupMenuItem( - onTap: () => _memberController.blockUser(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.block, size: 19), - const SizedBox(width: 10), - Text(_memberController.attribute.value != 128 - ? '加入黑名单' - : '移除黑名单'), - ], - ), - ) - ], - PopupMenuItem( - onTap: () => _memberController.shareUser(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.share_outlined, size: 19), - const SizedBox(width: 10), - Text(_memberController.ownerMid != _memberController.mid - ? '分享UP主' - : '分享我的主页'), - ], + const SizedBox(width: 10), + Obx( + () => Text( + _memberController.memberInfo.value.name ?? '', + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontSize: 14), ), ), ], ), - const SizedBox(width: 4), - ], + ); + }, + ), + actions: [ + IconButton( + onPressed: () => Get.toNamed( + '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), + icon: const Icon(Icons.search_outlined), ), - Expanded( - child: SingleChildScrollView( - controller: _extendNestCtr, - child: Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 20, - ), - child: Column( + PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + if (_memberController.ownerMid != _memberController.mid) ...[ + PopupMenuItem( + onTap: () => _memberController.blockUser(), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.block, size: 19), + const SizedBox(width: 10), + Text(_memberController.attribute.value != 128 + ? '加入黑名单' + : '移除黑名单'), + ], + ), + ) + ], + PopupMenuItem( + onTap: () => _memberController.shareUser(), + child: Row( + mainAxisSize: MainAxisSize.min, children: [ - profileWidget(), - - /// 动态链接 - Obx( - () => ListTile( - onTap: _memberController.pushDynamicsPage, - 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.pushArticlePage, - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), - ), - ), - - /// 合集 - Obx( - () => ListTile( - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的合集')), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: FutureBuilder( - future: _memberSeasonsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - if (data['data'].seasonsList.isEmpty) { - return commenWidget('用户没有设置合集'); - } else { - return MemberSeasonsPanel(data: data['data']); - } - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - - /// 追番 - /// 最近投币 - Obx( - () => _memberController.recentCoinsList.isNotEmpty - ? const ListTile(title: Text('最近投币的视频')) - : const SizedBox(), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberCoinsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - return MemberCoinsPanel(data: data['data']); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - ), - - /// 最近点赞 - Obx( - () => _memberController.recentLikeList.isNotEmpty - ? const ListTile(title: Text('最近点赞的视频')) - : const SizedBox(), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberLikeFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - return MemberLikePanel(data: data['data']); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - ), + const Icon(Icons.share_outlined, size: 19), + const SizedBox(width: 10), + Text(_memberController.ownerMid != _memberController.mid + ? '分享UP主' + : '分享我的主页'), ], ), ), + ], + ), + const SizedBox(width: 4), + ], + ), + primary: true, + body: ListView( + controller: _extendNestCtr, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 20, + ), + children: [ + profileWidget(), + + /// 动态链接 + Obx( + () => ListTile( + onTap: _memberController.pushDynamicsPage, + 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.pushArticlePage, + title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'), + trailing: const Icon(Icons.arrow_forward_outlined, size: 19), + ), + ), + + /// 合集 + Obx( + () => ListTile( + title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的合集'), + ), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: FutureBuilder( + future: _memberSeasonsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + if (data['data'].seasonsList.isEmpty) { + return const CommenWidget(msg: '用户没有设置合集'); + } else { + return MemberSeasonsPanel(data: data['data']); + } + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + + /// 追番 + /// 最近投币 + Obx( + () => _memberController.recentCoinsList.isNotEmpty + ? const ListTile(title: Text('最近投币的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberCoinsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberCoinsPanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ), + + /// 最近点赞 + Obx( + () => _memberController.recentLikeList.isNotEmpty + ? const ListTile(title: Text('最近点赞的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberLikeFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberLikePanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), ), ), ], @@ -334,115 +308,90 @@ class _MemberPageState extends State if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { + Rx memberInfo = _memberController.memberInfo; return Obx( - () => Stack( - alignment: AlignmentDirectional.center, + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + ProfilePanel(ctr: _memberController), + const SizedBox(height: 20), + Row( children: [ - ProfilePanel(ctr: _memberController), - const SizedBox(height: 20), - Row( - children: [ - Flexible( - child: Text( - _memberController.memberInfo.value.name!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.bold, - color: _memberController.memberInfo.value - .vip!.nicknameColor != - null - ? Color(_memberController.memberInfo - .value.vip!.nicknameColor!) - : null), - )), - const SizedBox(width: 2), - if (_memberController.memberInfo.value.sex == '女') - const Icon( - FontAwesomeIcons.venus, - size: 14, - color: Colors.pink, - ), - if (_memberController.memberInfo.value.sex == '男') - const Icon( - FontAwesomeIcons.mars, - size: 14, - color: Colors.blue, - ), - const SizedBox(width: 4), - Image.asset( - 'assets/images/lv/lv${_memberController.memberInfo.value.level}.png', - height: 11, - ), - const SizedBox(width: 6), - if (_memberController - .memberInfo.value.vip!.status == - 1 && - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans'] != - '') ...[ - Image.network( - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans'], - height: 20, - ), - ] else if (_memberController - .memberInfo.value.vip!.status == - 1 && - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans_static'] != - '') ...[ - Image.network( - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans_static'], - height: 20, - ), - ] - ], + Flexible( + child: Text( + memberInfo.value.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.bold, + color: memberInfo.value.vip!.nicknameColor != + null + ? Color(_memberController + .memberInfo.value.vip!.nicknameColor!) + : null), + )), + const SizedBox(width: 2), + if (memberInfo.value.sex == '女') + const Icon( + FontAwesomeIcons.venus, + size: 14, + color: Colors.pink, + ), + if (memberInfo.value.sex == '男') + const Icon( + FontAwesomeIcons.mars, + size: 14, + color: Colors.blue, + ), + const SizedBox(width: 4), + Image.asset( + 'assets/images/lv/lv${memberInfo.value.level}.png', + height: 11, ), - if (_memberController - .memberInfo.value.official!['title'] != - '') ...[ - const SizedBox(height: 6), - Text.rich( - maxLines: 2, - TextSpan( - text: _memberController - .memberInfo.value.official!['role'] == - 1 - ? '个人认证:' - : '企业认证:', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - children: [ - TextSpan( - text: _memberController - .memberInfo.value.official!['title'], - ), - ], - ), - softWrap: true, + const SizedBox(width: 6), + if (memberInfo.value.vip!.status == 1 && + memberInfo + .value.vip!.label!['img_label_uri_hans'] != + '') ...[ + Image.network( + memberInfo.value.vip!.label!['img_label_uri_hans'], + height: 20, ), - ], - const SizedBox(height: 6), - if (_memberController.memberInfo.value.sign != '') - SelectableText( - _memberController.memberInfo.value.sign!, + ] else if (memberInfo.value.vip!.status == 1 && + memberInfo.value.vip! + .label!['img_label_uri_hans_static'] != + '') ...[ + Image.network( + memberInfo + .value.vip!.label!['img_label_uri_hans_static'], + height: 20, ), + ] ], ), + if (memberInfo.value.official!['title'] != '') ...[ + const SizedBox(height: 6), + Text( + memberInfo.value.official!['role'] == 1 + ? '个人认证:${memberInfo.value.official!['title']}' + : '企业认证:${memberInfo.value.official!['title']}', + maxLines: 2, + softWrap: true, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + const SizedBox(height: 6), + SelectableText(memberInfo.value.sign ?? ''), ], ), ); } else { - return const SizedBox(); + return ProfilePanel(ctr: _memberController, loadingStatus: true); } } else { // 骨架屏 @@ -452,22 +401,4 @@ class _MemberPageState extends State ), ); } - - Widget commenWidget(msg) { - return Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 30, - ), - child: Center( - child: Text( - msg, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith(color: Theme.of(context).colorScheme.outline), - ), - ), - ); - } } diff --git a/lib/pages/member/widgets/commen_widget.dart b/lib/pages/member/widgets/commen_widget.dart new file mode 100644 index 00000000..0c92803e --- /dev/null +++ b/lib/pages/member/widgets/commen_widget.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class CommenWidget extends StatelessWidget { + final String msg; + + const CommenWidget({required this.msg, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0), + child: Center( + child: Text( + msg, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + ), + ); + } +} diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index 8c6385db..7001c886 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -18,253 +18,245 @@ class ProfilePanel extends StatelessWidget { @override Widget build(BuildContext context) { MemberInfoModel memberInfo = ctr.memberInfo.value; - return Builder( - builder: ((context) { - return Padding( - padding: - EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20), - child: Row( - children: [ - Hero( - tag: ctr.heroTag!, - child: Stack( - children: [ - NetworkImgLayer( - width: 90, - height: 90, - type: 'avatar', - src: !loadingStatus ? memberInfo.face : ctr.face.value, - ), - if (!loadingStatus && - memberInfo.liveRoom != null && - memberInfo.liveRoom!.liveStatus == 1) - Positioned( - bottom: 0, - left: 14, - child: GestureDetector( - onTap: () { - LiveItemModel liveItem = LiveItemModel.fromJson({ - 'title': memberInfo.liveRoom!.title, - 'uname': memberInfo.name, - 'face': memberInfo.face, - 'roomid': memberInfo.liveRoom!.roomId, - 'watched_show': memberInfo.liveRoom!.watchedShow, - }); - Get.toNamed( - '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', - arguments: {'liveItem': liveItem}, - ); - }, - child: Container( - padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - const BorderRadius.all(Radius.circular(10)), - ), - child: Row(children: [ - Image.asset( - 'assets/images/live.gif', - height: 10, - ), - Text( - ' 直播中', - style: TextStyle( - color: Colors.white, - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize), - ) - ]), - ), - ), - ) - ], + final int? mid = memberInfo.mid; + final String? name = memberInfo.name; + + Map buildStatItem({ + required String label, + required String value, + required VoidCallback onTap, + }) { + return { + 'label': label, + 'value': value, + 'fn': onTap, + }; + } + + final List> statList = [ + buildStatItem( + label: '关注', + value: !loadingStatus ? "${ctr.userStat!['following']}" : '-', + onTap: () { + Get.toNamed('/follow?mid=$mid&name=$name'); + }, + ), + buildStatItem( + label: '粉丝', + value: !loadingStatus + ? ctr.userStat!['follower'] != null + ? Utils.numFormat(ctr.userStat!['follower']) + : '-' + : '-', + onTap: () { + Get.toNamed('/fan?mid=$mid&name=$name'); + }, + ), + buildStatItem( + label: '获赞', + value: !loadingStatus + ? ctr.userStat!['likes'] != null + ? Utils.numFormat(ctr.userStat!['likes']) + : '-' + : '-', + onTap: () {}, + ), + ]; + + return Padding( + padding: const EdgeInsets.only(top: 30, left: 4), + child: Row( + children: [ + Hero( + tag: ctr.heroTag!, + child: Stack( + children: [ + NetworkImgLayer( + width: 90, + height: 90, + type: 'avatar', + src: !loadingStatus ? memberInfo.face : ctr.face.value, ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: - const EdgeInsets.only(top: 10, left: 10, right: 10), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - InkWell( - onTap: () { - Get.toNamed( - '/follow?mid=${memberInfo.mid}&name=${memberInfo.name}'); - }, - child: Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['following'].toString() - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - Text( - '关注', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), + if (!loadingStatus && + memberInfo.liveRoom != null && + memberInfo.liveRoom!.liveStatus == 1) + Positioned( + bottom: 0, + left: 14, + child: GestureDetector( + onTap: () { + LiveItemModel liveItem = LiveItemModel( + title: memberInfo.liveRoom!.title, + uname: memberInfo.name, + face: memberInfo.face, + roomId: memberInfo.liveRoom!.roomId, + watchedShow: memberInfo.liveRoom!.watchedShow, + ); + Get.toNamed( + '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', + arguments: {'liveItem': liveItem}, + ); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: + const BorderRadius.all(Radius.circular(10)), + ), + child: Row(children: [ + Image.asset( + 'assets/images/live.gif', + height: 10, ), - InkWell( - onTap: () { - Get.toNamed( - '/fan?mid=${memberInfo.mid}&name=${memberInfo.name}'); - }, - child: Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['follower'] != null - ? Utils.numFormat( - ctr.userStat!['follower'], - ) - : '-' - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold)), - Text( - '粉丝', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), - ), - Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['likes'] != null - ? Utils.numFormat( - ctr.userStat!['likes'], - ) - : '-' - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold)), - Text( - '获赞', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), - ], + Text( + ' 直播中', + style: TextStyle( + color: Colors.white, + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize), + ) + ]), ), ), - const SizedBox(height: 10), - if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[ - Row( - children: [ - Obx( - () => Expanded( - child: TextButton( - onPressed: () => loadingStatus - ? null - : ctr.actionRelationMod(), - style: TextButton.styleFrom( - foregroundColor: ctr.attribute.value == -1 - ? Colors.transparent - : ctr.attribute.value != 0 - ? Theme.of(context) - .colorScheme - .outline - : Theme.of(context) - .colorScheme - .onPrimary, - backgroundColor: ctr.attribute.value != 0 - ? Theme.of(context) - .colorScheme - .onInverseSurface - : Theme.of(context) - .colorScheme - .primary, // 设置按钮背景色 - ), - child: Obx(() => Text(ctr.attributeText.value)), - ), - ), - ), - const SizedBox(width: 8), - Expanded( - child: TextButton( - onPressed: () { - Get.toNamed( - '/whisperDetail', - parameters: { - 'name': memberInfo.name!, - 'face': memberInfo.face!, - 'mid': memberInfo.mid.toString(), - 'heroTag': ctr.heroTag!, - }, - ); - }, - style: TextButton.styleFrom( - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - child: const Text('发消息'), - ), - ) - ], - ) - ], - if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[ - TextButton( - onPressed: () { - SmartDialog.showToast('功能开发中 💪'); - }, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 80, right: 80), - foregroundColor: - Theme.of(context).colorScheme.onPrimary, - backgroundColor: - Theme.of(context).colorScheme.primary, - ), - child: const Text('编辑资料'), - ) - ], - if (ctr.ownerMid == -1) ...[ - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 80, right: 80), - foregroundColor: - Theme.of(context).colorScheme.outline, - backgroundColor: - Theme.of(context).colorScheme.onInverseSurface, - ), - child: const Text('未登录'), - ) - ] - ], - ), - ), - ], + ) + ], + ), ), - ); - }), + const SizedBox(width: 12), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: statList.map((item) { + return buildStatColumn( + context, + item['label'], + item['value'], + item['fn'], + ); + }).toList(), + ), + ), + const SizedBox(height: 16), + if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) + buildActionButtons(context, ctr, memberInfo), + if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) + buildEditProfileButton(context), + if (ctr.ownerMid == -1) buildNotLoggedInButton(context), + ], + ), + ), + ], + ), + ); + } + + Widget buildStatColumn( + BuildContext context, + String label, + String value, + VoidCallback? onTap, + ) { + return InkWell( + onTap: onTap, + child: Column( + children: [ + Text( + value, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + Text( + label, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + ), + ), + ], + ), + ); + } + + Widget buildActionButtons( + BuildContext context, + dynamic ctr, + MemberInfoModel memberInfo, + ) { + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Row( + children: [ + const SizedBox(width: 20), + Obx( + () => Expanded( + child: TextButton( + onPressed: () => loadingStatus ? null : ctr.actionRelationMod(), + style: TextButton.styleFrom( + foregroundColor: ctr.attribute.value == -1 + ? Colors.transparent + : ctr.attribute.value != 0 + ? colorScheme.outline + : colorScheme.onPrimary, + backgroundColor: ctr.attribute.value != 0 + ? colorScheme.onInverseSurface + : colorScheme.primary, + ), + child: Obx(() => Text(ctr.attributeText.value)), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: TextButton( + onPressed: () { + Get.toNamed( + '/whisperDetail', + parameters: { + 'name': memberInfo.name!, + 'face': memberInfo.face!, + 'mid': memberInfo.mid.toString(), + 'heroTag': ctr.heroTag!, + }, + ); + }, + style: TextButton.styleFrom( + backgroundColor: colorScheme.onInverseSurface, + ), + child: const Text('发消息'), + ), + ), + ], + ); + } + + Widget buildEditProfileButton(BuildContext context) { + return TextButton( + onPressed: () { + SmartDialog.showToast('功能开发中 💪'); + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 80), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context).colorScheme.primary, + ), + child: const Text('编辑资料'), + ); + } + + Widget buildNotLoggedInButton(BuildContext context) { + return TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 80), + foregroundColor: Theme.of(context).colorScheme.outline, + backgroundColor: Theme.of(context).colorScheme.onInverseSurface, + ), + child: const Text('未登录'), ); } } From b03fe0fce8c564879d660e82159adaa8cb2ccb23 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 01:19:59 +0800 Subject: [PATCH 313/438] =?UTF-8?q?fix:=20=E6=B8=85=E9=99=A4=E7=BC=93?= =?UTF-8?q?=E5=AD=98dialog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/cache_manage.dart | 77 +++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/lib/utils/cache_manage.dart b/lib/utils/cache_manage.dart index d6bbb816..4603da9f 100644 --- a/lib/utils/cache_manage.dart +++ b/lib/utils/cache_manage.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -50,13 +51,17 @@ class CacheManage { Future getTotalSizeOfFilesInDir(final FileSystemEntity file) async { if (file is File) { int length = await file.length(); - return double.parse(length.toString()); + return length.toDouble(); } if (file is Directory) { - final List children = file.listSync(); double total = 0; - for (final FileSystemEntity child in children) { - total += await getTotalSizeOfFilesInDir(child); + try { + await for (final FileSystemEntity child in file.list()) { + total += await getTotalSizeOfFilesInDir(child); + } + } catch (e) { + // 处理错误,例如记录日志或显示错误消息 + print('读取目录时出错: $e'); } return total; } @@ -77,16 +82,17 @@ class CacheManage { // 清除缓存 Future clearCacheAll() async { - bool cleanStatus = await SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, + bool? cleanStatus = await showDialog( + context: Get.context!, builder: (BuildContext context) { return AlertDialog( title: const Text('提示'), content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'), actions: [ TextButton( - onPressed: (() => {SmartDialog.dismiss()}), + onPressed: () { + Navigator.of(context).pop(false); + }, child: Text( '取消', style: TextStyle(color: Theme.of(context).colorScheme.outline), @@ -94,40 +100,45 @@ class CacheManage { ), TextButton( onPressed: () async { - SmartDialog.dismiss(); - SmartDialog.showLoading(msg: '正在清除...'); - try { - // 清除缓存 图片缓存 - await clearLibraryCache(); - SmartDialog.dismiss().then((res) { - SmartDialog.showToast('清除完成'); - }); - } catch (err) { - SmartDialog.dismiss(); - SmartDialog.showToast(err.toString()); - } + Navigator.of(context).pop(true); }, child: const Text('确认'), - ) + ), ], ); }, - ).then((res) { - return true; - }); - return cleanStatus; + ); + if (cleanStatus != null && cleanStatus) { + SmartDialog.showLoading(msg: '正在清除...'); + try { + // 清除缓存 图片缓存 + await clearLibraryCache(); + SmartDialog.dismiss().then((res) { + SmartDialog.showToast('清除完成'); + }); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + } + return cleanStatus!; } /// 清除 Documents 目录下的 DioCache.db - Future clearApplicationCache() async { - Directory directory = await getApplicationDocumentsDirectory(); - if (directory.existsSync()) { - String dioCacheFileName = - '${directory.path}${Platform.pathSeparator}DioCache.db'; - var dioCacheFile = File(dioCacheFileName); - if (dioCacheFile.existsSync()) { - dioCacheFile.delete(); + Future clearApplicationCache() async { + try { + Directory directory = await getApplicationDocumentsDirectory(); + if (directory.existsSync()) { + String dioCacheFileName = + '${directory.path}${Platform.pathSeparator}DioCache.db'; + File dioCacheFile = File(dioCacheFileName); + if (await dioCacheFile.exists()) { + await dioCacheFile.delete(); + } } + } catch (e) { + // 处理错误,例如记录日志或显示错误消息 + print('清除缓存时出错: $e'); } } From 326827624c2c30da8942dc79db54ba13e471605f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 01:27:07 +0800 Subject: [PATCH 314/438] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E9=A1=B6=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index f197fdfa..ea6e72ba 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -8,7 +8,7 @@ import 'package:pilipala/utils/storage.dart'; import '../../http/index.dart'; class HomeController extends GetxController with GetTickerProviderStateMixin { - bool flag = false; + bool flag = true; late RxList tabs = [].obs; RxInt initialIndex = 1.obs; late TabController tabController; From 430795017763f77444459b039c016bc0506fdfeb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 15:48:27 +0800 Subject: [PATCH 315/438] =?UTF-8?q?fix:=20=E8=BF=94=E5=9B=9E=E4=B8=8A?= =?UTF-8?q?=E4=B8=80=E9=A1=B5=E6=92=AD=E6=94=BE=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index a57ec1de..3fead9c7 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -675,7 +675,6 @@ class VideoDetailController extends GetxController @override void onClose() { super.onClose(); - plPlayerController.dispose(); tabCtr.removeListener(() { onTabChanged(); }); From 3a2d9def650e54e0fa77e95220856a27313dcf84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 16:41:53 +0800 Subject: [PATCH 316/438] =?UTF-8?q?feat:=20=E5=85=A8=E5=B1=8F=E6=89=8B?= =?UTF-8?q?=E5=8A=BF=E5=8F=AF=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/gesture_mode.dart | 7 +++++-- lib/pages/setting/pages/play_gesture_set.dart | 4 +++- lib/plugin/pl_player/view.dart | 14 +++++++++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/models/common/gesture_mode.dart b/lib/models/common/gesture_mode.dart index 1149ae12..bf51fd96 100644 --- a/lib/models/common/gesture_mode.dart +++ b/lib/models/common/gesture_mode.dart @@ -4,9 +4,12 @@ enum FullScreenGestureMode { /// 从下滑到上 fromBottomtoTop, + + /// 关闭手势 + none, } extension FullScreenGestureModeExtension on FullScreenGestureMode { - String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; - String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; + String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index]; + String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index]; } diff --git a/lib/pages/setting/pages/play_gesture_set.dart b/lib/pages/setting/pages/play_gesture_set.dart index e671bfb2..9659e58a 100644 --- a/lib/pages/setting/pages/play_gesture_set.dart +++ b/lib/pages/setting/pages/play_gesture_set.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/utils/global_data_cache.dart'; @@ -22,7 +23,7 @@ class _PlayGesturePageState extends State { void initState() { super.initState(); fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode, - defaultValue: FullScreenGestureMode.values.last.index); + defaultValue: FullScreenGestureMode.fromBottomtoTop.index); } @override @@ -71,6 +72,7 @@ class _PlayGesturePageState extends State { GlobalDataCache().fullScreenGestureMode.index; setting.put( SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); + SmartDialog.showToast('设置成功'); setState(() {}); } }, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index dd5b617e..b69a3d16 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -204,6 +204,14 @@ class _PLVideoPlayerState extends State widget.controller.brightness.value = value; } + bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) { + if (fullScreenGestureMode == FullScreenGestureMode.none) { + return false; + } else { + return tapPosition < sectionWidth * 2; + } + } + @override void dispose() { animationController.dispose(); @@ -660,12 +668,12 @@ class _PLVideoPlayerState extends State _brightnessValue.value - delta / level; final double result = brightness.clamp(0.0, 1.0); setBrightness(result); - } else if (tapPosition < sectionWidth * 2) { + } else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) { // 全屏 final double dy = details.delta.dy; const double threshold = 7.0; // 滑动阈值 - final bool flag = - fullScreenGestureMode != FullScreenGestureMode.values.last; + final bool flag = fullScreenGestureMode != + FullScreenGestureMode.fromBottomtoTop; if (dy > _distance.value && dy > threshold && !_.controlsLock.value) { From 51ff684aafb7a08d297026c2143c5a7a6bfd3f63 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 17:19:39 +0800 Subject: [PATCH 317/438] =?UTF-8?q?fix:=20=E5=AF=BC=E8=88=AA=E6=A0=8F?= =?UTF-8?q?=E6=B2=89=E6=B5=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 1 + lib/pages/main/view.dart | 9 +++++++++ lib/pages/video/detail/view.dart | 13 ++++++++----- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fe93da22..3ac97b25 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -60,6 +60,7 @@ void main() async { systemNavigationBarColor: Colors.transparent, systemNavigationBarDividerColor: Colors.transparent, statusBarColor: Colors.transparent, + systemNavigationBarContrastEnforced: false, )); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index f829b0c1..e978eaaa 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/dynamic_badge_mode.dart'; @@ -127,6 +128,14 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { MediaQuery.sizeOf(context).width * 9 / 16; localCache.put('sheetHeight', sheetHeight); localCache.put('statusBarHeight', statusBarHeight); + + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: + Get.isDarkMode ? Brightness.light : Brightness.dark, + ), + ); return PopScope( canPop: false, onPopInvoked: (bool didPop) async { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index d3afdf1d..7fe76745 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -511,6 +511,14 @@ class _VideoDetailPageState extends State exitFullScreen(); } + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: + Get.isDarkMode ? Brightness.light : Brightness.dark, + ), + ); + Widget buildLoadingWidget() { return Center(child: Lottie.asset('assets/loading.json', width: 200)); } @@ -605,11 +613,6 @@ class _VideoDetailPageState extends State backgroundColor: Colors.black, elevation: 0, scrolledUnderElevation: 0, - systemOverlayStyle: Get.isDarkMode - ? SystemUiOverlayStyle.light - : snapshot.data!.toDouble() > kToolbarHeight - ? SystemUiOverlayStyle.dark - : SystemUiOverlayStyle.light, ); }), ), From d0e689070e3d1a3e8172b76ec385a3511098949c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 18:53:48 +0800 Subject: [PATCH 318/438] =?UTF-8?q?mod:=20=E5=85=A8=E5=B1=8F=E6=89=8B?= =?UTF-8?q?=E5=8A=BF=E5=8F=AF=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 12 ++++++------ lib/utils/global_data_cache.dart | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index b69a3d16..6b57a8e1 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -205,11 +205,8 @@ class _PLVideoPlayerState extends State } bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) { - if (fullScreenGestureMode == FullScreenGestureMode.none) { - return false; - } else { - return tapPosition < sectionWidth * 2; - } + return fullScreenGestureMode != FullScreenGestureMode.none && + tapPosition < sectionWidth * 2; } @override @@ -646,7 +643,10 @@ class _PLVideoPlayerState extends State onVerticalDragUpdate: (DragUpdateDetails details) async { final double totalWidth = MediaQuery.sizeOf(context).width; final double tapPosition = details.localPosition.dx; - final double sectionWidth = totalWidth / 3; + final double sectionWidth = + fullScreenGestureMode == FullScreenGestureMode.none + ? totalWidth / 2 + : totalWidth / 3; final double delta = details.delta.dy; /// 锁定时禁用 diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart index a2b454a9..ea2eda37 100644 --- a/lib/utils/global_data_cache.dart +++ b/lib/utils/global_data_cache.dart @@ -60,7 +60,7 @@ class GlobalDataCache { defaultValue: 10); // 设置全局变量 fullScreenGestureMode = FullScreenGestureMode.values[setting.get( SettingBoxKey.fullScreenGestureMode, - defaultValue: FullScreenGestureMode.values.last.index) as int]; + defaultValue: FullScreenGestureMode.fromBottomtoTop.index)]; enablePlayerControlAnimation = setting .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort, From 47ec38b465272cb656d9ed55a2cca42823f2c853 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 23:00:32 +0800 Subject: [PATCH 319/438] feat: deepLink --- android/app/src/main/AndroidManifest.xml | 14 +- ios/Podfile.lock | 6 + ios/Runner/Info.plist | 49 ++-- .../detail/reply/widgets/reply_item.dart | 2 +- lib/utils/app_scheme.dart | 233 +++++++++++------- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 40 +++ pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 12 files changed, 235 insertions(+), 121 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f119eb1e..c948bc8f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -47,13 +47,14 @@ + + + + + + + + + diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a400600f..040c0297 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - app_links (0.0.2): + - Flutter - appscheme (1.0.4): - Flutter - audio_service (0.0.1): @@ -66,6 +68,7 @@ PODS: - Flutter DEPENDENCIES: + - app_links (from `.symlinks/plugins/app_links/ios`) - appscheme (from `.symlinks/plugins/appscheme/ios`) - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) @@ -102,6 +105,8 @@ SPEC REPOS: - Toast EXTERNAL SOURCES: + app_links: + :path: ".symlinks/plugins/app_links/ios" appscheme: :path: ".symlinks/plugins/appscheme/ios" audio_service: @@ -160,6 +165,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: + app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0 appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8 audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 1e7d9fed..24dceb17 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -65,44 +65,29 @@ CFBundleURLName - + bilibili CFBundleURLSchemes http https - - CFBundleURLTypes - - - CFBundleURLName - - CFBundleURLSchemes - - m.bilibili.com - bilibili.com - www.bilibili.com - bangumi.bilibili.com - bilibili.cn - www.bilibili.cn - bangumi.bilibili.cn - bilibili.tv - www.bilibili.tv - bangumi.bilibili.tv - miniapp.bilibili.com - live.bilibili.com - - - - - - - - CFBundleURLName - bilibili - CFBundleURLSchemes - bilibili + m.bilibili.com + bilibili.com + www.bilibili.com + bangumi.bilibili.com + bilibili.cn + www.bilibili.cn + bangumi.bilibili.cn + bilibili.tv + www.bilibili.tv + bangumi.bilibili.tv + miniapp.bilibili.com + live.bilibili.com + pili + pilipala + FlutterDeepLinkingEnabled + UIBackgroundModes diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 18242fea..1d23548b 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -730,7 +730,7 @@ InlineSpan buildContent( source: '', dataString: matchStr, ); - PiliSchame.fullPathPush(scheme); + PiliSchame.httpsScheme(scheme); } } else { if (appUrlSchema.startsWith('bilibili://search')) { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index f26eff77..61c2284f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -1,3 +1,4 @@ +import 'package:app_links/app_links.dart'; import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -10,22 +11,29 @@ import 'url_utils.dart'; import 'utils.dart'; class PiliSchame { + static late AppLinks appLinks; static AppScheme appScheme = AppSchemeImpl.getInstance()!; static Future init() async { - /// - final SchemeEntity? value = await appScheme.getInitScheme(); - if (value != null) { - _routePush(value); - } + appLinks = AppLinks(); + appLinks.uriLinkStream.listen((Uri uri) { + final String scheme = uri.scheme; + if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) { + piliScheme(uri); + } + }); + + appScheme.getInitScheme().then((SchemeEntity? value) { + if (value != null) { + _routePush(value); + } + }); - /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { if (value != null) { _routePush(value); } }); - /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { if (event != null) { _routePush(event); @@ -36,88 +44,11 @@ class PiliSchame { /// 路由跳转 static void _routePush(value) async { final String scheme = value.scheme; - final String host = value.host; - final String path = value.path; if (scheme == 'bilibili') { - switch (host) { - case 'root': - Navigator.popUntil( - Get.context!, (Route route) => route.isFirst); - break; - case 'space': - final String mid = path.split('/').last; - Get.toNamed( - '/member?mid=$mid', - arguments: {'face': null}, - ); - break; - case 'video': - String pathQuery = path.split('/').last; - final numericRegex = RegExp(r'^[0-9]+$'); - if (numericRegex.hasMatch(pathQuery)) { - pathQuery = 'AV$pathQuery'; - } - Map map = IdUtils.matchAvorBv(input: pathQuery); - if (map.containsKey('AV')) { - _videoPush(map['AV'], null); - } else if (map.containsKey('BV')) { - _videoPush(null, map['BV']); - } else { - SmartDialog.showToast('投稿匹配失败'); - } - break; - case 'live': - final String roomId = path.split('/').last; - Get.toNamed( - '/liveRoom?roomid=$roomId', - arguments: {'liveItem': null, 'heroTag': roomId}, - ); - break; - case 'bangumi': - if (path.startsWith('/season')) { - final String seasonId = path.split('/').last; - RoutePush.bangumiPush(int.parse(seasonId), null); - } - break; - case 'opus': - if (path.startsWith('/detail')) { - var opusId = path.split('/').last; - Get.toNamed('/opus', parameters: { - 'title': '', - 'id': opusId, - 'articleType': 'opus', - }); - } - break; - case 'search': - Get.toNamed('/searchResult', parameters: {'keyword': ''}); - break; - case 'article': - final String id = path.split('/').last.split('?').first; - Get.toNamed( - '/read', - parameters: { - 'title': 'cv$id', - 'id': id, - 'dynamicType': 'read', - }, - ); - break; - case 'pgc': - if (path.contains('ep')) { - final String lastPathSegment = path.split('/').last; - RoutePush.bangumiPush( - null, int.parse(lastPathSegment.split('?').first)); - } - break; - default: - SmartDialog.showToast('未匹配地址,请联系开发者'); - Clipboard.setData(ClipboardData(text: value.toJson().toString())); - break; - } + biliScheme(value); } if (scheme == 'https') { - fullPathPush(value); + httpsScheme(value); } } @@ -148,7 +79,7 @@ class PiliSchame { } } - static Future fullPathPush(SchemeEntity value) async { + static Future httpsScheme(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 // final String scheme = value.scheme!; @@ -281,6 +212,134 @@ class PiliSchame { } } + static Future biliScheme(SchemeEntity value) async { + final String host = value.host!; + final String path = value.path!; + switch (host) { + case 'root': + Navigator.popUntil( + Get.context!, (Route route) => route.isFirst); + break; + case 'space': + final String mid = path.split('/').last; + Get.toNamed( + '/member?mid=$mid', + arguments: {'face': null}, + ); + break; + case 'video': + String pathQuery = path.split('/').last; + final numericRegex = RegExp(r'^[0-9]+$'); + if (numericRegex.hasMatch(pathQuery)) { + pathQuery = 'AV$pathQuery'; + } + Map map = IdUtils.matchAvorBv(input: pathQuery); + if (map.containsKey('AV')) { + _videoPush(map['AV'], null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV']); + } else { + SmartDialog.showToast('投稿匹配失败'); + } + break; + case 'live': + final String roomId = path.split('/').last; + Get.toNamed( + '/liveRoom?roomid=$roomId', + arguments: {'liveItem': null, 'heroTag': roomId}, + ); + break; + case 'bangumi': + if (path.startsWith('/season')) { + final String seasonId = path.split('/').last; + RoutePush.bangumiPush(int.parse(seasonId), null); + } + break; + case 'opus': + if (path.startsWith('/detail')) { + var opusId = path.split('/').last; + Get.toNamed('/opus', parameters: { + 'title': '', + 'id': opusId, + 'articleType': 'opus', + }); + } + break; + case 'search': + Get.toNamed('/searchResult', parameters: {'keyword': ''}); + break; + case 'article': + final String id = path.split('/').last.split('?').first; + Get.toNamed( + '/read', + parameters: { + 'title': 'cv$id', + 'id': id, + 'dynamicType': 'read', + }, + ); + break; + case 'pgc': + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } + break; + default: + SmartDialog.showToast('未匹配地址,请联系开发者'); + Clipboard.setData(ClipboardData(text: value.toJson().toString())); + break; + } + } + + static Future piliScheme(Uri value) async { + final String host = value.host; + final String path = value.path; + final String arg = path.split('/').last; + switch (host) { + case 'home': + case 'root': + Get.toNamed('/'); + break; + case 'member': + if (arg != '') { + Get.toNamed( + '/member?mid=$arg', + arguments: {'face': null}, + ); + } else { + Get.toNamed('/mine'); + } + break; + case 'search': + if (arg != '') { + Get.toNamed('/searchResult', parameters: {'keyword': arg}); + } else { + Get.toNamed('/search'); + } + break; + case 'setting': + Get.toNamed('/setting'); + break; + case 'fav': + Get.toNamed('/fav'); + break; + case 'history': + Get.toNamed('/history'); + break; + case 'later': + Get.toNamed('/later'); + break; + case 'msg': + Get.toNamed('/whisper'); + break; + default: + Get.toNamed('/'); + break; + } + } + static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index c3b56ecd..4dd0e0b9 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 70cdeb4b..dca43a8e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color flutter_volume_controller + gtk media_kit_libs_linux media_kit_video url_launcher_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8af2f922..b7538b19 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import app_links import audio_service import audio_session import connectivity_plus @@ -22,6 +23,7 @@ import url_launcher_macos import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b9d48c78..9f2036e3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.2.0" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.2" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" appscheme: dependency: "direct main" description: @@ -686,6 +718,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.1.0" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" hive: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ac04a1b0..4af3459b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -112,6 +112,7 @@ dependencies: flutter_displaymode: ^0.6.0 # scheme跳转 appscheme: ^1.0.8 + app_links: ^6.3.2 # 弹幕 ns_danmaku: git: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d2cff3b2..f477ef65 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -17,6 +18,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 5d25e134..1db9f3fe 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links connectivity_plus dynamic_color flutter_volume_controller From d77450e1d3999f71c232a47c689d2bf3b1b91d67 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 13 Oct 2024 23:48:22 +0800 Subject: [PATCH 320/438] =?UTF-8?q?mod:=20=E3=80=8C=E6=94=B6=E5=88=B0?= =?UTF-8?q?=E7=9A=84=E8=B5=9E=E3=80=8D=E5=8F=96=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/message/like/view.dart | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index 8b061505..b30a0c4f 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -125,21 +125,29 @@ class LikeItem extends StatelessWidget { Color outline = Theme.of(context).colorScheme.outline; final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); int usersLen = item.users!.length > 3 ? 3 : item.users!.length; - final String bvid = item.item!.uri!.split('/').last; + final Uri uri = Uri.parse(item.item!.uri!); + final String path = uri.path; + final String bvid = path.split('/').last; + + /// bilibili:// + final Uri nativeUri = Uri.parse(item.item!.nativeUri!); + final Map queryParameters = nativeUri.queryParameters; + final String type = item.item!.type!; + // cid + final String? argCid = queryParameters['cid']; // 页码 - final String page = - item.item!.nativeUri!.split('page=').last.split('&').first; + final String? page = queryParameters['page']; // 根评论id - final String commentRootId = - item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + final String? commentRootId = queryParameters['comment_root_id']; // 二级评论id - final String commentSecondaryId = - item.item!.nativeUri!.split('comment_secondary_id=').last; + final String? commentSecondaryId = queryParameters['comment_secondary_id']; return InkWell( onTap: () async { try { - final int cid = await SearchHttp.ab2c(bvid: bvid); + final int cid = argCid != null + ? int.parse(argCid) + : await SearchHttp.ab2c(bvid: bvid); final String heroTag = Utils.makeHeroTag(bvid); Get.toNamed( '/video?bvid=$bvid&cid=$cid', @@ -148,8 +156,8 @@ class LikeItem extends StatelessWidget { 'heroTag': heroTag, }, ); - } catch (_) { - SmartDialog.showToast('视频可能失效了'); + } catch (e) { + SmartDialog.showToast('视频可能失效了$e'); } }, child: Stack( @@ -222,7 +230,7 @@ class LikeItem extends StatelessWidget { ), ), const SizedBox(width: 25), - if (item.item!.type! == 'reply') + if (type == 'reply' || type == 'danmu') Container( width: 60, height: 60, @@ -234,7 +242,7 @@ class LikeItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (item.item!.type! == 'video') + if (type == 'video') NetworkImgLayer( width: 60, height: 60, From 0c993bceeef9f3f4b6e158e4c6a4ea2b23ea6969 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 14 Oct 2024 00:02:12 +0800 Subject: [PATCH 321/438] fix: null error --- lib/common/widgets/video_card_v.dart | 5 +++-- lib/pages/whisper/view.dart | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 378c9f75..8cec3523 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -282,9 +282,10 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView(view: videoItem.stat.view), + if (videoItem.stat.view != null) StatView(view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu(danmu: videoItem.stat.danmu), + if (videoItem.stat.danmu != null) + StatDanMu(danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index e97aa79b..cbe4d938 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -228,7 +228,7 @@ class SessionItem extends StatelessWidget { parameters: { 'talkerId': sessionItem.talkerId.toString(), 'name': sessionItem.accountInfo.name, - 'face': sessionItem.accountInfo.face, + 'face': sessionItem.accountInfo.face ?? '', 'mid': (sessionItem.accountInfo?.mid ?? 0).toString(), 'heroTag': heroTag, }, @@ -244,7 +244,7 @@ class SessionItem extends StatelessWidget { width: 45, height: 45, type: 'avatar', - src: sessionItem.accountInfo.face, + src: sessionItem.accountInfo.face ?? '', ), ), ), From 334ce6d5b1e8b88e2f33c7380b65658ce8ef9695 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 14 Oct 2024 00:20:38 +0800 Subject: [PATCH 322/438] =?UTF-8?q?mod:=20deepLink=20=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 61c2284f..a8ad1aa0 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -304,8 +304,13 @@ class PiliSchame { break; case 'member': if (arg != '') { + final int? mid = int.tryParse(arg); + if (mid == null) { + SmartDialog.showToast('用户id有误'); + return; + } Get.toNamed( - '/member?mid=$arg', + '/member?mid=$mid', arguments: {'face': null}, ); } else { @@ -314,7 +319,8 @@ class PiliSchame { break; case 'search': if (arg != '') { - Get.toNamed('/searchResult', parameters: {'keyword': arg}); + final String encodedArg = Uri.decodeComponent(arg); + Get.toNamed('/searchResult', parameters: {'keyword': encodedArg}); } else { Get.toNamed('/search'); } From 3f9b35b56bfffbb755e6e2af10f11979f32a7124 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 15 Oct 2024 00:06:48 +0800 Subject: [PATCH 323/438] =?UTF-8?q?mod:=20ai=E6=80=BB=E7=BB=93=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 6 + lib/pages/video/detail/widgets/ai_detail.dart | 294 +++++++----------- lib/utils/global_data_cache.dart | 2 + lib/utils/utils.dart | 3 + 4 files changed, 122 insertions(+), 183 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 31edab2f..2f40414e 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -242,6 +242,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { showBottomSheet( context: context, enableDrag: true, + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(25), + topRight: Radius.circular(25), + ), + ), builder: (BuildContext context) { return AiDetail(modelResult: videoIntroController.modelResult); }, diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index b71b026d..64327553 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -1,16 +1,11 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/models/video/ai.dart'; import 'package:pilipala/pages/video/detail/index.dart'; -import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; - class AiDetail extends StatelessWidget { final ModelResult? modelResult; @@ -21,124 +16,21 @@ class AiDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); return Container( - color: Theme.of(context).colorScheme.surface, - padding: const EdgeInsets.only(left: 14, right: 14), - height: sheetHeight, + padding: const EdgeInsets.only(left: 16, right: 16), + height: GlobalDataCache().sheetHeight, child: Column( children: [ - InkWell( - onTap: () => Get.back(), - child: Container( - height: 35, - padding: const EdgeInsets.only(bottom: 2), - child: Center( - child: Container( - width: 32, - height: 3, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: const BorderRadius.all(Radius.circular(3)), - ), - ), - ), - ), - ), + _buildHeader(context), Expanded( child: SingleChildScrollView( child: Column( children: [ - if (modelResult!.resultType != 0 && - modelResult!.summary != '') ...[ - SelectableText( - modelResult!.summary!, - style: const TextStyle( - fontSize: 15, - fontWeight: FontWeight.bold, - height: 1.5, - ), - ), + if (modelResult!.summary != '') ...[ + _buildSummaryText(modelResult!.summary!), const SizedBox(height: 20), ], - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: modelResult!.outline!.length, - itemBuilder: (context, index) { - final outline = modelResult!.outline![index]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SelectableText( - outline.title!, - style: const TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - height: 1.5, - ), - ), - const SizedBox(height: 6), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemCount: outline.partOutline!.length, - itemBuilder: (context, i) { - final part = outline.partOutline![i]; - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - onTap: () { - try { - final controller = - Get.find( - tag: Get.arguments['heroTag'], - ); - controller.plPlayerController.seekTo( - Duration( - seconds: Utils.duration( - Utils.tampToSeektime( - part.timestamp!), - ).toInt(), - ), - ); - } catch (_) {} - }, - child: SelectableText.rich( - TextSpan( - style: TextStyle( - fontSize: 13, - color: Theme.of(context) - .colorScheme - .onSurface, - height: 1.5, - ), - children: [ - TextSpan( - text: Utils.tampToSeektime( - part.timestamp!), - style: TextStyle( - color: Theme.of(context) - .colorScheme - .primary, - ), - ), - const TextSpan(text: ' '), - TextSpan(text: part.content!), - ], - ), - ), - ), - const SizedBox(height: 20), - ], - ); - }, - ), - ], - ); - }, - ) + _buildOutlineList(context), ], ), ), @@ -148,77 +40,113 @@ class AiDetail extends StatelessWidget { ); } - InlineSpan buildContent(BuildContext context, content) { - List descV2 = content.descV2; - // type - // 1 普通文本 - // 2 @用户 - List spanChilds = List.generate(descV2.length, (index) { - final currentDesc = descV2[index]; - switch (currentDesc.type) { - case 1: - List spanChildren = []; - RegExp urlRegExp = RegExp(r'https?://\S+\b'); - Iterable matches = urlRegExp.allMatches(currentDesc.rawText); + Widget _buildHeader(BuildContext context) { + return Center( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).hintColor, + borderRadius: const BorderRadius.all(Radius.circular(10)), + ), + height: 4, + width: 40, + margin: const EdgeInsets.symmetric(vertical: 16), + ), + ); + } - int previousEndIndex = 0; - for (Match match in matches) { - if (match.start > previousEndIndex) { - spanChildren.add(TextSpan( - text: currentDesc.rawText - .substring(previousEndIndex, match.start))); - } - spanChildren.add( - TextSpan( - text: match.group(0), - style: TextStyle( - color: Theme.of(context).colorScheme.primary), // 设置颜色为蓝色 - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - try { - Get.toNamed( - '/webview', - parameters: { - 'url': match.group(0)!, - 'type': 'url', - 'pageTitle': match.group(0)!, - }, - ); - } catch (err) { - SmartDialog.showToast(err.toString()); - } - }, - ), - ); - previousEndIndex = match.end; - } + Widget _buildSummaryText(String summary) { + return SelectableText( + summary, + textAlign: TextAlign.justify, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + height: 1.6, + ), + ); + } - if (previousEndIndex < currentDesc.rawText.length) { - spanChildren.add(TextSpan( - text: currentDesc.rawText.substring(previousEndIndex))); - } + Widget _buildOutlineList(BuildContext context) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: modelResult!.outline!.length, + itemBuilder: (context, index) { + final outline = modelResult!.outline![index]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildOutlineTitle(outline.title!), + const SizedBox(height: 20), + _buildPartOutlineList(context, outline.partOutline!), + ], + ); + }, + ); + } - TextSpan result = TextSpan(children: spanChildren); - return result; - case 2: - final colorSchemePrimary = Theme.of(context).colorScheme.primary; - final heroTag = Utils.makeHeroTag(currentDesc.bizId); - return TextSpan( - text: '@${currentDesc.rawText}', - style: TextStyle(color: colorSchemePrimary), + Widget _buildOutlineTitle(String title) { + return SelectableText( + title, + textAlign: TextAlign.justify, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + height: 1.5, + ), + ); + } + + Widget _buildPartOutlineList( + BuildContext context, List partOutline) { + return ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: partOutline.length, + itemBuilder: (context, i) { + final part = partOutline[i]; + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _buildPartText(context, part), + const SizedBox(height: 20), + ], + ); + }, + ); + } + + void _onPartTap(BuildContext context, int timestamp) { + try { + final controller = Get.find( + tag: Get.arguments['heroTag'], + ); + controller.plPlayerController.seekTo( + Duration(seconds: timestamp), + ); + } catch (_) {} + } + + Widget _buildPartText(BuildContext context, PartOutline part) { + return SelectableText.rich( + TextSpan( + style: TextStyle( + fontSize: 15, + color: Theme.of(context).colorScheme.onSurface, + ), + children: [ + TextSpan( + text: Utils.tampToSeektime(part.timestamp!), + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), recognizer: TapGestureRecognizer() - ..onTap = () { - Get.toNamed( - '/member?mid=${currentDesc.bizId}', - arguments: {'face': '', 'heroTag': heroTag}, - ); - }, - ); - default: - return const TextSpan(); - } - }); - return TextSpan(children: spanChilds); + ..onTap = () => _onPartTap(context, part.timestamp!), + ), + const TextSpan(text: ' '), + TextSpan(text: part.content!), + ], + ), + ); } } diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart index ea2eda37..20bbea4d 100644 --- a/lib/utils/global_data_cache.dart +++ b/lib/utils/global_data_cache.dart @@ -15,6 +15,7 @@ class GlobalDataCache { late FullScreenGestureMode fullScreenGestureMode; late bool enablePlayerControlAnimation; late List actionTypeSort; + late double sheetHeight; String? wWebid; /// 播放器相关 @@ -103,5 +104,6 @@ class GlobalDataCache { speedsList.addAll(playSpeedSystem); userInfo = userInfoCache.get('userInfoCache'); + sheetHeight = localCache.get('sheetHeight'); } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 7a254f7d..c4cf7e16 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -351,6 +351,9 @@ class Utils { // 时间戳转时间 static tampToSeektime(number) { + if (number is String && int.tryParse(number) == null) { + return number; + } int hours = number ~/ 60; int minutes = number % 60; From 63a6c048e6efda70e9bb456ea6337b7374e06968 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 15 Oct 2024 00:17:46 +0800 Subject: [PATCH 324/438] =?UTF-8?q?feat:=20=E7=A8=8D=E5=90=8E=E5=86=8D?= =?UTF-8?q?=E7=9C=8B=E4=B8=8B=E6=8B=89=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/later/controller.dart | 4 +- lib/pages/later/view.dart | 121 +++++++++++++++++--------------- 2 files changed, 66 insertions(+), 59 deletions(-) diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index dd8f3c5a..e8170d0a 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -22,11 +22,11 @@ class LaterController extends GetxController { userInfo = userInfoCache.get('userInfoCache'); } - Future queryLaterList() async { + Future queryLaterList({type = 'init'}) async { if (userInfo == null) { return {'status': false, 'msg': '账号未登录', 'code': -101}; } - isLoading.value = true; + isLoading.value = type == 'init'; var res = await UserHttp.seeYouLater(); if (res['status']) { count = res['data']['count']; diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 771d8631..db82258d 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -66,67 +66,74 @@ class _LaterPageState extends State { const SizedBox(width: 8), ], ), - body: CustomScrollView( - controller: _laterController.scrollController, - slivers: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - return Obx( - () => _laterController.laterList.isNotEmpty && - !_laterController.isLoading.value - ? SliverList( - delegate: - SliverChildBuilderDelegate((context, index) { - var videoItem = _laterController.laterList[index]; - return VideoCardH( - videoItem: videoItem, - source: 'later', - onPressedFn: () => _laterController.toViewDel( - aid: videoItem.aid)); - }, childCount: _laterController.laterList.length), - ) - : _laterController.isLoading.value - ? const SliverToBoxAdapter( - child: Center(child: Text('加载中')), - ) - : const NoData(), - ); + body: RefreshIndicator( + onRefresh: () async { + await _laterController.queryLaterList(type: 'onRefresh'); + }, + child: CustomScrollView( + controller: _laterController.scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + return Obx( + () => _laterController.laterList.isNotEmpty && + !_laterController.isLoading.value + ? SliverList( + delegate: + SliverChildBuilderDelegate((context, index) { + var videoItem = + _laterController.laterList[index]; + return VideoCardH( + videoItem: videoItem, + source: 'later', + onPressedFn: () => _laterController + .toViewDel(aid: videoItem.aid)); + }, childCount: _laterController.laterList.length), + ) + : _laterController.isLoading.value + ? const SliverToBoxAdapter( + child: Center(child: Text('加载中')), + ) + : const NoData(), + ); + } else { + return HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _laterController.queryLaterList(); + }); + } + }, + ); + } } else { - return HttpError( - errMsg: data?['msg'] ?? '请求异常', - btnText: data?['code'] == -101 ? '去登录' : null, - fn: () { - if (data?['code'] == -101) { - RoutePush.loginRedirectPush(); - } else { - setState(() { - _futureBuilderFuture = - _laterController.queryLaterList(); - }); - } - }, + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), ); } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const VideoCardHSkeleton(); - }, childCount: 10), - ); - } - }, - ), - SliverToBoxAdapter( - child: SizedBox( - height: MediaQuery.of(context).padding.bottom + 10, + }, ), - ) - ], + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).padding.bottom + 10, + ), + ) + ], + ), ), floatingActionButton: Obx( () => _laterController.laterList.isNotEmpty From dc8f034df74e88937602943a98928f99ce27c29b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 15 Oct 2024 00:24:31 +0800 Subject: [PATCH 325/438] typo --- lib/http/msg.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 6426a6f2..5568e226 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -278,10 +278,10 @@ class MsgHttp { 'data': MessageLikeModel.fromJson(res.data['data']), }; } catch (err) { - return {'status': false, 'date': [], 'msg': err.toString()}; + return {'status': false, 'data': [], 'msg': err.toString()}; } } else { - return {'status': false, 'date': [], 'msg': res.data['message']}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } From eb2a51e5e2f5be5aef7bfe99fed037a934b8a948 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 16 Oct 2024 00:52:10 +0800 Subject: [PATCH 326/438] =?UTF-8?q?opt:=20=E5=BF=AB=E8=BF=9B=E5=BF=AB?= =?UTF-8?q?=E9=80=80=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/panels/seek_panel.dart | 76 ++++++++++ lib/plugin/pl_player/view.dart | 132 +++++------------- .../pl_player/widgets/backward_seek.dart | 91 ------------ .../pl_player/widgets/forward_seek.dart | 91 ------------ .../pl_player/widgets/seek_indicator.dart | 94 +++++++++++++ 5 files changed, 205 insertions(+), 279 deletions(-) create mode 100644 lib/plugin/pl_player/panels/seek_panel.dart delete mode 100644 lib/plugin/pl_player/widgets/backward_seek.dart delete mode 100644 lib/plugin/pl_player/widgets/forward_seek.dart create mode 100644 lib/plugin/pl_player/widgets/seek_indicator.dart diff --git a/lib/plugin/pl_player/panels/seek_panel.dart b/lib/plugin/pl_player/panels/seek_panel.dart new file mode 100644 index 00000000..0d5e7c9e --- /dev/null +++ b/lib/plugin/pl_player/panels/seek_panel.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../widgets/seek_indicator.dart'; + +class SeekPanel extends StatelessWidget { + const SeekPanel({ + required this.mountSeekBackwardButton, + required this.mountSeekForwardButton, + required this.hideSeekBackwardButton, + required this.hideSeekForwardButton, + required this.onSubmittedcb, + Key? key, + }) : super(key: key); + + final RxBool mountSeekBackwardButton; + final RxBool mountSeekForwardButton; + final RxBool hideSeekBackwardButton; + final RxBool hideSeekForwardButton; + final void Function(String, Duration) onSubmittedcb; + + @override + Widget build(BuildContext context) { + return Obx( + () => Visibility( + visible: mountSeekBackwardButton.value || mountSeekForwardButton.value, + child: Positioned.fill( + child: Row( + children: [ + _buildSeekIndicator( + mountSeekBackwardButton, + hideSeekBackwardButton, + 'backward', + SeekIndicator( + direction: SeekDirection.backward, + onSubmitted: (Duration value) { + onSubmittedcb.call('backward', value); + }, + ), + ), + Expanded(child: Container()), + _buildSeekIndicator( + mountSeekForwardButton, + hideSeekForwardButton, + 'forward', + SeekIndicator( + direction: SeekDirection.forward, + onSubmitted: (Duration value) { + onSubmittedcb.call('forward', value); + }, + ), + ), + ], + ), + ), + ), + ); + } + + Widget _buildSeekIndicator( + RxBool mountSeekButton, + RxBool hideSeekButton, + String direction, + Widget seekIndicator, + ) { + return Expanded( + child: mountSeekButton.value + ? AnimatedOpacity( + opacity: hideSeekButton.value ? 0.0 : 1.0, + duration: const Duration(milliseconds: 400), + curve: Curves.easeInOut, + child: seekIndicator, + ) + : const SizedBox.shrink(), + ); + } +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 6b57a8e1..a88bc86f 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -22,12 +22,11 @@ import 'package:screen_brightness/screen_brightness.dart'; import '../../utils/global_data_cache.dart'; import 'models/bottom_control_type.dart'; import 'models/bottom_progress_behavior.dart'; +import 'panels/seek_panel.dart'; 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'; class PLVideoPlayer extends StatefulWidget { @@ -69,8 +68,8 @@ class _PLVideoPlayerState extends State final RxBool _mountSeekBackwardButton = false.obs; final RxBool _mountSeekForwardButton = false.obs; - final RxBool _hideSeekBackwardButton = false.obs; - final RxBool _hideSeekForwardButton = false.obs; + final RxBool _hideSeekBackwardButton = true.obs; + final RxBool _hideSeekForwardButton = true.obs; final RxDouble _brightnessValue = 0.0.obs; final RxBool _brightnessIndicator = false.obs; @@ -97,10 +96,12 @@ class _PLVideoPlayerState extends State void onDoubleTapSeekBackward() { _mountSeekBackwardButton.value = true; + _hideSeekBackwardButton.value = false; } void onDoubleTapSeekForward() { _mountSeekForwardButton.value = true; + _hideSeekForwardButton.value = false; } // 双击播放、暂停 @@ -375,6 +376,29 @@ class _PLVideoPlayerState extends State return list; } + void _handleSubmittedCallback(String type, Duration value) { + final PlPlayerController _ = widget.controller; + final Player player = + _.videoPlayerController ?? widget.controller.videoPlayerController!; + late Duration result; + + switch (type) { + case 'backward': + _hideSeekBackwardButton.value = true; + result = player.state.position - value; + break; + case 'forward': + _hideSeekForwardButton.value = true; + result = player.state.position + value; + break; + } + _mountSeekBackwardButton.value = false; + _mountSeekForwardButton.value = false; + result = result.clamp(Duration.zero, player.state.duration); + player.seek(result); + _.play(); + } + @override Widget build(BuildContext context) { final PlPlayerController _ = widget.controller; @@ -865,99 +889,13 @@ class _PLVideoPlayerState extends State } }), - /// 点击 快进/快退 - Obx( - () => Visibility( - visible: - _mountSeekBackwardButton.value || _mountSeekForwardButton.value, - child: Positioned.fill( - child: Row( - children: [ - Expanded( - child: _mountSeekBackwardButton.value - ? TweenAnimationBuilder( - tween: Tween( - begin: 0.0, - end: _hideSeekBackwardButton.value ? 0.0 : 1.0, - ), - duration: const Duration(milliseconds: 150), - builder: (BuildContext context, double value, - Widget? child) => - Opacity( - opacity: value, - child: child, - ), - onEnd: () { - if (_hideSeekBackwardButton.value) { - _hideSeekBackwardButton.value = false; - _mountSeekBackwardButton.value = false; - } - }, - child: BackwardSeekIndicator( - onChanged: (Duration value) => {}, - onSubmitted: (Duration value) { - _hideSeekBackwardButton.value = true; - final Player player = - widget.controller.videoPlayerController!; - Duration result = player.state.position - value; - result = result.clamp( - Duration.zero, - player.state.duration, - ); - player.seek(result); - widget.controller.play(); - }, - ), - ) - : const SizedBox(), - ), - Expanded( - child: SizedBox( - width: MediaQuery.sizeOf(context).width / 4, - ), - ), - Expanded( - child: _mountSeekForwardButton.value - ? TweenAnimationBuilder( - tween: Tween( - begin: 0.0, - end: _hideSeekForwardButton.value ? 0.0 : 1.0, - ), - duration: const Duration(milliseconds: 150), - builder: (BuildContext context, double value, - Widget? child) => - Opacity( - opacity: value, - child: child, - ), - onEnd: () { - if (_hideSeekForwardButton.value) { - _hideSeekForwardButton.value = false; - _mountSeekForwardButton.value = false; - } - }, - child: ForwardSeekIndicator( - onChanged: (Duration value) => {}, - onSubmitted: (Duration value) { - _hideSeekForwardButton.value = true; - final Player player = - widget.controller.videoPlayerController!; - Duration result = player.state.position + value; - result = result.clamp( - Duration.zero, - player.state.duration, - ); - player.seek(result); - widget.controller.play(); - }, - ), - ) - : const SizedBox(), - ), - ], - ), - ), - ), + /// 快进/快退面板 + SeekPanel( + mountSeekBackwardButton: _mountSeekBackwardButton, + mountSeekForwardButton: _mountSeekForwardButton, + hideSeekBackwardButton: _hideSeekBackwardButton, + hideSeekForwardButton: _hideSeekForwardButton, + onSubmittedcb: _handleSubmittedCallback, ), ], ); diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart deleted file mode 100644 index 8289d77c..00000000 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -class BackwardSeekIndicator extends StatefulWidget { - final void Function(Duration) onChanged; - final void Function(Duration) onSubmitted; - const BackwardSeekIndicator({ - Key? key, - required this.onChanged, - required this.onSubmitted, - }) : super(key: key); - - @override - State createState() => BackwardSeekIndicatorState(); -} - -class BackwardSeekIndicatorState extends State { - Duration value = const Duration(seconds: 10); - - Timer? timer; - - @override - void setState(VoidCallback fn) { - if (mounted) { - super.setState(fn); - } - } - - @override - void initState() { - super.initState(); - timer = Timer(const Duration(milliseconds: 200), () { - widget.onSubmitted.call(value); - }); - } - - void increment() { - timer?.cancel(); - timer = Timer(const Duration(milliseconds: 200), () { - widget.onSubmitted.call(value); - }); - widget.onChanged.call(value); - // 重复点击 快退秒数累加10 - setState(() { - value += const Duration(seconds: 10); - }); - } - - @override - Widget build(BuildContext context) { - return Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [ - Color(0x88767676), - Color(0x00767676), - ], - begin: Alignment.centerLeft, - end: Alignment.centerRight, - ), - ), - child: InkWell( - splashColor: const Color(0x44767676), - onTap: increment, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Icon( - Icons.fast_rewind, - size: 24.0, - color: Color(0xFFFFFFFF), - ), - const SizedBox(height: 8.0), - Text( - '快退${value.inSeconds}秒', - style: const TextStyle( - fontSize: 12.0, - color: Color(0xFFFFFFFF), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart deleted file mode 100644 index 3f68fe0d..00000000 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; - -class ForwardSeekIndicator extends StatefulWidget { - final void Function(Duration) onChanged; - final void Function(Duration) onSubmitted; - const ForwardSeekIndicator({ - Key? key, - required this.onChanged, - required this.onSubmitted, - }) : super(key: key); - - @override - State createState() => ForwardSeekIndicatorState(); -} - -class ForwardSeekIndicatorState extends State { - Duration value = const Duration(seconds: 10); - - Timer? timer; - - @override - void setState(VoidCallback fn) { - if (mounted) { - super.setState(fn); - } - } - - @override - void initState() { - super.initState(); - timer = Timer(const Duration(milliseconds: 200), () { - widget.onSubmitted.call(value); - }); - } - - void increment() { - timer?.cancel(); - timer = Timer(const Duration(milliseconds: 200), () { - widget.onSubmitted.call(value); - }); - widget.onChanged.call(value); - // 重复点击 快进秒数累加10 - setState(() { - value += const Duration(seconds: 10); - }); - } - - @override - Widget build(BuildContext context) { - return Container( - decoration: const BoxDecoration( - gradient: LinearGradient( - colors: [ - Color(0x00767676), - Color(0x88767676), - ], - begin: Alignment.centerLeft, - end: Alignment.centerRight, - ), - ), - child: InkWell( - splashColor: const Color(0x44767676), - onTap: increment, - child: Center( - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - const Icon( - Icons.fast_forward, - size: 24.0, - color: Color(0xFFFFFFFF), - ), - const SizedBox(height: 8.0), - Text( - '快进${value.inSeconds}秒', - style: const TextStyle( - fontSize: 12.0, - color: Color(0xFFFFFFFF), - ), - ), - ], - ), - ), - ), - ); - } -} diff --git a/lib/plugin/pl_player/widgets/seek_indicator.dart b/lib/plugin/pl_player/widgets/seek_indicator.dart new file mode 100644 index 00000000..45fa5a16 --- /dev/null +++ b/lib/plugin/pl_player/widgets/seek_indicator.dart @@ -0,0 +1,94 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; + +enum SeekDirection { forward, backward } + +class SeekIndicator extends StatefulWidget { + final SeekDirection direction; + final void Function(Duration) onSubmitted; + + const SeekIndicator({ + Key? key, + required this.direction, + required this.onSubmitted, + }) : super(key: key); + + @override + State createState() => _SeekIndicatorState(); +} + +class _SeekIndicatorState extends State { + Timer? timer; + + @override + void initState() { + super.initState(); + _startTimer(); + } + + void _startTimer() { + timer?.cancel(); + timer = Timer(const Duration(milliseconds: 400), () { + widget.onSubmitted.call(const Duration(seconds: 10)); + timer = null; + }); + } + + @override + void dispose() { + timer?.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + gradient: LinearGradient( + colors: widget.direction == SeekDirection.forward + ? [ + const Color(0x00767676), + const Color(0x88767676), + ] + : [ + const Color(0x88767676), + const Color(0x00767676), + ], + begin: Alignment.centerLeft, + end: Alignment.centerRight, + ), + ), + child: Center( + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Icon( + widget.direction == SeekDirection.forward + ? Icons.fast_forward + : Icons.fast_rewind, + size: 24.0, + color: const Color(0xFFFFFFFF), + ), + const SizedBox(height: 8.0), + Text( + widget.direction == SeekDirection.forward ? '快进10秒' : '快退10秒', + style: const TextStyle( + fontSize: 12.0, + color: Color(0xFFFFFFFF), + shadows: [ + Shadow( + color: Color(0xFF000000), + offset: Offset(0, 2), + blurRadius: 4, + ), + ], + ), + ), + ], + ), + ), + ); + } +} From 174eff71510e259101a6d3e773ddf2da4efb3ba4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 16 Oct 2024 14:17:24 +0800 Subject: [PATCH 327/438] Merge branch 'main' into feature-minePage --- .github/workflows/beta_ci.yml | 3 +- .github/workflows/release_ci.yml | 4 +- android/app/src/main/AndroidManifest.xml | 14 +- change_log/1.0.25.1010.md | 39 ++ ios/Podfile.lock | 6 + ios/Runner/Info.plist | 49 +- lib/common/widgets/network_img_layer.dart | 6 +- lib/common/widgets/video_card_v.dart | 5 +- lib/http/api.dart | 3 + lib/http/init.dart | 14 +- lib/http/member.dart | 31 +- lib/http/reply.dart | 21 + lib/http/video.dart | 3 +- lib/main.dart | 1 + lib/models/common/gesture_mode.dart | 7 +- lib/models/common/search_type.dart | 20 +- lib/models/video/later.dart | 12 +- lib/pages/about/index.dart | 4 +- lib/pages/bangumi/introduction/view.dart | 6 +- lib/pages/bangumi/view.dart | 8 +- lib/pages/bangumi/widgets/bangumi_panel.dart | 8 +- lib/pages/bangumi/widgets/bangumu_card_v.dart | 2 +- lib/pages/home/controller.dart | 2 +- lib/pages/live_room/controller.dart | 2 +- lib/pages/live_room/view.dart | 214 +++--- lib/pages/main/controller.dart | 5 +- lib/pages/main/view.dart | 40 +- lib/pages/member/controller.dart | 118 ++-- lib/pages/member/view.dart | 651 ++++++++---------- lib/pages/member/widgets/commen_widget.dart | 24 + lib/pages/member/widgets/profile.dart | 476 +++++++------ lib/pages/member_article/controller.dart | 16 - lib/pages/message/like/view.dart | 32 +- lib/pages/opus/view.dart | 2 +- lib/pages/read/controller.dart | 2 +- lib/pages/read/view.dart | 11 +- lib/pages/search_panel/controller.dart | 4 +- lib/pages/search_panel/view.dart | 13 +- .../search_panel/widgets/article_panel.dart | 317 ++++++--- lib/pages/setting/pages/play_gesture_set.dart | 4 +- lib/pages/video/detail/controller.dart | 1 - .../video/detail/introduction/controller.dart | 74 +- lib/pages/video/detail/introduction/view.dart | 169 +---- lib/pages/video/detail/reply/view.dart | 12 +- .../detail/reply/widgets/reply_item.dart | 122 +++- lib/pages/video/detail/view.dart | 64 +- .../detail/widgets/watch_later_list.dart | 4 +- lib/pages/whisper/view.dart | 4 +- lib/plugin/pl_player/controller.dart | 8 + lib/plugin/pl_player/view.dart | 16 +- lib/utils/app_scheme.dart | 244 ++++--- lib/utils/cache_manage.dart | 77 ++- lib/utils/global_data_cache.dart | 3 +- lib/utils/subtitle.dart | 56 +- lib/utils/utils.dart | 2 +- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 40 ++ pubspec.yaml | 3 +- .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 62 files changed, 1738 insertions(+), 1371 deletions(-) create mode 100644 change_log/1.0.25.1010.md create mode 100644 lib/pages/member/widgets/commen_widget.dart diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index 14b51780..9c40de6b 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -12,7 +12,6 @@ on: - ".idea/**" - "!.github/workflows/**" - jobs: update_version: name: Read and update version @@ -96,7 +95,7 @@ jobs: if: steps.cache-flutter.outputs.cache-hit != 'true' uses: subosito/flutter-action@v2 with: - flutter-version: 3.16.5 + flutter-version: 3.19.6 channel: any - name: 下载项目依赖 diff --git a/.github/workflows/release_ci.yml b/.github/workflows/release_ci.yml index 78230645..f7c06d29 100644 --- a/.github/workflows/release_ci.yml +++ b/.github/workflows/release_ci.yml @@ -36,7 +36,7 @@ jobs: if: steps.cache-flutter.outputs.cache-hit != 'true' uses: subosito/flutter-action@v2 with: - flutter-version: 3.16.5 + flutter-version: 3.19.6 channel: any - name: 下载项目依赖 @@ -98,7 +98,7 @@ jobs: uses: subosito/flutter-action@v2.10.0 with: cache: true - flutter-version: 3.16.5 + flutter-version: 3.19.6 - name: flutter build ipa run: | diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f119eb1e..c948bc8f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -47,13 +47,14 @@ + + + + + + + + + diff --git a/change_log/1.0.25.1010.md b/change_log/1.0.25.1010.md new file mode 100644 index 00000000..951efcb1 --- /dev/null +++ b/change_log/1.0.25.1010.md @@ -0,0 +1,39 @@ +## 1.0.25 + +### 功能 ++ 直播弹幕 ++ 稍后再看、收藏夹播放全部 ++ 收藏夹新建、编辑 ++ 评论删除 ++ 评论保存为图片 ++ 动态页滑动切换up ++ up投稿筛选充电视频 ++ 直播tab展示关注up ++ up主页专栏展示 + +### 优化 ++ 视频详情页一键三连 ++ 动态页标识充电视频 ++ 播放器亮度、音量调整百分比展示 ++ 封面预览时视频标题可复制 ++ 竖屏直播布局 ++ 图片预览 ++ 专栏渲染优化 ++ 私信图片查看 + +### 修复 ++ 收藏夹点击异常 ++ 搜索up异常 ++ 系统通知已读异常 ++ [赞了我的]展示错误 ++ 部分up合集无法打开 ++ 切换合集视频投币个数未重置 ++ 搜索条件筛选面板无法滚动 ++ 部分机型导航条未沉浸 ++ 专栏图片渲染问题 ++ 专栏浏览历史记录 ++ 直播间历史记录 + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a400600f..040c0297 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,4 +1,6 @@ PODS: + - app_links (0.0.2): + - Flutter - appscheme (1.0.4): - Flutter - audio_service (0.0.1): @@ -66,6 +68,7 @@ PODS: - Flutter DEPENDENCIES: + - app_links (from `.symlinks/plugins/app_links/ios`) - appscheme (from `.symlinks/plugins/appscheme/ios`) - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) @@ -102,6 +105,8 @@ SPEC REPOS: - Toast EXTERNAL SOURCES: + app_links: + :path: ".symlinks/plugins/app_links/ios" appscheme: :path: ".symlinks/plugins/appscheme/ios" audio_service: @@ -160,6 +165,7 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/webview_flutter_wkwebview/ios" SPEC CHECKSUMS: + app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0 appscheme: b1c3f8862331cb20430cf9e0e4af85dbc1572ad8 audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 1e7d9fed..24dceb17 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -65,44 +65,29 @@ CFBundleURLName - + bilibili CFBundleURLSchemes http https - - CFBundleURLTypes - - - CFBundleURLName - - CFBundleURLSchemes - - m.bilibili.com - bilibili.com - www.bilibili.com - bangumi.bilibili.com - bilibili.cn - www.bilibili.cn - bangumi.bilibili.cn - bilibili.tv - www.bilibili.tv - bangumi.bilibili.tv - miniapp.bilibili.com - live.bilibili.com - - - - - - - - CFBundleURLName - bilibili - CFBundleURLSchemes - bilibili + m.bilibili.com + bilibili.com + www.bilibili.com + bangumi.bilibili.com + bilibili.cn + www.bilibili.cn + bangumi.bilibili.cn + bilibili.tv + www.bilibili.tv + bangumi.bilibili.tv + miniapp.bilibili.com + live.bilibili.com + pili + pilipala + FlutterDeepLinkingEnabled + UIBackgroundModes diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 0b715a89..fbedfbba 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -33,7 +33,11 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { - final int defaultImgQuality = GlobalDataCache().imgQuality; + int defaultImgQuality = 10; + try { + defaultImgQuality = GlobalDataCache().imgQuality; + } catch (_) {} + if (src == '' || src == null) { return placeholder(context); } diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 378c9f75..8cec3523 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -282,9 +282,10 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView(view: videoItem.stat.view), + if (videoItem.stat.view != null) StatView(view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu(danmu: videoItem.stat.danmu), + if (videoItem.stat.danmu != null) + StatDanMu(danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( diff --git a/lib/http/api.dart b/lib/http/api.dart index 0ad15f8f..e2e24b12 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -598,4 +598,7 @@ class Api { /// 更新用户信息 static const String updateAccountInfo = '/x/member/web/update'; + + /// 删除评论 + static const String replyDel = '/x/v2/reply/del'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index eae94ae4..abe8d019 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -217,12 +217,13 @@ class Request { /* * get请求 */ - get(url, {data, options, cancelToken, extra}) async { + get(url, {data, Options? options, cancelToken, extra}) async { Response response; - final Options options = Options(); + options ??= Options(); // 如果 options 为 null,则初始化一个新的 Options 对象 ResponseType resType = ResponseType.json; + if (extra != null) { - resType = extra!['resType'] ?? ResponseType.json; + resType = extra['resType'] ?? ResponseType.json; if (extra['ua'] != null) { options.headers = {'user-agent': headerUa(type: extra['ua'])}; } @@ -238,14 +239,11 @@ class Request { ); return response; } on DioException catch (e) { - Response errResponse = Response( - data: { - 'message': await ApiInterceptor.dioError(e) - }, // 将自定义 Map 数据赋值给 Response 的 data 属性 + return Response( + data: {'message': await ApiInterceptor.dioError(e)}, statusCode: 200, requestOptions: RequestOptions(), ); - return errResponse; } } diff --git a/lib/http/member.dart b/lib/http/member.dart index c7b22359..fc99c987 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -4,6 +4,7 @@ import 'package:hive/hive.dart'; import 'package:html/parser.dart'; import 'package:pilipala/models/member/article.dart'; import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import '../common/constants.dart'; import '../models/dynamics/result.dart'; import '../models/follow/result.dart'; @@ -19,14 +20,20 @@ import 'index.dart'; class MemberHttp { static Future memberInfo({ - int? mid, + required int mid, String token = '', }) async { + String? wWebid; + if ((await getWWebid(mid: mid))['status']) { + wWebid = GlobalDataCache().wWebid; + } + Map params = await WbiSign().makSign({ 'mid': mid, 'token': token, 'platform': 'web', 'web_location': 1550101, + ...wWebid != null ? {'w_webid': wWebid} : {}, }); var res = await Request().get( Api.memberInfo, @@ -566,6 +573,10 @@ class MemberHttp { } static Future getWWebid({required int mid}) async { + String? wWebid = GlobalDataCache().wWebid; + if (wWebid != null) { + return {'status': true, 'data': wWebid}; + } var res = await Request().get('https://space.bilibili.com/$mid/article'); String? headContent = parse(res.data).head?.outerHtml; final regex = RegExp( @@ -576,6 +587,7 @@ class MemberHttp { final content = match.group(1); String decodedString = Uri.decodeComponent(content!); Map map = jsonDecode(decodedString); + GlobalDataCache().wWebid = map['access_id']; return {'status': true, 'data': map['access_id']}; } else { return {'status': false, 'data': '请检查登录状态'}; @@ -588,25 +600,20 @@ class MemberHttp { static Future getMemberArticle({ required int mid, required int pn, - required String wWebid, String? offset, }) async { + String? wWebid; + if ((await getWWebid(mid: mid))['status']) { + wWebid = GlobalDataCache().wWebid; + } 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'], + ...wWebid != null ? {'w_webid': wWebid} : {}, }); + var res = await Request().get(Api.opusList, data: params); if (res.data['code'] == 0) { return { 'status': true, diff --git a/lib/http/reply.dart b/lib/http/reply.dart index fc00f06b..c07d9e81 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -115,4 +115,25 @@ class ReplyHttp { }; } } + + static Future replyDel({ + required int type, //replyType + required int oid, + required int rpid, + }) async { + var res = await Request().post( + Api.replyDel, + queryParameters: { + 'type': type, //type.index + 'oid': oid, + 'rpid': rpid, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true, 'msg': '删除成功'}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/http/video.dart b/lib/http/video.dart index 95ea6782..406e59f6 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -536,7 +536,8 @@ class VideoHttp { // 获取字幕内容 static Future> getSubtitleContent(url) async { var res = await Request().get('https:$url'); - final String content = SubTitleUtils.convertToWebVTT(res.data['body']); + final String content = + await SubTitleUtils.convertToWebVTT(res.data['body']); final List body = res.data['body']; return {'content': content, 'body': body}; } diff --git a/lib/main.dart b/lib/main.dart index fe93da22..3ac97b25 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -60,6 +60,7 @@ void main() async { systemNavigationBarColor: Colors.transparent, systemNavigationBarDividerColor: Colors.transparent, statusBarColor: Colors.transparent, + systemNavigationBarContrastEnforced: false, )); } diff --git a/lib/models/common/gesture_mode.dart b/lib/models/common/gesture_mode.dart index 1149ae12..bf51fd96 100644 --- a/lib/models/common/gesture_mode.dart +++ b/lib/models/common/gesture_mode.dart @@ -4,9 +4,12 @@ enum FullScreenGestureMode { /// 从下滑到上 fromBottomtoTop, + + /// 关闭手势 + none, } extension FullScreenGestureModeExtension on FullScreenGestureMode { - String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; - String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; + String get values => ['fromToptoBottom', 'fromBottomtoTop', 'none'][index]; + String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏', '关闭手势'][index]; } diff --git a/lib/models/common/search_type.dart b/lib/models/common/search_type.dart index d7d13aec..843e7954 100644 --- a/lib/models/common/search_type.dart +++ b/lib/models/common/search_type.dart @@ -28,7 +28,7 @@ extension SearchTypeExtension on SearchType { String get label => ['视频', '番剧', '直播间', '用户', '专栏'][index]; } -// 搜索类型为视频、专栏及相簿时 +// 搜索类型为视频时 enum ArchiveFilterType { totalrank, click, @@ -44,3 +44,21 @@ extension ArchiveFilterTypeExtension on ArchiveFilterType { String get description => ['默认排序', '播放多', '新发布', '弹幕多', '收藏多', '评论多', '最多喜欢'][index]; } + +// 搜索类型为专栏时 +enum ArticleFilterType { + // 综合排序 + totalrank, + // 最新发布 + pubdate, + // 最多点击 + click, + // 最多喜欢 + attention, + // 最多评论 + scores, +} + +extension ArticleFilterTypeExtension on ArticleFilterType { + String get description => ['综合排序', '最新发布', '最多点击', '最多喜欢', '最多评论'][index]; +} diff --git a/lib/models/video/later.dart b/lib/models/video/later.dart index f722a231..7829e1c7 100644 --- a/lib/models/video/later.dart +++ b/lib/models/video/later.dart @@ -1,6 +1,7 @@ class MediaVideoItemModel { MediaVideoItemModel({ this.id, + this.aid, this.offset, this.index, this.intro, @@ -14,12 +15,13 @@ class MediaVideoItemModel { this.likeState, this.favState, this.page, + this.cid, this.pages, this.title, this.type, this.upper, this.link, - this.bvId, + this.bvid, this.shortLink, this.rights, this.elecInfo, @@ -32,6 +34,7 @@ class MediaVideoItemModel { }); int? id; + int? aid; int? offset; int? index; String? intro; @@ -45,12 +48,13 @@ class MediaVideoItemModel { int? likeState; int? favState; int? page; + int? cid; List? pages; String? title; int? type; Upper? upper; String? link; - String? bvId; + String? bvid; String? shortLink; Rights? rights; dynamic elecInfo; @@ -64,6 +68,7 @@ class MediaVideoItemModel { factory MediaVideoItemModel.fromJson(Map json) => MediaVideoItemModel( id: json["id"], + aid: json["id"], offset: json["offset"], index: json["index"], intro: json["intro"], @@ -77,6 +82,7 @@ class MediaVideoItemModel { likeState: json["like_state"], favState: json["fav_state"], page: json["page"], + cid: json["pages"] == null ? -1 : json["pages"].first['id'], // json["pages"] 可能为null pages: json["pages"] == null ? [] @@ -85,7 +91,7 @@ class MediaVideoItemModel { type: json["type"], upper: Upper.fromJson(json["upper"]), link: json["link"], - bvId: json["bv_id"], + bvid: json["bv_id"], shortLink: json["short_link"], rights: Rights.fromJson(json["rights"]), elecInfo: json["elec_info"], diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 81d3c3f4..9164d4e9 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -295,7 +295,7 @@ class AboutController extends GetxController { displayTime: const Duration(milliseconds: 500), ).then( (value) => launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'), mode: LaunchMode.externalApplication, ), ); @@ -349,7 +349,7 @@ class AboutController extends GetxController { // 官网 webSiteUrl() { launchUrl( - Uri.parse('https://pilipalanet.mysxl.cn'), + Uri.parse('https://pilipala.life'), mode: LaunchMode.externalApplication, ); } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 94ee24de..188debef 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -189,8 +189,8 @@ class _BangumiInfoState extends State { Stack( children: [ NetworkImgLayer( - width: 105, - height: 160, + width: 115, + height: 115 / 0.75, src: widget.bangumiDetail!.cover!, ), PBadge( @@ -208,7 +208,7 @@ class _BangumiInfoState extends State { child: InkWell( onTap: () => showIntroDetail(), child: SizedBox( - height: 158, + height: 115 / 0.75, child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 3adfdc1f..9ec72350 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -96,7 +96,8 @@ class _BangumiPageState extends State ), ), SizedBox( - height: 268, + height: Get.size.width / 3 / 0.75 + + MediaQuery.textScalerOf(context).scale(50.0), child: FutureBuilder( future: _futureBuilderFutureFollow, builder: @@ -117,7 +118,6 @@ class _BangumiPageState extends State itemBuilder: (context, index) { return Container( width: Get.size.width / 3, - height: 254, margin: EdgeInsets.only( left: StyleString.safeSpace, right: index == @@ -208,8 +208,8 @@ class _BangumiPageState extends State crossAxisSpacing: StyleString.cardSpace, // 列数 crossAxisCount: 3, - mainAxisExtent: Get.size.width / 3 / 0.65 + - MediaQuery.textScalerOf(context).scale(32.0), + mainAxisExtent: Get.size.width / 3 / 0.75 + + MediaQuery.textScalerOf(context).scale(42.0), ), delegate: SliverChildBuilderDelegate( (BuildContext context, int index) { diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3df7ce25..3a589db6 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -86,9 +86,11 @@ class _BangumiPanelState extends State { item.aid, item.cover, ); - if (_bottomSheetController != null) { - _bottomSheetController?.close(); - } + try { + if (_bottomSheetController != null) { + _bottomSheetController?.close(); + } + } catch (_) {} currentIndex.value = i; scrollToIndex(); } diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index a1d7a931..10d95a1c 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -37,7 +37,7 @@ class BangumiCardV extends StatelessWidget { StyleString.imgRadius, ), child: AspectRatio( - aspectRatio: 0.65, + aspectRatio: 0.75, child: LayoutBuilder(builder: (context, boxConstraints) { final double maxWidth = boxConstraints.maxWidth; final double maxHeight = boxConstraints.maxHeight; diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index f197fdfa..ea6e72ba 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -8,7 +8,7 @@ import 'package:pilipala/utils/storage.dart'; import '../../http/index.dart'; class HomeController extends GetxController with GetTickerProviderStateMixin { - bool flag = false; + bool flag = true; late RxList tabs = [].obs; RxInt initialIndex = 1.obs; late TabController tabController; diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 34379f64..0d347bf2 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -64,7 +64,7 @@ class LiveRoomController extends GetxController { ? liveItem.pic : (liveItem.cover != null && liveItem.cover != '') ? liveItem.cover - : null; + : ''; } Request.getBuvid().then((value) => buvid = value); } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index cff0c410..1e0814e9 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -108,6 +108,12 @@ class _LiveRoomPageState extends State @override Widget build(BuildContext context) { + final mediaQuery = MediaQuery.of(context); + final isPortrait = mediaQuery.orientation == Orientation.portrait; + final isLandscape = mediaQuery.orientation == Orientation.landscape; + + final padding = mediaQuery.padding; + Widget videoPlayerPanel = FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { @@ -187,10 +193,8 @@ class _LiveRoomPageState extends State children: [ Obx( () => SizedBox( - height: MediaQuery.of(context).padding.top + - (_liveRoomController.isPortrait.value || - MediaQuery.of(context).orientation == - Orientation.landscape + height: padding.top + + (_liveRoomController.isPortrait.value || isLandscape ? 0 : kToolbarHeight), ), @@ -201,21 +205,18 @@ class _LiveRoomPageState extends State if (plPlayerController.isFullScreen.value == true) { plPlayerController.triggerFullScreen(status: false); } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { + if (isLandscape) { verticalScreen(); } }, child: Obx( () => Container( width: Get.size.width, - height: MediaQuery.of(context).orientation == - Orientation.landscape + height: isLandscape ? Get.size.height : !_liveRoomController.isPortrait.value ? Get.size.width * 9 / 16 - : Get.size.height - - MediaQuery.of(context).padding.top, + : Get.size.height - padding.top, clipBehavior: Clip.hardEdge, decoration: const BoxDecoration( borderRadius: BorderRadius.all(Radius.circular(6)), @@ -229,7 +230,7 @@ class _LiveRoomPageState extends State // 定位 快速滑动到底部 Positioned( right: 20, - bottom: MediaQuery.of(context).padding.bottom + 80, + bottom: padding.bottom + 80, child: SlideTransition( position: Tween( begin: const Offset(0, 4), @@ -262,10 +263,7 @@ class _LiveRoomPageState extends State titleSpacing: 0, backgroundColor: Colors.transparent, foregroundColor: Colors.white, - toolbarHeight: - MediaQuery.of(context).orientation == Orientation.portrait - ? 56 - : 0, + toolbarHeight: isPortrait ? 56 : 0, title: FutureBuilder( future: _futureBuilder, builder: (context, snapshot) { @@ -317,35 +315,38 @@ class _LiveRoomPageState extends State ), // 消息列表 Obx( - () => Positioned( - top: MediaQuery.of(context).padding.top + - kToolbarHeight + - (_liveRoomController.isPortrait.value - ? Get.size.width - : Get.size.width * 9 / 16), - bottom: 90 + MediaQuery.of(context).padding.bottom, - left: 0, - right: 0, - child: buildMessageListUI( - context, - _liveRoomController, - _scrollController, + () => Align( + alignment: Alignment.bottomCenter, + child: Container( + margin: EdgeInsets.only( + bottom: 90 + padding.bottom, + ), + height: Get.size.height - + (padding.top + + kToolbarHeight + + (_liveRoomController.isPortrait.value + ? Get.size.width + : Get.size.width * 9 / 16) + + 100 + + padding.bottom), + child: buildMessageListUI( + context, + _liveRoomController, + _scrollController, + ), ), ), ), // 消息输入框 Visibility( - visible: MediaQuery.of(context).orientation == Orientation.portrait, + visible: isPortrait, child: Positioned( bottom: 0, left: 0, right: 0, child: Container( padding: EdgeInsets.only( - left: 14, - right: 14, - top: 4, - bottom: MediaQuery.of(context).padding.bottom + 20), + left: 14, right: 14, top: 4, bottom: padding.bottom + 20), decoration: BoxDecoration( color: Colors.grey.withOpacity(0.1), borderRadius: const BorderRadius.all(Radius.circular(20)), @@ -421,6 +422,7 @@ class _LiveRoomPageState extends State ], ), ); + if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, @@ -438,84 +440,82 @@ Widget buildMessageListUI( LiveRoomController liveRoomController, ScrollController scrollController, ) { - return Expanded( - child: Obx( - () => MediaQuery.removePadding( - context: context, - removeTop: true, - removeBottom: true, - child: ShaderMask( - shaderCallback: (Rect bounds) { - return LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black.withOpacity(0.5), - Colors.black, - ], - stops: const [0.01, 0.05, 0.2], - ).createShader(bounds); + return Obx( + () => MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ShaderMask( + shaderCallback: (Rect bounds) { + return LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.5), + Colors.black, + ], + stops: const [0.01, 0.05, 0.2], + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, + child: GestureDetector( + onTap: () { + // 键盘失去焦点 + FocusScope.of(context).requestFocus(FocusNode()); }, - blendMode: BlendMode.dstIn, - child: GestureDetector( - onTap: () { - // 键盘失去焦点 - FocusScope.of(context).requestFocus(FocusNode()); - }, - child: ListView.builder( - controller: scrollController, - itemCount: liveRoomController.messageList.length, - itemBuilder: (context, index) { - final LiveMessageModel liveMsgItem = - liveRoomController.messageList[index]; - return Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: liveRoomController.isPortrait.value - ? Colors.black.withOpacity(0.3) - : Colors.grey.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - margin: EdgeInsets.only( - top: index == 0 ? 20.0 : 0.0, - bottom: 6.0, - left: 14.0, - right: 14.0, - ), - padding: const EdgeInsets.symmetric( - vertical: 3.0, - horizontal: 10.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: liveRoomController.isPortrait.value + ? Colors.black.withOpacity(0.3) + : Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], - ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, + ), + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], ), ), - ); - }, - ), + ), + ); + }, ), ), ), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 8a0e4418..37c8d174 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -14,6 +14,7 @@ import '../../models/common/nav_bar_config.dart'; class MainController extends GetxController { List pages = []; + List pagesIds = []; RxList navigationBars = [].obs; late List defaultNavTabs; late List navBarSort; @@ -45,7 +46,8 @@ class MainController extends GetxController { SettingBoxKey.dynamicBadgeMode, defaultValue: DynamicBadgeMode.number.code)]; setNavBarConfig(); - if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { + if (dynamicBadgeType.value != DynamicBadgeMode.hidden && + pagesIds.contains(2)) { getUnreadDynamic(); } enableGradientBg = @@ -114,6 +116,7 @@ class MainController extends GetxController { // 如果找不到匹配项,默认索引设置为0或其他合适的值 selectedIndex = defaultIndex != -1 ? defaultIndex : 0; pages = navigationBars.map((e) => e['page']).toList(); + pagesIds = navigationBars.map((e) => e['id']).toList(); } @override diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 93ee2d83..b4f028d4 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/dynamic_badge_mode.dart'; @@ -22,10 +23,10 @@ class MainApp extends StatefulWidget { class _MainAppState extends State with SingleTickerProviderStateMixin { final MainController _mainController = Get.put(MainController()); - final HomeController _homeController = Get.put(HomeController()); - final RankController _rankController = Get.put(RankController()); - final DynamicsController _dynamicController = Get.put(DynamicsController()); - final MineController _mineController = Get.put(MineController()); + late HomeController _homeController; + RankController? _rankController; + late DynamicsController _dynamicController; + late MineController _mineController; int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; @@ -38,6 +39,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _mainController.pageController = PageController(initialPage: _mainController.selectedIndex); enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); + controllerInit(); } void setIndex(int value) async { @@ -60,18 +62,18 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { } if (currentPage is RankPage) { - if (_rankController.flag) { + if (_rankController!.flag) { // 单击返回顶部 双击并刷新 if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) { - _rankController.onRefresh(); + _rankController!.onRefresh(); } else { - _rankController.animateToTop(); + _rankController!.animateToTop(); } _lastSelectTime = DateTime.now().millisecondsSinceEpoch; } - _rankController.flag = true; + _rankController!.flag = true; } else { - _rankController.flag = false; + _rankController?.flag = false; } if (currentPage is DynamicsPage) { @@ -96,6 +98,18 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { } } + void controllerInit() { + _homeController = Get.put(HomeController()); + _dynamicController = Get.put(DynamicsController()); + _mineController = Get.put(MineController()); + if (_mainController.pagesIds.contains(1)) { + _rankController = Get.put(RankController()); + } + if (_mainController.pagesIds.contains(2)) { + _dynamicController = Get.put(DynamicsController()); + } + } + @override void dispose() async { await GStrorage.close(); @@ -112,6 +126,14 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { MediaQuery.sizeOf(context).width * 9 / 16; localCache.put('sheetHeight', sheetHeight); localCache.put('statusBarHeight', statusBarHeight); + + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: + Get.isDarkMode ? Brightness.light : Brightness.dark, + ), + ); return PopScope( canPop: false, onPopInvoked: (bool didPop) async { diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 3b7f24a4..8454cebe 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -49,6 +49,8 @@ class MemberController extends GetxController { if (res['status']) { memberInfo.value = res['data']; face.value = res['data'].face; + } else { + SmartDialog.showToast('用户信息请求异常:${res['msg']}'); } return res; } @@ -78,42 +80,10 @@ class MemberController extends GetxController { return; } if (attribute.value == 128) { - blockUser(); - return; + modifyRelation('block'); + } else { + modifyRelation('follow'); } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text(memberInfo.value.isFollowed! ? '取消关注UP主?' : '关注UP主?'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - await VideoHttp.relationMod( - mid: mid, - act: memberInfo.value.isFollowed! ? 2 : 1, - reSrc: 11, - ); - memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; - relationSearch(); - SmartDialog.dismiss(); - memberInfo.update((val) {}); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); } // 关系查询 @@ -123,24 +93,15 @@ class MemberController extends GetxController { var res = await UserHttp.hasFollow(mid); if (res['status']) { attribute.value = res['data']['attribute']; - switch (attribute.value) { - case 1: - attributeText.value = '悄悄关注'; - break; - case 2: - attributeText.value = '已关注'; - break; - case 6: - attributeText.value = '已互关'; - break; - case 128: - attributeText.value = '已拉黑'; - break; - default: - attributeText.value = '关注'; - } + final Map attributeTextMap = { + 1: '悄悄关注', + 2: '已关注', + 6: '已互关', + 128: '已拉黑', + }; + attributeText.value = attributeTextMap[attribute.value] ?? '关注'; if (res['data']['special'] == 1) { - attributeText.value += 'SP'; + attributeText.value = '特别关注'; } } } @@ -151,16 +112,37 @@ class MemberController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, + modifyRelation('block'); + } + +// 合并关注/取关和拉黑逻辑 + Future modifyRelation(String actionType) async { + if (userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } + + String contentText; + int act; + if (actionType == 'follow') { + contentText = memberInfo.value.isFollowed! ? '确定取消关注UP主?' : '确定关注UP主?'; + act = memberInfo.value.isFollowed! ? 2 : 1; + } else if (actionType == 'block') { + contentText = attribute.value != 128 ? '确定拉黑UP主?' : '确定从黑名单移除UP主?'; + act = attribute.value != 128 ? 5 : 6; + } else { + return; + } + + showDialog( + context: Get.context!, builder: (BuildContext context) { return AlertDialog( title: const Text('提示'), - content: Text(attribute.value != 128 ? '确定拉黑UP主?' : '从黑名单移除UP主'), + content: Text(contentText), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), + onPressed: () => Navigator.of(context).pop(), child: Text( '点错了', style: TextStyle(color: Theme.of(context).colorScheme.outline), @@ -170,19 +152,26 @@ class MemberController extends GetxController { onPressed: () async { var res = await VideoHttp.relationMod( mid: mid, - act: attribute.value != 128 ? 5 : 6, + act: act, reSrc: 11, ); SmartDialog.dismiss(); if (res['status']) { - attribute.value = attribute.value != 128 ? 128 : 0; - attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; - memberInfo.value.isFollowed = false; + if (actionType == 'follow') { + memberInfo.value.isFollowed = !memberInfo.value.isFollowed!; + } else if (actionType == 'block') { + attribute.value = attribute.value != 128 ? 128 : 0; + attributeText.value = attribute.value == 128 ? '已拉黑' : '关注'; + memberInfo.value.isFollowed = false; + } relationSearch(); + if (context.mounted) { + Navigator.of(context).pop(); + } memberInfo.update((val) {}); } }, - child: const Text('确认'), + child: const Text('确定'), ) ], ); @@ -228,17 +217,14 @@ class MemberController extends GetxController { // 跳转查看动态 void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); - // 跳转查看投稿 void pushArchivesPage() => Get.toNamed('/memberArchive?mid=$mid'); - - // 跳转查看专栏 - void pushSeasonsPage() {} // 跳转查看最近投币 void pushRecentCoinsPage() async { if (recentCoinsList.isNotEmpty) {} } + // 跳转查看收藏夹 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 fafba9dc..df501253 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -1,13 +1,13 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/member/info.dart'; import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/utils/utils.dart'; - +import 'widgets/commen_widget.dart'; import 'widgets/conis.dart'; import 'widgets/like.dart'; import 'widgets/profile.dart'; @@ -65,259 +65,233 @@ class _MemberPageState extends State @override Widget build(BuildContext context) { return Scaffold( - primary: true, - body: Column( - children: [ - AppBar( - title: StreamBuilder( - stream: appbarStream.stream.distinct(), - initialData: false, - builder: (BuildContext context, AsyncSnapshot snapshot) { - return AnimatedOpacity( - opacity: snapshot.data ? 1 : 0, - curve: Curves.easeOut, - duration: const Duration(milliseconds: 500), - child: Row( - children: [ - Row( - children: [ - Obx( - () => NetworkImgLayer( - width: 35, - height: 35, - type: 'avatar', - src: _memberController.face.value, - ), - ), - const SizedBox(width: 10), - Obx( - () => Text( - _memberController.memberInfo.value.name ?? '', - style: TextStyle( - color: - Theme.of(context).colorScheme.onSurface, - fontSize: 14), - ), - ), - ], - ) - ], + appBar: AppBar( + title: StreamBuilder( + stream: appbarStream.stream.distinct(), + initialData: false, + builder: (BuildContext context, AsyncSnapshot snapshot) { + return AnimatedOpacity( + opacity: snapshot.data ? 1 : 0, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 500), + child: Row( + children: [ + Obx( + () => NetworkImgLayer( + width: 35, + height: 35, + type: 'avatar', + src: _memberController.face.value, + ), ), - ); - }, - ), - actions: [ - IconButton( - onPressed: () => Get.toNamed( - '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), - icon: const Icon(Icons.search_outlined), - ), - PopupMenuButton( - icon: const Icon(Icons.more_vert), - itemBuilder: (BuildContext context) => [ - if (_memberController.ownerMid != _memberController.mid) ...[ - PopupMenuItem( - onTap: () => _memberController.blockUser(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.block, size: 19), - const SizedBox(width: 10), - Text(_memberController.attribute.value != 128 - ? '加入黑名单' - : '移除黑名单'), - ], - ), - ) - ], - PopupMenuItem( - onTap: () => _memberController.shareUser(), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.share_outlined, size: 19), - const SizedBox(width: 10), - Text(_memberController.ownerMid != _memberController.mid - ? '分享UP主' - : '分享我的主页'), - ], + const SizedBox(width: 10), + Obx( + () => Text( + _memberController.memberInfo.value.name ?? '', + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontSize: 14), ), ), ], ), - const SizedBox(width: 4), - ], + ); + }, + ), + actions: [ + IconButton( + onPressed: () => Get.toNamed( + '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), + icon: const Icon(Icons.search_outlined), ), - Expanded( - child: SingleChildScrollView( - controller: _extendNestCtr, - child: Padding( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 20, - ), - child: Column( + PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + if (_memberController.ownerMid != _memberController.mid) ...[ + PopupMenuItem( + onTap: () => _memberController.blockUser(), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.block, size: 19), + const SizedBox(width: 10), + Text(_memberController.attribute.value != 128 + ? '加入黑名单' + : '移除黑名单'), + ], + ), + ) + ], + PopupMenuItem( + onTap: () => _memberController.shareUser(), + child: Row( + mainAxisSize: MainAxisSize.min, children: [ - profileWidget(), - - /// 动态链接 - Obx( - () => ListTile( - onTap: _memberController.pushDynamicsPage, - 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.pushArticlePage, - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), - ), - ), - - /// 合集 - Obx( - () => ListTile( - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的合集')), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: FutureBuilder( - future: _memberSeasonsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - if (data['data'].seasonsList.isEmpty) { - return commenWidget('用户没有设置合集'); - } else { - return MemberSeasonsPanel(data: data['data']); - } - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - - /// 追番 - /// 最近投币 - Obx( - () => _memberController.recentCoinsList.isNotEmpty - ? const ListTile(title: Text('最近投币的视频')) - : const SizedBox(), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberCoinsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - return MemberCoinsPanel(data: data['data']); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - ), - - /// 最近点赞 - Obx( - () => _memberController.recentLikeList.isNotEmpty - ? const ListTile(title: Text('最近点赞的视频')) - : const SizedBox(), - ), - MediaQuery.removePadding( - removeTop: true, - removeBottom: true, - context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberLikeFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - return MemberLikePanel(data: data['data']); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - return const SizedBox(); - } - }, - ), - ), - ), + const Icon(Icons.share_outlined, size: 19), + const SizedBox(width: 10), + Text(_memberController.ownerMid != _memberController.mid + ? '分享UP主' + : '分享我的主页'), ], ), ), + ], + ), + const SizedBox(width: 4), + ], + ), + primary: true, + body: ListView( + controller: _extendNestCtr, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 20, + ), + children: [ + profileWidget(), + + /// 动态链接 + Obx( + () => ListTile( + onTap: _memberController.pushDynamicsPage, + 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.pushArticlePage, + title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'), + trailing: const Icon(Icons.arrow_forward_outlined, size: 19), + ), + ), + + /// 合集 + Obx( + () => ListTile( + title: Text('${_memberController.isOwner.value ? '我' : 'Ta'}的合集'), + ), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: FutureBuilder( + future: _memberSeasonsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + if (data['data'].seasonsList.isEmpty) { + return const CommenWidget(msg: '用户没有设置合集'); + } else { + return MemberSeasonsPanel(data: data['data']); + } + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + + /// 追番 + /// 最近投币 + Obx( + () => _memberController.recentCoinsList.isNotEmpty + ? const ListTile(title: Text('最近投币的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberCoinsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberCoinsPanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ), + + /// 最近点赞 + Obx( + () => _memberController.recentLikeList.isNotEmpty + ? const ListTile(title: Text('最近点赞的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberLikeFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberLikePanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), ), ), ], @@ -334,115 +308,90 @@ class _MemberPageState extends State if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { + Rx memberInfo = _memberController.memberInfo; return Obx( - () => Stack( - alignment: AlignmentDirectional.center, + () => Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, + ProfilePanel(ctr: _memberController), + const SizedBox(height: 20), + Row( children: [ - ProfilePanel(ctr: _memberController), - const SizedBox(height: 20), - Row( - children: [ - Flexible( - child: Text( - _memberController.memberInfo.value.name!, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith( - fontWeight: FontWeight.bold, - color: _memberController.memberInfo.value - .vip!.nicknameColor != - null - ? Color(_memberController.memberInfo - .value.vip!.nicknameColor!) - : null), - )), - const SizedBox(width: 2), - if (_memberController.memberInfo.value.sex == '女') - const Icon( - FontAwesomeIcons.venus, - size: 14, - color: Colors.pink, - ), - if (_memberController.memberInfo.value.sex == '男') - const Icon( - FontAwesomeIcons.mars, - size: 14, - color: Colors.blue, - ), - const SizedBox(width: 4), - Image.asset( - 'assets/images/lv/lv${_memberController.memberInfo.value.level}.png', - height: 11, - ), - const SizedBox(width: 6), - if (_memberController - .memberInfo.value.vip!.status == - 1 && - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans'] != - '') ...[ - Image.network( - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans'], - height: 20, - ), - ] else if (_memberController - .memberInfo.value.vip!.status == - 1 && - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans_static'] != - '') ...[ - Image.network( - _memberController.memberInfo.value.vip! - .label!['img_label_uri_hans_static'], - height: 20, - ), - ] - ], + Flexible( + child: Text( + memberInfo.value.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith( + fontWeight: FontWeight.bold, + color: memberInfo.value.vip!.nicknameColor != + null + ? Color(_memberController + .memberInfo.value.vip!.nicknameColor!) + : null), + )), + const SizedBox(width: 2), + if (memberInfo.value.sex == '女') + const Icon( + FontAwesomeIcons.venus, + size: 14, + color: Colors.pink, + ), + if (memberInfo.value.sex == '男') + const Icon( + FontAwesomeIcons.mars, + size: 14, + color: Colors.blue, + ), + const SizedBox(width: 4), + Image.asset( + 'assets/images/lv/lv${memberInfo.value.level}.png', + height: 11, ), - if (_memberController - .memberInfo.value.official!['title'] != - '') ...[ - const SizedBox(height: 6), - Text.rich( - maxLines: 2, - TextSpan( - text: _memberController - .memberInfo.value.official!['role'] == - 1 - ? '个人认证:' - : '企业认证:', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - children: [ - TextSpan( - text: _memberController - .memberInfo.value.official!['title'], - ), - ], - ), - softWrap: true, + const SizedBox(width: 6), + if (memberInfo.value.vip!.status == 1 && + memberInfo + .value.vip!.label!['img_label_uri_hans'] != + '') ...[ + Image.network( + memberInfo.value.vip!.label!['img_label_uri_hans'], + height: 20, ), - ], - const SizedBox(height: 6), - if (_memberController.memberInfo.value.sign != '') - SelectableText( - _memberController.memberInfo.value.sign!, + ] else if (memberInfo.value.vip!.status == 1 && + memberInfo.value.vip! + .label!['img_label_uri_hans_static'] != + '') ...[ + Image.network( + memberInfo + .value.vip!.label!['img_label_uri_hans_static'], + height: 20, ), + ] ], ), + if (memberInfo.value.official!['title'] != '') ...[ + const SizedBox(height: 6), + Text( + memberInfo.value.official!['role'] == 1 + ? '个人认证:${memberInfo.value.official!['title']}' + : '企业认证:${memberInfo.value.official!['title']}', + maxLines: 2, + softWrap: true, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + const SizedBox(height: 6), + SelectableText(memberInfo.value.sign ?? ''), ], ), ); } else { - return const SizedBox(); + return ProfilePanel(ctr: _memberController, loadingStatus: true); } } else { // 骨架屏 @@ -452,22 +401,4 @@ class _MemberPageState extends State ), ); } - - Widget commenWidget(msg) { - return Padding( - padding: const EdgeInsets.only( - top: 20, - bottom: 30, - ), - child: Center( - child: Text( - msg, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith(color: Theme.of(context).colorScheme.outline), - ), - ), - ); - } } diff --git a/lib/pages/member/widgets/commen_widget.dart b/lib/pages/member/widgets/commen_widget.dart new file mode 100644 index 00000000..0c92803e --- /dev/null +++ b/lib/pages/member/widgets/commen_widget.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; + +class CommenWidget extends StatelessWidget { + final String msg; + + const CommenWidget({required this.msg, Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 10.0), + child: Center( + child: Text( + msg, + textAlign: TextAlign.center, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + ), + ); + } +} diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index 596ace46..2509160f 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -17,253 +17,245 @@ class ProfilePanel extends StatelessWidget { @override Widget build(BuildContext context) { MemberInfoModel memberInfo = ctr.memberInfo.value; - return Builder( - builder: ((context) { - return Padding( - padding: - EdgeInsets.only(top: MediaQuery.of(context).padding.top - 20), - child: Row( - children: [ - Hero( - tag: ctr.heroTag!, - child: Stack( - children: [ - NetworkImgLayer( - width: 90, - height: 90, - type: 'avatar', - src: !loadingStatus ? memberInfo.face : ctr.face.value, - ), - if (!loadingStatus && - memberInfo.liveRoom != null && - memberInfo.liveRoom!.liveStatus == 1) - Positioned( - bottom: 0, - left: 14, - child: GestureDetector( - onTap: () { - LiveItemModel liveItem = LiveItemModel.fromJson({ - 'title': memberInfo.liveRoom!.title, - 'uname': memberInfo.name, - 'face': memberInfo.face, - 'roomid': memberInfo.liveRoom!.roomId, - 'watched_show': memberInfo.liveRoom!.watchedShow, - }); - Get.toNamed( - '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', - arguments: {'liveItem': liveItem}, - ); - }, - child: Container( - padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - const BorderRadius.all(Radius.circular(10)), - ), - child: Row(children: [ - Image.asset( - 'assets/images/live.gif', - height: 10, - ), - Text( - ' 直播中', - style: TextStyle( - color: Colors.white, - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize), - ) - ]), - ), - ), - ) - ], + final int? mid = memberInfo.mid; + final String? name = memberInfo.name; + + Map buildStatItem({ + required String label, + required String value, + required VoidCallback onTap, + }) { + return { + 'label': label, + 'value': value, + 'fn': onTap, + }; + } + + final List> statList = [ + buildStatItem( + label: '关注', + value: !loadingStatus ? "${ctr.userStat!['following']}" : '-', + onTap: () { + Get.toNamed('/follow?mid=$mid&name=$name'); + }, + ), + buildStatItem( + label: '粉丝', + value: !loadingStatus + ? ctr.userStat!['follower'] != null + ? Utils.numFormat(ctr.userStat!['follower']) + : '-' + : '-', + onTap: () { + Get.toNamed('/fan?mid=$mid&name=$name'); + }, + ), + buildStatItem( + label: '获赞', + value: !loadingStatus + ? ctr.userStat!['likes'] != null + ? Utils.numFormat(ctr.userStat!['likes']) + : '-' + : '-', + onTap: () {}, + ), + ]; + + return Padding( + padding: const EdgeInsets.only(top: 30, left: 4), + child: Row( + children: [ + Hero( + tag: ctr.heroTag!, + child: Stack( + children: [ + NetworkImgLayer( + width: 90, + height: 90, + type: 'avatar', + src: !loadingStatus ? memberInfo.face : ctr.face.value, ), - ), - const SizedBox(width: 12), - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: - const EdgeInsets.only(top: 10, left: 10, right: 10), - child: Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - InkWell( - onTap: () { - Get.toNamed( - '/follow?mid=${memberInfo.mid}&name=${memberInfo.name}'); - }, - child: Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['following'].toString() - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold), - ), - Text( - '关注', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), + if (!loadingStatus && + memberInfo.liveRoom != null && + memberInfo.liveRoom!.liveStatus == 1) + Positioned( + bottom: 0, + left: 14, + child: GestureDetector( + onTap: () { + LiveItemModel liveItem = LiveItemModel( + title: memberInfo.liveRoom!.title, + uname: memberInfo.name, + face: memberInfo.face, + roomId: memberInfo.liveRoom!.roomId, + watchedShow: memberInfo.liveRoom!.watchedShow, + ); + Get.toNamed( + '/liveRoom?roomid=${memberInfo.liveRoom!.roomId}', + arguments: {'liveItem': liveItem}, + ); + }, + child: Container( + padding: const EdgeInsets.fromLTRB(6, 2, 6, 2), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: + const BorderRadius.all(Radius.circular(10)), + ), + child: Row(children: [ + Image.asset( + 'assets/images/live.gif', + height: 10, ), - InkWell( - onTap: () { - Get.toNamed( - '/fan?mid=${memberInfo.mid}&name=${memberInfo.name}'); - }, - child: Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['follower'] != null - ? Utils.numFormat( - ctr.userStat!['follower'], - ) - : '-' - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold)), - Text( - '粉丝', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), - ), - Column( - children: [ - Text( - !loadingStatus - ? ctr.userStat!['likes'] != null - ? Utils.numFormat( - ctr.userStat!['likes'], - ) - : '-' - : '-', - style: const TextStyle( - fontWeight: FontWeight.bold)), - Text( - '获赞', - style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelMedium! - .fontSize), - ) - ], - ), - ], + Text( + ' 直播中', + style: TextStyle( + color: Colors.white, + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize), + ) + ]), ), ), - const SizedBox(height: 10), - if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) ...[ - Row( - children: [ - Obx( - () => Expanded( - child: TextButton( - onPressed: () => loadingStatus - ? null - : ctr.actionRelationMod(), - style: TextButton.styleFrom( - foregroundColor: ctr.attribute.value == -1 - ? Colors.transparent - : ctr.attribute.value != 0 - ? Theme.of(context) - .colorScheme - .outline - : Theme.of(context) - .colorScheme - .onPrimary, - backgroundColor: ctr.attribute.value != 0 - ? Theme.of(context) - .colorScheme - .onInverseSurface - : Theme.of(context) - .colorScheme - .primary, // 设置按钮背景色 - ), - child: Obx(() => Text(ctr.attributeText.value)), - ), - ), - ), - const SizedBox(width: 8), - Expanded( - child: TextButton( - onPressed: () { - Get.toNamed( - '/whisperDetail', - parameters: { - 'name': memberInfo.name!, - 'face': memberInfo.face!, - 'mid': memberInfo.mid.toString(), - 'heroTag': ctr.heroTag!, - }, - ); - }, - style: TextButton.styleFrom( - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - child: const Text('发消息'), - ), - ) - ], - ) - ], - if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) ...[ - TextButton( - onPressed: () { - Get.toNamed('/mineEdit'); - }, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 80, right: 80), - foregroundColor: - Theme.of(context).colorScheme.onPrimary, - backgroundColor: - Theme.of(context).colorScheme.primary, - ), - child: const Text('编辑资料'), - ) - ], - if (ctr.ownerMid == -1) ...[ - TextButton( - onPressed: () {}, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 80, right: 80), - foregroundColor: - Theme.of(context).colorScheme.outline, - backgroundColor: - Theme.of(context).colorScheme.onInverseSurface, - ), - child: const Text('未登录'), - ) - ] - ], - ), - ), - ], + ) + ], + ), ), - ); - }), + const SizedBox(width: 12), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: statList.map((item) { + return buildStatColumn( + context, + item['label'], + item['value'], + item['fn'], + ); + }).toList(), + ), + ), + const SizedBox(height: 16), + if (ctr.ownerMid != ctr.mid && ctr.ownerMid != -1) + buildActionButtons(context, ctr, memberInfo), + if (ctr.ownerMid == ctr.mid && ctr.ownerMid != -1) + buildEditProfileButton(context), + if (ctr.ownerMid == -1) buildNotLoggedInButton(context), + ], + ), + ), + ], + ), + ); + } + + Widget buildStatColumn( + BuildContext context, + String label, + String value, + VoidCallback? onTap, + ) { + return InkWell( + onTap: onTap, + child: Column( + children: [ + Text( + value, + style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 16), + ), + Text( + label, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + ), + ), + ], + ), + ); + } + + Widget buildActionButtons( + BuildContext context, + dynamic ctr, + MemberInfoModel memberInfo, + ) { + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Row( + children: [ + const SizedBox(width: 20), + Obx( + () => Expanded( + child: TextButton( + onPressed: () => loadingStatus ? null : ctr.actionRelationMod(), + style: TextButton.styleFrom( + foregroundColor: ctr.attribute.value == -1 + ? Colors.transparent + : ctr.attribute.value != 0 + ? colorScheme.outline + : colorScheme.onPrimary, + backgroundColor: ctr.attribute.value != 0 + ? colorScheme.onInverseSurface + : colorScheme.primary, + ), + child: Obx(() => Text(ctr.attributeText.value)), + ), + ), + ), + const SizedBox(width: 8), + Expanded( + child: TextButton( + onPressed: () { + Get.toNamed( + '/whisperDetail', + parameters: { + 'name': memberInfo.name!, + 'face': memberInfo.face!, + 'mid': memberInfo.mid.toString(), + 'heroTag': ctr.heroTag!, + }, + ); + }, + style: TextButton.styleFrom( + backgroundColor: colorScheme.onInverseSurface, + ), + child: const Text('发消息'), + ), + ), + ], + ); + } + + Widget buildEditProfileButton(BuildContext context) { + return TextButton( + onPressed: () { + Get.toNamed('/mineEdit'); + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 80), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: Theme.of(context).colorScheme.primary, + ), + child: const Text('编辑资料'), + ); + } + + Widget buildNotLoggedInButton(BuildContext context) { + return TextButton( + onPressed: () {}, + style: TextButton.styleFrom( + padding: const EdgeInsets.symmetric(horizontal: 80), + foregroundColor: Theme.of(context).colorScheme.outline, + backgroundColor: Theme.of(context).colorScheme.onInverseSurface, + ), + child: const Text('未登录'), ); } } diff --git a/lib/pages/member_article/controller.dart b/lib/pages/member_article/controller.dart index cffce2fe..d79fb4a6 100644 --- a/lib/pages/member_article/controller.dart +++ b/lib/pages/member_article/controller.dart @@ -10,7 +10,6 @@ class MemberArticleController extends GetxController { int pn = 1; String? offset; bool hasMore = true; - String? wWebid; RxBool isLoading = false.obs; RxList articleList = [].obs; @@ -20,25 +19,11 @@ class MemberArticleController extends GetxController { 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(); @@ -47,7 +32,6 @@ class MemberArticleController extends GetxController { mid: mid, pn: pn, offset: offset, - wWebid: wWebid!, ); if (res['status']) { offset = res['data'].offset; diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index 8b061505..b30a0c4f 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -125,21 +125,29 @@ class LikeItem extends StatelessWidget { Color outline = Theme.of(context).colorScheme.outline; final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); int usersLen = item.users!.length > 3 ? 3 : item.users!.length; - final String bvid = item.item!.uri!.split('/').last; + final Uri uri = Uri.parse(item.item!.uri!); + final String path = uri.path; + final String bvid = path.split('/').last; + + /// bilibili:// + final Uri nativeUri = Uri.parse(item.item!.nativeUri!); + final Map queryParameters = nativeUri.queryParameters; + final String type = item.item!.type!; + // cid + final String? argCid = queryParameters['cid']; // 页码 - final String page = - item.item!.nativeUri!.split('page=').last.split('&').first; + final String? page = queryParameters['page']; // 根评论id - final String commentRootId = - item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + final String? commentRootId = queryParameters['comment_root_id']; // 二级评论id - final String commentSecondaryId = - item.item!.nativeUri!.split('comment_secondary_id=').last; + final String? commentSecondaryId = queryParameters['comment_secondary_id']; return InkWell( onTap: () async { try { - final int cid = await SearchHttp.ab2c(bvid: bvid); + final int cid = argCid != null + ? int.parse(argCid) + : await SearchHttp.ab2c(bvid: bvid); final String heroTag = Utils.makeHeroTag(bvid); Get.toNamed( '/video?bvid=$bvid&cid=$cid', @@ -148,8 +156,8 @@ class LikeItem extends StatelessWidget { 'heroTag': heroTag, }, ); - } catch (_) { - SmartDialog.showToast('视频可能失效了'); + } catch (e) { + SmartDialog.showToast('视频可能失效了$e'); } }, child: Stack( @@ -222,7 +230,7 @@ class LikeItem extends StatelessWidget { ), ), const SizedBox(width: 25), - if (item.item!.type! == 'reply') + if (type == 'reply' || type == 'danmu') Container( width: 60, height: 60, @@ -234,7 +242,7 @@ class LikeItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - if (item.item!.type! == 'video') + if (type == 'video') NetworkImgLayer( width: 60, height: 60, diff --git a/lib/pages/opus/view.dart b/lib/pages/opus/view.dart index 434a9405..42c0c419 100644 --- a/lib/pages/opus/view.dart +++ b/lib/pages/opus/view.dart @@ -157,7 +157,7 @@ class _OpusPageState extends State { Container( alignment: TextHelper.getAlignment(paragraph.align), margin: const EdgeInsets.only(bottom: 10), - child: Text.rich( + child: SelectableText.rich( TextSpan( children: paragraph.text?.nodes?.map((node) { return TextHelper.buildTextSpan( diff --git a/lib/pages/read/controller.dart b/lib/pages/read/controller.dart index 178ebfda..b580b964 100644 --- a/lib/pages/read/controller.dart +++ b/lib/pages/read/controller.dart @@ -20,7 +20,7 @@ class ReadPageController extends GetxController { super.onInit(); title.value = Get.parameters['title'] ?? ''; id = Get.parameters['id']!; - articleType = Get.parameters['articleType']!; + articleType = Get.parameters['articleType'] ?? 'read'; url = 'https://www.bilibili.com/read/cv$id'; scrollController.addListener(_scrollListener); fetchViewInfo(); diff --git a/lib/pages/read/view.dart b/lib/pages/read/view.dart index d37eeae5..710934eb 100644 --- a/lib/pages/read/view.dart +++ b/lib/pages/read/view.dart @@ -126,7 +126,6 @@ class _ReadPageState extends State { Widget _buildContent(ReadDataModel cvData) { final List picList = _extractPicList(cvData); final List imgList = extractDataSrc(cvData.readInfo!.content!); - return Padding( padding: EdgeInsets.fromLTRB( 16, 0, 16, MediaQuery.of(context).padding.bottom + 40), @@ -163,9 +162,11 @@ class _ReadPageState extends State { padding: const EdgeInsets.only(bottom: 20), child: _buildAuthorWidget(cvData), ), - HtmlRender( - htmlContent: cvData.readInfo!.content!, - imgList: imgList, + SelectionArea( + child: HtmlRender( + htmlContent: cvData.readInfo!.content!, + imgList: imgList, + ), ), ], ); @@ -206,7 +207,7 @@ class _ReadPageState extends State { return Container( alignment: TextHelper.getAlignment(paragraph.align), margin: const EdgeInsets.only(bottom: 10), - child: Text.rich( + child: SelectableText.rich( TextSpan( children: paragraph.text?.nodes?.map((node) { return TextHelper.buildTextSpan(node, paragraph.align, context); diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index dc0b2bac..2d1aa228 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -24,7 +24,9 @@ class SearchPanelController extends GetxController { searchType: searchType!, keyword: keyword!, page: page.value, - order: searchType!.type != 'video' ? null : order.value, + order: !['video', 'article'].contains(searchType!.type) + ? null + : (order.value == '' ? null : order.value), duration: searchType!.type != 'video' ? null : duration.value, tids: searchType!.type != 'video' ? null : tids.value, ); diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index c5824d70..fa669489 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -1,3 +1,5 @@ +// ignore_for_file: invalid_use_of_protected_member + import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -45,6 +47,11 @@ class _SearchPanelState extends State ), tag: widget.searchType!.type + widget.keyword!, ); + + /// 专栏默认排序 + if (widget.searchType == SearchType.article) { + _searchPanelController.order.value = 'totalrank'; + } scrollController = _searchPanelController.scrollController; scrollController.addListener(() async { if (scrollController.position.pixels >= @@ -84,7 +91,6 @@ class _SearchPanelState extends State case SearchType.video: return SearchVideoPanel( ctr: _searchPanelController, - // ignore: invalid_use_of_protected_member list: list.value, ); case SearchType.media_bangumi: @@ -94,7 +100,10 @@ class _SearchPanelState extends State case SearchType.live_room: return searchLivePanel(context, ctr, list); case SearchType.article: - return searchArticlePanel(context, ctr, list); + return SearchArticlePanel( + ctr: _searchPanelController, + list: list.value, + ); default: return const SizedBox(); } diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index dd53de66..be08ed56 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -1,106 +1,249 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/search_panel/index.dart'; import 'package:pilipala/utils/utils.dart'; +class SearchArticlePanel extends StatelessWidget { + SearchArticlePanel({ + required this.ctr, + this.list, + Key? key, + }) : super(key: key); + + final SearchPanelController ctr; + final List? list; + + final ArticlePanelController controller = Get.put(ArticlePanelController()); + + @override + Widget build(BuildContext context) { + return Stack( + alignment: Alignment.topCenter, + children: [ + searchArticlePanel(context, ctr, list), + Container( + width: double.infinity, + height: 36, + padding: const EdgeInsets.only(left: 8, top: 0, right: 8), + child: Row( + children: [ + Expanded( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Obx( + () => Wrap( + // spacing: , + children: [ + for (var i in controller.filterList) ...[ + CustomFilterChip( + label: i['label'], + type: i['type'], + selectedType: controller.selectedType.value, + callFn: (bool selected) async { + controller.selectedType.value = i['type']; + ctr.order.value = + i['type'].toString().split('.').last; + SmartDialog.showLoading(msg: 'loading'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + ), + ] + ], + ), + ), + ), + ), + ], + ), + ), + ], + ); + } +} + Widget searchArticlePanel(BuildContext context, ctr, list) { TextStyle textStyle = TextStyle( fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, color: Theme.of(context).colorScheme.outline); - return ListView.builder( - controller: ctr!.scrollController, - itemCount: list.length, - itemBuilder: (context, index) { - return InkWell( - onTap: () { - Get.toNamed('/read', parameters: { - 'title': list[index].subTitle, - 'id': list[index].id.toString(), - 'articleType': 'read' - }); - }, - child: Padding( - padding: const EdgeInsets.fromLTRB( - StyleString.safeSpace, 5, StyleString.safeSpace, 5), - child: LayoutBuilder(builder: (context, boxConstraints) { - final double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(1.0)) / - 2; - return Container( - constraints: const BoxConstraints(minHeight: 88), - height: width / StyleString.aspectRatio, - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (list[index].imageUrls != null && - list[index].imageUrls.isNotEmpty) - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder(builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return NetworkImgLayer( - width: maxWidth, - height: maxHeight, - src: list[index].imageUrls.first, - ); - }), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), - child: Column( - mainAxisSize: MainAxisSize.min, + return Padding( + padding: const EdgeInsets.only(top: 36), + child: list!.isNotEmpty + ? ListView.builder( + controller: ctr!.scrollController, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: list.length, + itemBuilder: (context, index) { + return InkWell( + onTap: () { + Get.toNamed('/read', parameters: { + 'title': list[index].subTitle, + 'id': list[index].id.toString(), + 'articleType': 'read' + }); + }, + child: Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder(builder: (context, boxConstraints) { + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; + return Container( + constraints: const BoxConstraints(minHeight: 88), + height: width / StyleString.aspectRatio, + child: Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - maxLines: 2, - text: TextSpan( - children: [ - for (var i in list[index].title) ...[ - TextSpan( - text: i['text'], - style: TextStyle( - fontWeight: FontWeight.w500, - letterSpacing: 0.3, - color: i['type'] == 'em' - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) - .colorScheme - .onSurface, + children: [ + if (list[index].imageUrls != null && + list[index].imageUrls.isNotEmpty) + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return NetworkImgLayer( + width: maxWidth, + height: maxHeight, + src: list[index].imageUrls.first, + ); + }), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + RichText( + maxLines: 2, + text: TextSpan( + children: [ + for (var i in list[index].title) ...[ + TextSpan( + text: i['text'], + style: TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + color: i['type'] == 'em' + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .onSurface, + ), + ), + ] + ], ), ), - ] - ], + const Spacer(), + Text( + Utils.dateFormat(list[index].pubTime, + formatType: 'detail'), + style: textStyle), + Row( + children: [ + Text('${list[index].view}浏览', + style: textStyle), + Text(' • ', style: textStyle), + Text('${list[index].reply}评论', + style: textStyle), + ], + ), + ], + ), ), ), - const Spacer(), - Text( - Utils.dateFormat(list[index].pubTime, - formatType: 'detail'), - style: textStyle), - Row( - children: [ - Text('${list[index].view}浏览', style: textStyle), - Text(' • ', style: textStyle), - Text('${list[index].reply}评论', style: textStyle), - ], - ), ], ), - ), - ), - ], - ), - ); - }), - ), - ); - }, + ); + }), + ), + ); + }, + ) + : CustomScrollView( + slivers: [ + HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + ) + ], + ), ); } + +class CustomFilterChip extends StatelessWidget { + const CustomFilterChip({ + this.label, + this.type, + this.selectedType, + this.callFn, + Key? key, + }) : super(key: key); + + final String? label; + final ArticleFilterType? type; + final ArticleFilterType? selectedType; + final Function? callFn; + + @override + Widget build(BuildContext context) { + return SizedBox( + height: 34, + child: FilterChip( + padding: const EdgeInsets.only(left: 11, right: 11), + labelPadding: EdgeInsets.zero, + label: Text( + label!, + style: const TextStyle(fontSize: 13), + ), + labelStyle: TextStyle( + color: type == selectedType + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline), + selected: type == selectedType, + showCheckmark: false, + shape: ContinuousRectangleBorder( + borderRadius: BorderRadius.circular(12), + ), + selectedColor: Colors.transparent, + // backgroundColor: + // Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + backgroundColor: Colors.transparent, + side: BorderSide.none, + onSelected: (bool selected) => callFn!(selected), + ), + ); + } +} + +class ArticlePanelController extends GetxController { + RxList filterList = [{}].obs; + Rx selectedType = ArticleFilterType.values.first.obs; + + @override + void onInit() { + List> list = ArticleFilterType.values + .map((type) => { + 'label': type.description, + 'type': type, + }) + .toList(); + filterList.value = list; + super.onInit(); + } +} diff --git a/lib/pages/setting/pages/play_gesture_set.dart b/lib/pages/setting/pages/play_gesture_set.dart index e671bfb2..9659e58a 100644 --- a/lib/pages/setting/pages/play_gesture_set.dart +++ b/lib/pages/setting/pages/play_gesture_set.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/utils/global_data_cache.dart'; @@ -22,7 +23,7 @@ class _PlayGesturePageState extends State { void initState() { super.initState(); fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode, - defaultValue: FullScreenGestureMode.values.last.index); + defaultValue: FullScreenGestureMode.fromBottomtoTop.index); } @override @@ -71,6 +72,7 @@ class _PlayGesturePageState extends State { GlobalDataCache().fullScreenGestureMode.index; setting.put( SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); + SmartDialog.showToast('设置成功'); setState(() {}); } }, diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index a57ec1de..3fead9c7 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -675,7 +675,6 @@ class VideoDetailController extends GetxController @override void onClose() { super.onClose(); - plPlayerController.dispose(); tabCtr.removeListener(() { onTabChanged(); }); diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index c0f6eebb..bc9af109 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -3,7 +3,6 @@ import 'dart:async'; import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; @@ -154,11 +153,10 @@ class VideoIntroController extends GetxController { } if (hasLike.value && hasCoin.value && hasFav.value) { // 已点赞、投币、收藏 - SmartDialog.showToast('🙏 UP已经收到了~'); + SmartDialog.showToast('UP已经收到了~'); return false; } var result = await VideoHttp.oneThree(bvid: bvid); - print('🤣🦴:${result["data"]}'); if (result['status']) { hasLike.value = result["data"]["like"]; hasCoin.value = result["data"]["coin"]; @@ -413,7 +411,12 @@ class VideoIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid, cover) async { + Future changeSeasonOrbangu( + String bvid, + int cid, + int? aid, + String? cover, + ) async { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); @@ -424,13 +427,14 @@ class VideoIntroController extends GetxController { releatedCtr.queryRelatedVideo(); } - videoDetailCtr.bvid = bvid; - videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); - videoDetailCtr.cid.value = cid; - videoDetailCtr.danmakuCid.value = cid; - videoDetailCtr.cover.value = cover; - videoDetailCtr.queryVideoUrl(); - videoDetailCtr.clearSubtitleContent(); + videoDetailCtr + ..bvid = bvid + ..oid.value = aid ?? IdUtils.bv2av(bvid) + ..cid.value = cid + ..danmakuCid.value = cid + ..cover.value = cover ?? '' + ..queryVideoUrl() + ..clearSubtitleContent(); await videoDetailCtr.getSubtitle(); videoDetailCtr.setSubtitleContent(); // 重新请求评论 @@ -480,7 +484,13 @@ class VideoIntroController extends GetxController { final List episodes = []; bool isPages = false; late String cover; - if (videoDetail.value.ugcSeason != null) { + final VideoDetailController videoDetailCtr = + Get.find(tag: heroTag); + + /// 优先稍后再看、收藏夹 + if (videoDetailCtr.isWatchLaterVisible.value) { + episodes.addAll(videoDetailCtr.mediaList); + } else if (videoDetail.value.ugcSeason != null) { final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final List sections = ugcSeason.sections!; for (int i = 0; i < sections.length; i++) { @@ -497,10 +507,15 @@ class VideoIntroController extends GetxController { episodes.indexWhere((e) => e.cid == lastPlayCid.value); int nextIndex = currentIndex + 1; cover = episodes[nextIndex].cover; - final VideoDetailController videoDetailCtr = - Get.find(tag: heroTag); final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; + int cid = episodes[nextIndex].cid!; + while (cid == -1) { + nextIndex += 1; + SmartDialog.showToast('当前视频暂不支持播放,自动跳过'); + cid = episodes[nextIndex].cid!; + } + // 列表循环 if (nextIndex >= episodes.length) { if (platRepeat == PlayRepeat.listCycle) { @@ -510,7 +525,6 @@ class VideoIntroController extends GetxController { return; } } - final int cid = episodes[nextIndex].cid!; final String rBvid = isPages ? bvid : episodes[nextIndex].bvid; final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; changeSeasonOrbangu(rBvid, cid, rAid, cover); @@ -604,4 +618,34 @@ class VideoIntroController extends GetxController { ).buildShowContent(Get.context!), ); } + + // + oneThreeDialog() { + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('是否一键三连'), + actions: [ + TextButton( + onPressed: () => navigator!.pop(), + child: Text( + '取消', + style: TextStyle( + color: Theme.of(Get.context!).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + actionOneThree(); + navigator!.pop(); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 04cf92fe..31edab2f 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,7 +1,4 @@ -import 'dart:ffi'; - import 'package:bottom_sheet/bottom_sheet.dart'; -import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -151,11 +148,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - // 一键三连动画 - late AnimationController _controller; - late Animation _scaleTransition; - final RxDouble _progress = 0.0.obs; - void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -178,26 +170,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { owner = widget.videoDetail!.owner; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); - - /// 一键三连动画 - _controller = AnimationController( - duration: const Duration(milliseconds: 1500), - reverseDuration: const Duration(milliseconds: 300), - vsync: this, - ); - _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) - ..addListener(() async { - _progress.value = - double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3)); - if (_progress.value == 1) { - if (_controller.status == AnimationStatus.completed) { - await videoIntroController.actionOneThree(); - } - _progress.value = 0; - _scaleTransition.removeListener(() {}); - _controller.stop(); - } - }); } // 收藏 @@ -279,8 +251,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); - _controller.dispose(); - _scaleTransition.removeListener(() {}); super.dispose(); } @@ -573,131 +543,34 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget actionGrid(BuildContext context, videoIntroController) { final actionTypeSort = GlobalDataCache().actionTypeSort; - Widget progressWidget(progress) { - return SizedBox( - width: const IconThemeData.fallback().size! + 5, - height: const IconThemeData.fallback().size! + 5, - child: CircularProgressIndicator( - value: progress.value, - strokeWidth: 2, - ), - ); - } - Map menuListWidgets = { 'like': Obx( - () { - bool likeStatus = videoIntroController.hasLike.value; - ColorScheme colorScheme = Theme.of(context).colorScheme; - return Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - InkWell( - onTapDown: (details) { - feedBack(); - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } - _controller.forward(); - }, - onTapUp: (TapUpDetails details) { - if (_progress.value == 0) { - feedBack(); - EasyThrottle.throttle( - 'my-throttler', const Duration(milliseconds: 200), () { - videoIntroController.actionLikeVideo(); - }); - } - _controller.reverse(); - }, - onTapCancel: () { - _controller.reverse(); - }, - borderRadius: StyleString.mdRadius, - child: SizedBox( - width: (Get.size.width - 24) / 5, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Icon( - key: ValueKey(likeStatus), - likeStatus - ? FontAwesomeIcons.solidThumbsUp - : FontAwesomeIcons.thumbsUp, - color: likeStatus - ? colorScheme.primary - : colorScheme.outline, - size: 21, - ), - ), - const SizedBox(height: 6), - Text( - widget.videoDetail!.stat!.like!.toString(), - style: TextStyle( - color: likeStatus ? colorScheme.primary : null, - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - ), - ) - ], - ), - ), - ), - ], - ); - }, + () => ActionItem( + icon: const Icon(FontAwesomeIcons.thumbsUp), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + onTap: handleState(videoIntroController.actionLikeVideo), + onLongPress: () => videoIntroController.oneThreeDialog(), + selectStatus: videoIntroController.hasLike.value, + text: widget.videoDetail!.stat!.like!.toString(), + ), ), 'coin': Obx( - () => Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ], + () => ActionItem( + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), ), ), 'collect': Obx( - () => Stack( - children: [ - Positioned( - top: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size!), - left: ((Get.size.width - 24) / 5) / 2 - - (const IconThemeData.fallback().size! + 5) / 2, - child: progressWidget(_progress)), - ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ], + () => ActionItem( + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), ), ), 'watchLater': ActionItem( diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index f91ef625..1b70015c 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -153,7 +153,17 @@ class _VideoReplyPanelState extends State child: Container( height: 40, padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), - color: Theme.of(context).colorScheme.surface, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + boxShadow: [ + BoxShadow( + color: Theme.of(context).colorScheme.surface, + blurRadius: 0.0, + spreadRadius: 0.0, + offset: const Offset(2, 0), + ), + ], + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 8bb6992a..1d23548b 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -9,6 +9,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/reply.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/main/index.dart'; @@ -18,6 +19,7 @@ import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; @@ -48,6 +50,8 @@ class ReplyItem extends StatelessWidget { @override Widget build(BuildContext context) { + final bool isOwner = int.parse(replyItem!.member!.mid!) == + (GlobalDataCache().userInfo?.mid ?? -1); return Material( child: InkWell( // 点击整个评论区 评论详情/回复 @@ -73,6 +77,7 @@ class ReplyItem extends StatelessWidget { return MorePanel( item: replyItem, mainFloor: true, + isOwner: isOwner, ); }, ); @@ -195,25 +200,36 @@ class ReplyItem extends StatelessWidget { ), ], ), - Row( - children: [ - Text( - Utils.dateFormat(replyItem!.ctime), - style: TextStyle( - fontSize: textTheme.labelSmall!.fontSize, - color: colorScheme.outline, - ), - ), - if (replyItem!.replyControl != null && - replyItem!.replyControl!.location != '') - Text( - ' • ${replyItem!.replyControl!.location!}', + RichText( + text: TextSpan( + children: [ + TextSpan( + text: Utils.dateFormat(replyItem!.ctime), style: TextStyle( - fontSize: textTheme.labelSmall!.fontSize, - color: colorScheme.outline), + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, + ), ), - ], - ) + if (replyItem!.replyControl != null && + replyItem!.replyControl!.location != '') + TextSpan( + text: ' • ${replyItem!.replyControl!.location!}', + style: TextStyle( + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, + ), + ), + if (replyItem!.invisible!) + TextSpan( + text: ' • 隐藏的评论', + style: TextStyle( + color: colorScheme.outline, + fontSize: textTheme.labelSmall!.fontSize, + ), + ), + ], + ), + ), ], ), ], @@ -698,14 +714,11 @@ InlineSpan buildContent( '', ); } else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) { - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://www.bilibili.com/read/$matchStr', - 'type': 'url', - 'pageTitle': title - }, - ); + Get.toNamed('/read', parameters: { + 'title': title, + 'id': Utils.matchNum(matchStr).first.toString(), + 'articleType': 'read', + }); } else { Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( @@ -717,7 +730,7 @@ InlineSpan buildContent( source: '', dataString: matchStr, ); - PiliSchame.fullPathPush(scheme); + PiliSchame.httpsScheme(scheme); } } else { if (appUrlSchema.startsWith('bilibili://search')) { @@ -1004,10 +1017,12 @@ InlineSpan buildContent( class MorePanel extends StatelessWidget { final dynamic item; final bool mainFloor; + final bool isOwner; const MorePanel({ super.key, required this.item, this.mainFloor = false, + this.isOwner = false, }); Future menuActionHandler(String type) async { @@ -1043,9 +1058,43 @@ class MorePanel extends StatelessWidget { // case 'report': // SmartDialog.showToast('举报'); // break; - // case 'delete': - // SmartDialog.showToast('删除'); - // break; + case 'delete': + // 删除评论提示 + await showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('删除评论'), + content: const Text('删除评论后,评论下所有回复将被删除,确定删除吗?'), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text('取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline)), + ), + TextButton( + onPressed: () async { + Get.back(); + var result = await ReplyHttp.replyDel( + type: item.type!, + oid: item.oid!, + rpid: item.rpid!, + ); + if (result['status']) { + SmartDialog.showToast('评论删除成功,需手动刷新'); + Get.back(); + } else { + SmartDialog.showToast(result['msg']); + } + }, + child: const Text('确定'), + ), + ], + ); + }, + ); + break; default: } } @@ -1054,6 +1103,7 @@ class MorePanel extends StatelessWidget { Widget build(BuildContext context) { ColorScheme colorScheme = Theme.of(context).colorScheme; TextTheme textTheme = Theme.of(context).textTheme; + Color errorColor = colorScheme.error; return Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), child: Column( @@ -1106,12 +1156,14 @@ class MorePanel extends StatelessWidget { // leading: Icon(Icons.report_outlined, color: errorColor), // title: Text('举报', style: TextStyle(color: errorColor)), // ), - // ListTile( - // onTap: () async => await menuActionHandler('del'), - // minLeadingWidth: 0, - // leading: Icon(Icons.delete_outline, color: errorColor), - // title: Text('删除', style: TextStyle(color: errorColor)), - // ), + if (isOwner) + ListTile( + onTap: () async => await menuActionHandler('delete'), + minLeadingWidth: 0, + leading: Icon(Icons.delete_outline, color: errorColor), + title: Text('删除评论', + style: textTheme.titleSmall!.copyWith(color: errorColor)), + ), ], ), ); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 5476adc9..7fe76745 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -68,10 +69,6 @@ class _VideoDetailPageState extends State late final AppLifecycleListener _lifecycleListener; late double statusHeight; - // 稍后再看控制器 - // late AnimationController _laterCtr; - // late Animation _laterOffsetAni; - @override void initState() { super.initState(); @@ -85,14 +82,16 @@ class _VideoDetailPageState extends State videoIntroController.videoDetail.listen((value) { videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); - bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); - bangumiIntroController.bangumiDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); - }); - vdCtr.cid.listen((p0) { - videoPlayerServiceHandler.onVideoDetailChange( - bangumiIntroController.bangumiDetail.value, p0); - }); + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); + bangumiIntroController.bangumiDetail.listen((value) { + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); + }); + vdCtr.cid.listen((p0) { + videoPlayerServiceHandler.onVideoDetailChange( + bangumiIntroController.bangumiDetail.value, p0); + }); + } statusBarHeight = localCache.get('statusBarHeight'); autoExitFullcreen = setting.get(SettingBoxKey.enableAutoExit, defaultValue: false); @@ -108,7 +107,6 @@ class _VideoDetailPageState extends State } WidgetsBinding.instance.addObserver(this); lifecycleListener(); - // watchLaterControllerInit(); } // 获取视频资源,初始化播放器 @@ -242,8 +240,6 @@ class _VideoDetailPageState extends State appbarStream.close(); WidgetsBinding.instance.removeObserver(this); _lifecycleListener.dispose(); - // _laterCtr.dispose(); - // _laterOffsetAni.removeListener(() {}); super.dispose(); } @@ -297,6 +293,7 @@ class _VideoDetailPageState extends State plPlayerController?.play(); } plPlayerController?.addStatusLister(playerListener); + appbarStream.add(0); super.didPopNext(); } @@ -490,21 +487,6 @@ class _VideoDetailPageState extends State ); } - /// 稍后再看控制器初始化 - // void watchLaterControllerInit() { - // _laterCtr = AnimationController( - // duration: const Duration(milliseconds: 300), - // vsync: this, - // ); - // _laterOffsetAni = Tween( - // begin: const Offset(0.0, 1.0), - // end: Offset.zero, - // ).animate(CurvedAnimation( - // parent: _laterCtr, - // curve: Curves.easeInOut, - // )); - // } - @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); @@ -529,6 +511,14 @@ class _VideoDetailPageState extends State exitFullScreen(); } + SystemChrome.setSystemUIOverlayStyle( + SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarIconBrightness: + Get.isDarkMode ? Brightness.light : Brightness.dark, + ), + ); + Widget buildLoadingWidget() { return Center(child: Lottie.asset('assets/loading.json', width: 200)); } @@ -615,10 +605,16 @@ class _VideoDetailPageState extends State key: vdCtr.scaffoldKey, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), - child: AppBar( - backgroundColor: Colors.black, - elevation: 0, - scrolledUnderElevation: 0, + child: StreamBuilder( + stream: appbarStream.stream.distinct(), + initialData: 0, + builder: ((context, snapshot) { + return AppBar( + backgroundColor: Colors.black, + elevation: 0, + scrolledUnderElevation: 0, + ); + }), ), ), body: ExtendedNestedScrollView( diff --git a/lib/pages/video/detail/widgets/watch_later_list.dart b/lib/pages/video/detail/widgets/watch_later_list.dart index b367ae40..8e83af4e 100644 --- a/lib/pages/video/detail/widgets/watch_later_list.dart +++ b/lib/pages/video/detail/widgets/watch_later_list.dart @@ -109,7 +109,7 @@ class _MediaListPanelState extends State { var item = mediaList[index]; return InkWell( onTap: () async { - String bvid = item.bvId!; + String bvid = item.bvid!; int? aid = item.id; String cover = item.cover ?? ''; final int cid = @@ -173,7 +173,7 @@ class _MediaListPanelState extends State { overflow: TextOverflow.ellipsis, style: TextStyle( fontWeight: FontWeight.w500, - color: item.bvId == widget.bvid + color: item.bvid == widget.bvid ? Theme.of(context) .colorScheme .primary diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index e97aa79b..cbe4d938 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -228,7 +228,7 @@ class SessionItem extends StatelessWidget { parameters: { 'talkerId': sessionItem.talkerId.toString(), 'name': sessionItem.accountInfo.name, - 'face': sessionItem.accountInfo.face, + 'face': sessionItem.accountInfo.face ?? '', 'mid': (sessionItem.accountInfo?.mid ?? 0).toString(), 'heroTag': heroTag, }, @@ -244,7 +244,7 @@ class SessionItem extends StatelessWidget { width: 45, height: 45, type: 'avatar', - src: sessionItem.accountInfo.face, + src: sessionItem.accountInfo.face ?? '', ), ), ), diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 8dff954a..0bd7950c 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1045,6 +1045,14 @@ class PlPlayerController { /// 缓存本次弹幕选项 cacheDanmakuOption() { + final cache = GlobalDataCache(); + cache.blockTypes = blockTypes; + cache.showArea = showArea; + cache.opacityVal = opacityVal; + cache.fontSizeVal = fontSizeVal; + cache.danmakuDurationVal = danmakuDurationVal; + cache.strokeWidth = strokeWidth; + localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); localCache.put(LocalCacheKey.danmakuShowArea, showArea); localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index dd5b617e..6b57a8e1 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -204,6 +204,11 @@ class _PLVideoPlayerState extends State widget.controller.brightness.value = value; } + bool isUsingFullScreenGestures(double tapPosition, double sectionWidth) { + return fullScreenGestureMode != FullScreenGestureMode.none && + tapPosition < sectionWidth * 2; + } + @override void dispose() { animationController.dispose(); @@ -638,7 +643,10 @@ class _PLVideoPlayerState extends State onVerticalDragUpdate: (DragUpdateDetails details) async { final double totalWidth = MediaQuery.sizeOf(context).width; final double tapPosition = details.localPosition.dx; - final double sectionWidth = totalWidth / 3; + final double sectionWidth = + fullScreenGestureMode == FullScreenGestureMode.none + ? totalWidth / 2 + : totalWidth / 3; final double delta = details.delta.dy; /// 锁定时禁用 @@ -660,12 +668,12 @@ class _PLVideoPlayerState extends State _brightnessValue.value - delta / level; final double result = brightness.clamp(0.0, 1.0); setBrightness(result); - } else if (tapPosition < sectionWidth * 2) { + } else if (isUsingFullScreenGestures(tapPosition, sectionWidth)) { // 全屏 final double dy = details.delta.dy; const double threshold = 7.0; // 滑动阈值 - final bool flag = - fullScreenGestureMode != FullScreenGestureMode.values.last; + final bool flag = fullScreenGestureMode != + FullScreenGestureMode.fromBottomtoTop; if (dy > _distance.value && dy > threshold && !_.controlsLock.value) { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 22184139..a8ad1aa0 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -1,3 +1,4 @@ +import 'package:app_links/app_links.dart'; import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -10,22 +11,29 @@ import 'url_utils.dart'; import 'utils.dart'; class PiliSchame { + static late AppLinks appLinks; static AppScheme appScheme = AppSchemeImpl.getInstance()!; static Future init() async { - /// - final SchemeEntity? value = await appScheme.getInitScheme(); - if (value != null) { - _routePush(value); - } + appLinks = AppLinks(); + appLinks.uriLinkStream.listen((Uri uri) { + final String scheme = uri.scheme; + if (RegExp(r'^pili', caseSensitive: false).hasMatch(scheme)) { + piliScheme(uri); + } + }); + + appScheme.getInitScheme().then((SchemeEntity? value) { + if (value != null) { + _routePush(value); + } + }); - /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { if (value != null) { _routePush(value); } }); - /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { if (event != null) { _routePush(event); @@ -36,88 +44,11 @@ class PiliSchame { /// 路由跳转 static void _routePush(value) async { final String scheme = value.scheme; - final String host = value.host; - final String path = value.path; if (scheme == 'bilibili') { - switch (host) { - case 'root': - Navigator.popUntil( - Get.context!, (Route route) => route.isFirst); - break; - case 'space': - final String mid = path.split('/').last; - Get.toNamed( - '/member?mid=$mid', - arguments: {'face': null}, - ); - break; - case 'video': - String pathQuery = path.split('/').last; - final numericRegex = RegExp(r'^[0-9]+$'); - if (numericRegex.hasMatch(pathQuery)) { - pathQuery = 'AV$pathQuery'; - } - Map map = IdUtils.matchAvorBv(input: pathQuery); - if (map.containsKey('AV')) { - _videoPush(map['AV'], null); - } else if (map.containsKey('BV')) { - _videoPush(null, map['BV']); - } else { - SmartDialog.showToast('投稿匹配失败'); - } - break; - case 'live': - final String roomId = path.split('/').last; - Get.toNamed( - '/liveRoom?roomid=$roomId', - arguments: {'liveItem': null, 'heroTag': roomId}, - ); - break; - case 'bangumi': - if (path.startsWith('/season')) { - final String seasonId = path.split('/').last; - RoutePush.bangumiPush(int.parse(seasonId), null); - } - break; - case 'opus': - if (path.startsWith('/detail')) { - var opusId = path.split('/').last; - Get.toNamed('/opus', parameters: { - 'title': '', - 'id': opusId, - 'articleType': 'opus', - }); - } - break; - case 'search': - Get.toNamed('/searchResult', parameters: {'keyword': ''}); - break; - case 'article': - final String id = path.split('/').last.split('?').first; - Get.toNamed( - '/read', - parameters: { - 'title': 'cv$id', - 'id': id, - 'dynamicType': 'read', - }, - ); - break; - case 'pgc': - if (path.contains('ep')) { - final String lastPathSegment = path.split('/').last; - RoutePush.bangumiPush( - null, int.parse(lastPathSegment.split('?').first)); - } - break; - default: - SmartDialog.showToast('未匹配地址,请联系开发者'); - Clipboard.setData(ClipboardData(text: value.toJson().toString())); - break; - } + biliScheme(value); } if (scheme == 'https') { - fullPathPush(value); + httpsScheme(value); } } @@ -148,7 +79,7 @@ class PiliSchame { } } - static Future fullPathPush(SchemeEntity value) async { + static Future httpsScheme(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 // final String scheme = value.scheme!; @@ -175,6 +106,11 @@ class PiliSchame { if (lastPathSegment.contains('ep')) { RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first); } + } else if (path.startsWith('/BV')) { + final String bvid = path.split('?').first.split('/').last; + _videoPush(null, bvid); + } else if (path.startsWith('/av')) { + _videoPush(Utils.matchNum(path.split('?').first).first, null); } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); @@ -276,6 +212,140 @@ class PiliSchame { } } + static Future biliScheme(SchemeEntity value) async { + final String host = value.host!; + final String path = value.path!; + switch (host) { + case 'root': + Navigator.popUntil( + Get.context!, (Route route) => route.isFirst); + break; + case 'space': + final String mid = path.split('/').last; + Get.toNamed( + '/member?mid=$mid', + arguments: {'face': null}, + ); + break; + case 'video': + String pathQuery = path.split('/').last; + final numericRegex = RegExp(r'^[0-9]+$'); + if (numericRegex.hasMatch(pathQuery)) { + pathQuery = 'AV$pathQuery'; + } + Map map = IdUtils.matchAvorBv(input: pathQuery); + if (map.containsKey('AV')) { + _videoPush(map['AV'], null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV']); + } else { + SmartDialog.showToast('投稿匹配失败'); + } + break; + case 'live': + final String roomId = path.split('/').last; + Get.toNamed( + '/liveRoom?roomid=$roomId', + arguments: {'liveItem': null, 'heroTag': roomId}, + ); + break; + case 'bangumi': + if (path.startsWith('/season')) { + final String seasonId = path.split('/').last; + RoutePush.bangumiPush(int.parse(seasonId), null); + } + break; + case 'opus': + if (path.startsWith('/detail')) { + var opusId = path.split('/').last; + Get.toNamed('/opus', parameters: { + 'title': '', + 'id': opusId, + 'articleType': 'opus', + }); + } + break; + case 'search': + Get.toNamed('/searchResult', parameters: {'keyword': ''}); + break; + case 'article': + final String id = path.split('/').last.split('?').first; + Get.toNamed( + '/read', + parameters: { + 'title': 'cv$id', + 'id': id, + 'dynamicType': 'read', + }, + ); + break; + case 'pgc': + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } + break; + default: + SmartDialog.showToast('未匹配地址,请联系开发者'); + Clipboard.setData(ClipboardData(text: value.toJson().toString())); + break; + } + } + + static Future piliScheme(Uri value) async { + final String host = value.host; + final String path = value.path; + final String arg = path.split('/').last; + switch (host) { + case 'home': + case 'root': + Get.toNamed('/'); + break; + case 'member': + if (arg != '') { + final int? mid = int.tryParse(arg); + if (mid == null) { + SmartDialog.showToast('用户id有误'); + return; + } + Get.toNamed( + '/member?mid=$mid', + arguments: {'face': null}, + ); + } else { + Get.toNamed('/mine'); + } + break; + case 'search': + if (arg != '') { + final String encodedArg = Uri.decodeComponent(arg); + Get.toNamed('/searchResult', parameters: {'keyword': encodedArg}); + } else { + Get.toNamed('/search'); + } + break; + case 'setting': + Get.toNamed('/setting'); + break; + case 'fav': + Get.toNamed('/fav'); + break; + case 'history': + Get.toNamed('/history'); + break; + case 'later': + Get.toNamed('/later'); + break; + case 'msg': + Get.toNamed('/whisper'); + break; + default: + Get.toNamed('/'); + break; + } + } + static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); diff --git a/lib/utils/cache_manage.dart b/lib/utils/cache_manage.dart index d6bbb816..4603da9f 100644 --- a/lib/utils/cache_manage.dart +++ b/lib/utils/cache_manage.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:path_provider/path_provider.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -50,13 +51,17 @@ class CacheManage { Future getTotalSizeOfFilesInDir(final FileSystemEntity file) async { if (file is File) { int length = await file.length(); - return double.parse(length.toString()); + return length.toDouble(); } if (file is Directory) { - final List children = file.listSync(); double total = 0; - for (final FileSystemEntity child in children) { - total += await getTotalSizeOfFilesInDir(child); + try { + await for (final FileSystemEntity child in file.list()) { + total += await getTotalSizeOfFilesInDir(child); + } + } catch (e) { + // 处理错误,例如记录日志或显示错误消息 + print('读取目录时出错: $e'); } return total; } @@ -77,16 +82,17 @@ class CacheManage { // 清除缓存 Future clearCacheAll() async { - bool cleanStatus = await SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, + bool? cleanStatus = await showDialog( + context: Get.context!, builder: (BuildContext context) { return AlertDialog( title: const Text('提示'), content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'), actions: [ TextButton( - onPressed: (() => {SmartDialog.dismiss()}), + onPressed: () { + Navigator.of(context).pop(false); + }, child: Text( '取消', style: TextStyle(color: Theme.of(context).colorScheme.outline), @@ -94,40 +100,45 @@ class CacheManage { ), TextButton( onPressed: () async { - SmartDialog.dismiss(); - SmartDialog.showLoading(msg: '正在清除...'); - try { - // 清除缓存 图片缓存 - await clearLibraryCache(); - SmartDialog.dismiss().then((res) { - SmartDialog.showToast('清除完成'); - }); - } catch (err) { - SmartDialog.dismiss(); - SmartDialog.showToast(err.toString()); - } + Navigator.of(context).pop(true); }, child: const Text('确认'), - ) + ), ], ); }, - ).then((res) { - return true; - }); - return cleanStatus; + ); + if (cleanStatus != null && cleanStatus) { + SmartDialog.showLoading(msg: '正在清除...'); + try { + // 清除缓存 图片缓存 + await clearLibraryCache(); + SmartDialog.dismiss().then((res) { + SmartDialog.showToast('清除完成'); + }); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + } + return cleanStatus!; } /// 清除 Documents 目录下的 DioCache.db - Future clearApplicationCache() async { - Directory directory = await getApplicationDocumentsDirectory(); - if (directory.existsSync()) { - String dioCacheFileName = - '${directory.path}${Platform.pathSeparator}DioCache.db'; - var dioCacheFile = File(dioCacheFileName); - if (dioCacheFile.existsSync()) { - dioCacheFile.delete(); + Future clearApplicationCache() async { + try { + Directory directory = await getApplicationDocumentsDirectory(); + if (directory.existsSync()) { + String dioCacheFileName = + '${directory.path}${Platform.pathSeparator}DioCache.db'; + File dioCacheFile = File(dioCacheFileName); + if (await dioCacheFile.exists()) { + await dioCacheFile.delete(); + } } + } catch (e) { + // 处理错误,例如记录日志或显示错误消息 + print('清除缓存时出错: $e'); } } diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart index 9d925a0f..ea2eda37 100644 --- a/lib/utils/global_data_cache.dart +++ b/lib/utils/global_data_cache.dart @@ -15,6 +15,7 @@ class GlobalDataCache { late FullScreenGestureMode fullScreenGestureMode; late bool enablePlayerControlAnimation; late List actionTypeSort; + String? wWebid; /// 播放器相关 // 弹幕开关 @@ -59,7 +60,7 @@ class GlobalDataCache { defaultValue: 10); // 设置全局变量 fullScreenGestureMode = FullScreenGestureMode.values[setting.get( SettingBoxKey.fullScreenGestureMode, - defaultValue: FullScreenGestureMode.values.last.index) as int]; + defaultValue: FullScreenGestureMode.fromBottomtoTop.index)]; enablePlayerControlAnimation = setting .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort, diff --git a/lib/utils/subtitle.dart b/lib/utils/subtitle.dart index 1b4088f3..9743711c 100644 --- a/lib/utils/subtitle.dart +++ b/lib/utils/subtitle.dart @@ -1,21 +1,51 @@ +import 'dart:async'; +import 'dart:isolate'; + class SubTitleUtils { // 格式整理 - static String convertToWebVTT(List jsonData) { - String webVTTContent = 'WEBVTT FILE\n\n'; + static Future convertToWebVTT(List jsonData) async { + final receivePort = ReceivePort(); + await Isolate.spawn(_convertToWebVTTIsolate, receivePort.sendPort); - for (int i = 0; i < jsonData.length; i++) { - final item = jsonData[i]; - double from = double.parse(item['from'].toString()); - double to = double.parse(item['to'].toString()); - int sid = (item['sid'] ?? 0) as int; - String content = item['content'] as String; + final sendPort = await receivePort.first as SendPort; + final response = ReceivePort(); + sendPort.send([jsonData, response.sendPort]); - webVTTContent += '$sid\n'; - webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n'; - webVTTContent += '$content\n\n'; + return await response.first as String; + } + + static void _convertToWebVTTIsolate(SendPort sendPort) async { + final port = ReceivePort(); + sendPort.send(port.sendPort); + + await for (final message in port) { + final List jsonData = message[0]; + final SendPort replyTo = message[1]; + + String webVTTContent = 'WEBVTT FILE\n\n'; + int chunkSize = 100; // 每次处理100条数据 + int totalChunks = (jsonData.length / chunkSize).ceil(); + + for (int chunk = 0; chunk < totalChunks; chunk++) { + int start = chunk * chunkSize; + int end = start + chunkSize; + if (end > jsonData.length) end = jsonData.length; + + for (int i = start; i < end; i++) { + final item = jsonData[i]; + double from = double.parse(item['from'].toString()); + double to = double.parse(item['to'].toString()); + int sid = (item['sid'] ?? 0) as int; + String content = item['content'] as String; + + webVTTContent += '$sid\n'; + webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n'; + webVTTContent += '$content\n\n'; + } + } + + replyTo.send(webVTTContent); } - - return webVTTContent; } static String formatTime(num seconds) { diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index e50c295c..7a254f7d 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -306,7 +306,7 @@ class Utils { onPressed: () async { await SmartDialog.dismiss(); launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + Uri.parse('https://www.123684.com/s/9sVqVv-DEZ0A'), mode: LaunchMode.externalApplication, ); }, diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index c3b56ecd..4dd0e0b9 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -19,6 +20,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); + g_autoptr(FlPluginRegistrar) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); g_autoptr(FlPluginRegistrar) media_kit_libs_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "MediaKitLibsLinuxPlugin"); media_kit_libs_linux_plugin_register_with_registrar(media_kit_libs_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 70cdeb4b..dca43a8e 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color flutter_volume_controller + gtk media_kit_libs_linux media_kit_video url_launcher_linux diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8af2f922..b7538b19 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import app_links import audio_service import audio_session import connectivity_plus @@ -22,6 +23,7 @@ import url_launcher_macos import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b9d48c78..9f2036e3 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.2.0" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: ad1a6d598e7e39b46a34f746f9a8b011ee147e4c275d407fa457e7a62f84dd99 + url: "https://pub.flutter-io.cn" + source: hosted + version: "6.3.2" + app_links_linux: + dependency: transitive + description: + name: app_links_linux + sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.3" + app_links_platform_interface: + dependency: transitive + description: + name: app_links_platform_interface + sha256: "05f5379577c513b534a29ddea68176a4d4802c46180ee8e2e966257158772a3f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.0.2" + app_links_web: + dependency: transitive + description: + name: app_links_web + sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555 + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.0.4" appscheme: dependency: "direct main" description: @@ -686,6 +718,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.1.0" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.1.0" hive: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 617b0c7d..4af3459b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.24+1024 +version: 1.0.25+1025 environment: sdk: ">=3.0.0 <4.0.0" @@ -112,6 +112,7 @@ dependencies: flutter_displaymode: ^0.6.0 # scheme跳转 appscheme: ^1.0.8 + app_links: ^6.3.2 # 弹幕 ns_danmaku: git: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d2cff3b2..f477ef65 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -17,6 +18,8 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); ConnectivityPlusWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 5d25e134..1db9f3fe 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links connectivity_plus dynamic_color flutter_volume_controller From 0ec14fe1fa1d395c391873f1bde34e97f1679161 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 16 Oct 2024 22:59:34 +0800 Subject: [PATCH 328/438] opt: HttpError --- lib/common/widgets/http_error.dart | 67 +++++++++---------- lib/pages/bangumi/view.dart | 6 +- lib/pages/blacklist/index.dart | 19 +++--- lib/pages/fan/view.dart | 19 +++--- lib/pages/fav/view.dart | 30 ++++----- lib/pages/follow/widgets/follow_list.dart | 15 +++-- .../follow/widgets/owner_follow_list.dart | 11 ++- lib/pages/follow_search/view.dart | 22 +++--- lib/pages/html/view.dart | 11 ++- lib/pages/member_article/view.dart | 14 ++-- lib/pages/member_search/view.dart | 11 ++- lib/pages/message/like/view.dart | 20 +++--- lib/pages/message/reply/view.dart | 21 +++--- lib/pages/message/system/view.dart | 21 +++--- lib/pages/search/view.dart | 11 ++- lib/pages/search_panel/view.dart | 40 +++++------ .../search_panel/widgets/article_panel.dart | 13 ++-- .../search_panel/widgets/video_panel.dart | 13 ++-- lib/pages/subscription/view.dart | 39 +++++------ 19 files changed, 175 insertions(+), 228 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index 0381319e..305d43c8 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -7,6 +7,7 @@ class HttpError extends StatelessWidget { required this.fn, this.btnText, this.isShowBtn = true, + this.isInSliver = true, super.key, }); @@ -14,46 +15,42 @@ class HttpError extends StatelessWidget { final Function()? fn; final String? btnText; final bool isShowBtn; + final bool isInSliver; @override Widget build(BuildContext context) { - return SliverToBoxAdapter( - child: SizedBox( - height: 400, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SvgPicture.asset( - "assets/images/error.svg", - height: 200, - ), - const SizedBox(height: 30), - Text( - errMsg ?? '请求异常', - textAlign: TextAlign.center, - style: Theme.of(context).textTheme.titleSmall, - ), - const SizedBox(height: 20), - if (isShowBtn) - FilledButton.tonal( - onPressed: () { - fn!(); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { - return Theme.of(context).colorScheme.primary.withAlpha(20); - }), - ), - child: Text( - btnText ?? '点击重试', - style: - TextStyle(color: Theme.of(context).colorScheme.primary), - ), + Color primary = Theme.of(context).colorScheme.primary; + final errorContent = SizedBox( + height: 400, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SvgPicture.asset("assets/images/error.svg", height: 200), + const SizedBox(height: 30), + Text( + errMsg ?? '请求异常', + textAlign: TextAlign.center, + style: Theme.of(context).textTheme.titleSmall, + ), + const SizedBox(height: 20), + if (isShowBtn) + FilledButton.tonal( + onPressed: () => fn?.call(), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + return primary.withAlpha(20); + }), ), - ], - ), + child: Text(btnText ?? '点击重试', style: TextStyle(color: primary)), + ), + ], ), ); + if (isInSliver) { + return SliverToBoxAdapter(child: errorContent); + } else { + return Center(child: errorContent); + } } } diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 9ec72350..4092943b 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -183,8 +183,10 @@ class _BangumiPageState extends State return HttpError( errMsg: data['msg'], fn: () { - _futureBuilderFuture = - _bangumidController.queryBangumiListFeed(); + setState(() { + _futureBuilderFuture = + _bangumidController.queryBangumiListFeed(); + }); }, ); } diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart index fb7e0891..bdd2346f 100644 --- a/lib/pages/blacklist/index.dart +++ b/lib/pages/blacklist/index.dart @@ -77,10 +77,10 @@ class _BlackListPageState extends State { List list = _blackListController.blackList; return Obx( () => list.isEmpty - ? CustomScrollView( - slivers: [ - HttpError(errMsg: '你没有拉黑任何人哦~_~', fn: () => {}) - ], + ? HttpError( + errMsg: '你没有拉黑任何人哦~_~', + fn: () => {}, + isInSliver: false, ) : ListView.builder( controller: scrollController, @@ -119,13 +119,10 @@ class _BlackListPageState extends State { ), ); } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => _blackListController.queryBlacklist(), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () => _blackListController.queryBlacklist(), + isInSliver: false, ); } } else { diff --git a/lib/pages/fan/view.dart b/lib/pages/fan/view.dart index 5d5c02a7..67a1eddd 100644 --- a/lib/pages/fan/view.dart +++ b/lib/pages/fan/view.dart @@ -103,17 +103,14 @@ class _FansPageState extends State { ), ); } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () { - _futureBuilderFuture = - _fansController.queryFans('init'); - }, - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = _fansController.queryFans('init'); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 317ba4d5..9d7534f5 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -112,23 +112,19 @@ class _FavPageState extends State { ), ); } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data?['msg'] ?? '请求异常', - btnText: data?['code'] == -101 ? '去登录' : null, - fn: () { - if (data?['code'] == -101) { - RoutePush.loginRedirectPush(); - } else { - setState(() { - _futureBuilderFuture = _favController.queryFavFolder(); - }); - } - }, - ), - ], + return HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = _favController.queryFavFolder(); + }); + } + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/follow/widgets/follow_list.dart b/lib/pages/follow/widgets/follow_list.dart index d198bec2..dc9bf3c9 100644 --- a/lib/pages/follow/widgets/follow_list.dart +++ b/lib/pages/follow/widgets/follow_list.dart @@ -94,13 +94,14 @@ class _FollowListState extends State { : const CustomScrollView(slivers: [NoData()]), ); } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => widget.ctr.queryFollowings('init'), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = widget.ctr.queryFollowings('init'); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/follow/widgets/owner_follow_list.dart b/lib/pages/follow/widgets/owner_follow_list.dart index ccc11c44..38950cf7 100644 --- a/lib/pages/follow/widgets/owner_follow_list.dart +++ b/lib/pages/follow/widgets/owner_follow_list.dart @@ -112,13 +112,10 @@ class _OwnerFollowListState extends State : const CustomScrollView(slivers: [NoData()]), ); } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => widget.ctr.queryFollowings('init'), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () => widget.ctr.queryFollowings('init'), + isInSliver: false, ); } } else { diff --git a/lib/pages/follow_search/view.dart b/lib/pages/follow_search/view.dart index 6be42676..78cdb11d 100644 --- a/lib/pages/follow_search/view.dart +++ b/lib/pages/follow_search/view.dart @@ -82,10 +82,10 @@ class _FollowSearchPageState extends State { if (snapshot.connectionState == ConnectionState.done) { var data = snapshot.data; if (data == null) { - return CustomScrollView( - slivers: [ - HttpError(errMsg: snapshot.data['msg'], fn: reRequest) - ], + return HttpError( + errMsg: snapshot.data['msg'], + fn: reRequest, + isInSliver: false, ); } if (data['status']) { @@ -101,15 +101,17 @@ class _FollowSearchPageState extends State { ); }), ) - : CustomScrollView( - slivers: [HttpError(errMsg: '未搜索到结果', fn: reRequest)], + : HttpError( + errMsg: '未搜索到结果', + fn: reRequest, + isInSliver: false, ), ); } else { - return CustomScrollView( - slivers: [ - HttpError(errMsg: snapshot.data['msg'], fn: reRequest) - ], + return HttpError( + errMsg: snapshot.data['msg'], + fn: reRequest, + isInSliver: false, ); } } else { diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 9f0c865c..2c3075c8 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -380,13 +380,10 @@ class _HtmlRenderPageState extends State ); } else { // 请求错误 - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + isInSliver: false, ); } } else { diff --git a/lib/pages/member_article/view.dart b/lib/pages/member_article/view.dart index e23e208d..0728a7b6 100644 --- a/lib/pages/member_article/view.dart +++ b/lib/pages/member_article/view.dart @@ -138,16 +138,10 @@ class _MemberArticlePageState extends State { } Widget _buildError(String errMsg) { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - SliverToBoxAdapter( - child: HttpError( - errMsg: errMsg, - fn: () {}, - ), - ), - ], + return HttpError( + errMsg: errMsg, + fn: () {}, + isInSliver: false, ); } diff --git a/lib/pages/member_search/view.dart b/lib/pages/member_search/view.dart index ff0553a2..192c7346 100644 --- a/lib/pages/member_search/view.dart +++ b/lib/pages/member_search/view.dart @@ -164,13 +164,10 @@ class _MemberSearchPageState extends State ), ); } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + isInSliver: false, ); } } else { diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index b30a0c4f..273e49f2 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -85,18 +85,14 @@ class _MessageLikePageState extends State { ); } else { // 请求错误 - return CustomScrollView( - slivers: [ - HttpError( - errMsg: snapshot.data['msg'], - fn: () { - setState(() { - _futureBuilderFuture = - _messageLikeCtr.queryMessageLike(); - }); - }, - ) - ], + return HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = _messageLikeCtr.queryMessageLike(); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index 9776c7f7..b9112bfc 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -82,18 +82,15 @@ class _MessageReplyPageState extends State { ); } else { // 请求错误 - return CustomScrollView( - slivers: [ - HttpError( - errMsg: snapshot.data['msg'], - fn: () { - setState(() { - _futureBuilderFuture = - _messageReplyCtr.queryMessageReply(); - }); - }, - ) - ], + return HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageReplyCtr.queryMessageReply(); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart index ee10b639..a5b46858 100644 --- a/lib/pages/message/system/view.dart +++ b/lib/pages/message/system/view.dart @@ -63,18 +63,15 @@ class _MessageSystemPageState extends State { ); } else { // 请求错误 - return CustomScrollView( - slivers: [ - HttpError( - errMsg: snapshot.data['msg'], - fn: () { - setState(() { - _futureBuilderFuture = - _messageSystemCtr.queryMessageSystem(); - }); - }, - ) - ], + return HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageSystemCtr.queryMessageSystem(); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index 95d3134e..c0f3e5c5 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -187,13 +187,10 @@ class _SearchPageState extends State with RouteAware { ), ); } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ) - ], + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + isInSliver: false, ); } } else { diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index fa669489..f032b12b 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -109,33 +109,25 @@ class _SearchPanelState extends State } }); } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () { - setState(() { - _searchPanelController.onSearch(); - }); - }, - ), - ], + return HttpError( + errMsg: data['msg'], + fn: () { + setState(() { + _searchPanelController.onSearch(); + }); + }, + isInSliver: false, ); } } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: '没有相关数据', - fn: () { - setState(() { - _searchPanelController.onSearch(); - }); - }, - ), - ], + return HttpError( + errMsg: '没有相关数据', + fn: () { + setState(() { + _searchPanelController.onSearch(); + }); + }, + isInSliver: false, ); } } else { diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index be08ed56..75df52f9 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -174,14 +174,11 @@ Widget searchArticlePanel(BuildContext context, ctr, list) { ); }, ) - : CustomScrollView( - slivers: [ - HttpError( - errMsg: '没有数据', - isShowBtn: false, - fn: () => {}, - ) - ], + : HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + isInSliver: false, ), ); } diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 7da6aaaa..6a7fa445 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -46,14 +46,11 @@ class SearchVideoPanel extends StatelessWidget { ); }, ) - : CustomScrollView( - slivers: [ - HttpError( - errMsg: '没有数据', - isShowBtn: false, - fn: () => {}, - ) - ], + : HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + isInSliver: false, ), ), // 分类筛选 diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index cb9993b0..1bd91cdc 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -68,30 +68,27 @@ class _SubPageState extends State { ), ); } else { - return const CustomScrollView( - physics: NeverScrollableScrollPhysics(), - slivers: [HttpError(errMsg: '', btnText: '没有数据', fn: null)], + return const HttpError( + errMsg: '', + btnText: '没有数据', + fn: null, + isInSliver: false, ); } } else { - return CustomScrollView( - physics: const NeverScrollableScrollPhysics(), - slivers: [ - HttpError( - errMsg: data?['msg'] ?? '请求异常', - btnText: data?['code'] == -101 ? '去登录' : null, - fn: () { - if (data?['code'] == -101) { - RoutePush.loginRedirectPush(); - } else { - setState(() { - _futureBuilderFuture = - _subController.querySubFolder(); - }); - } - }, - ), - ], + return HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = _subController.querySubFolder(); + }); + } + }, + isInSliver: false, ); } } else { From 163b8a3013652c661eeb25e32a45b139e4e0ad25 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 17 Oct 2024 00:36:02 +0800 Subject: [PATCH 329/438] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=E6=9B=B4=E5=A4=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/reply/content.dart | 17 ++- .../detail/reply/widgets/reply_item.dart | 115 +++++++++++++----- 2 files changed, 98 insertions(+), 34 deletions(-) diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart index d62a4bca..3a5f90ab 100644 --- a/lib/models/video/reply/content.dart +++ b/lib/models/video/reply/content.dart @@ -1,6 +1,7 @@ class ReplyContent { ReplyContent({ this.message, + this.message2, this.atNameToMid, // @的用户的mid null this.members, // 被@的用户List 如果有的话 [] this.emote, // 表情包 如果有的话 null @@ -13,6 +14,7 @@ class ReplyContent { }); String? message; + String? message2; Map? atNameToMid; List? members; Map? emote; @@ -24,10 +26,17 @@ class ReplyContent { Map? topicsMeta; ReplyContent.fromJson(Map json) { - message = json['message'] + message = message2 = json['message'] .replaceAll('>', '>') .replaceAll('"', '"') - .replaceAll(''', "'"); + .replaceAll(''', "'") + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll(' ', ' '); + atNameToMid = json['at_name_to_mid'] ?? {}; members = json['members'] != null ? json['members'] @@ -39,8 +48,8 @@ class ReplyContent { pictures = json['pictures'] ?? []; vote = json['vote'] ?? {}; richText = json['rich_text'] ?? {}; - // 不包含@ 笔记 图片的时候,文字可折叠 - isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; + // 不包含@ 笔记的时候,文字可折叠 + isText = atNameToMid!.isEmpty && vote!.isEmpty; topicsMeta = json['topics_meta'] ?? {}; } } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 18242fea..52605121 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -238,28 +238,53 @@ class ReplyItem extends StatelessWidget { // title Container( margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), - child: Text.rich( - style: const TextStyle(height: 1.75), - maxLines: - replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999, - overflow: TextOverflow.ellipsis, - TextSpan( - children: [ - if (replyItem!.isTop!) - const WidgetSpan( - alignment: PlaceholderAlignment.top, - child: PBadge( - text: 'TOP', - size: 'small', - stack: 'normal', - type: 'line', - fs: 9, + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints boxConstraints) { + String text = replyItem?.content?.message ?? ''; + bool didExceedMaxLines = false; + final double maxWidth = boxConstraints.maxWidth; + TextPainter? textPainter; + final int maxLines = + replyItem!.content!.isText! && replyLevel == '1' ? 6 : 999; + try { + textPainter = TextPainter( + text: TextSpan(text: text), + maxLines: maxLines, + textDirection: Directionality.of(context), + ); + textPainter.layout(maxWidth: maxWidth); + didExceedMaxLines = textPainter.didExceedMaxLines; + } catch (e) { + debugPrint('Error while measuring text: $e'); + didExceedMaxLines = false; + } + return Text.rich( + style: const TextStyle(height: 1.75), + TextSpan( + children: [ + if (replyItem!.isTop!) + const WidgetSpan( + alignment: PlaceholderAlignment.top, + child: PBadge( + text: 'TOP', + size: 'small', + stack: 'normal', + type: 'line', + fs: 9, + ), ), + buildContent( + context, + replyItem!, + replyReply, + null, + didExceedMaxLines, + textPainter, ), - buildContent(context, replyItem!, replyReply, null), - ], - ), - ), + ], + ), + ); + }), ), // 操作区域 bottonAction(context, replyItem!.replyControl, replySave), @@ -465,8 +490,8 @@ class ReplyItemRow extends StatelessWidget { fs: 9, ), ), - buildContent( - context, replies![i], replyReply, replyItem), + buildContent(context, replies![i], replyReply, + replyItem, false, null), ], ), ), @@ -508,7 +533,13 @@ class ReplyItemRow extends StatelessWidget { } InlineSpan buildContent( - BuildContext context, replyItem, replyReply, fReplyItem) { + BuildContext context, + replyItem, + replyReply, + fReplyItem, + bool didExceedMaxLines, + TextPainter? textPainter, +) { final String routePath = Get.currentRoute; bool isVideoPage = routePath.startsWith('/video'); ColorScheme colorScheme = Theme.of(context).colorScheme; @@ -519,6 +550,25 @@ InlineSpan buildContent( final content = replyItem.content; final List spanChilds = []; + if (didExceedMaxLines && content.message != '') { + final textSize = textPainter!.size; + var position = textPainter.getPositionForOffset( + Offset( + textSize.width, + textSize.height, + ), + ); + final endOffset = textPainter.getOffsetBefore(position.offset); + + if (endOffset != null && endOffset > 0) { + content.message = content.message.substring(0, endOffset); + } else { + content.message = content.message.substring(0, position.offset); + } + } else { + content.message = content.message2; + } + // 投票 if (content.vote.isNotEmpty) { content.message.splitMapJoin(RegExp(r"\{vote:.*?\}"), @@ -547,13 +597,6 @@ InlineSpan buildContent( }); } content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' '); - content.message = content.message - .replaceAll('&', '&') - .replaceAll('<', '<') - .replaceAll('>', '>') - .replaceAll('"', '"') - .replaceAll(''', "'") - .replaceAll(' ', ' '); // 构建正则表达式 final List specialTokens = [ ...content.emote.keys, @@ -874,6 +917,18 @@ InlineSpan buildContent( } } } + + if (didExceedMaxLines) { + spanChilds.add( + TextSpan( + text: '\n查看更多', + style: TextStyle( + color: colorScheme.primary, + ), + ), + ); + } + // 图片渲染 if (content.pictures.isNotEmpty) { final List picList = []; From 113a76894aeb8eff00210bc0fd5446d4edb8b263 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 17 Oct 2024 00:40:42 +0800 Subject: [PATCH 330/438] =?UTF-8?q?feat:=20=E5=B8=A6=E5=9B=BE=E8=AF=84?= =?UTF-8?q?=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 6 + lib/http/api.dart | 3 + lib/http/reply.dart | 43 ++++++ lib/http/video.dart | 29 +++- lib/pages/video/detail/reply_new/view.dart | 136 +++++++++++++++++- linux/flutter/generated_plugin_registrant.cc | 4 + linux/flutter/generated_plugins.cmake | 1 + macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 96 +++++++++++++ pubspec.yaml | 2 + .../flutter/generated_plugin_registrant.cc | 3 + windows/flutter/generated_plugins.cmake | 1 + 12 files changed, 319 insertions(+), 7 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index a400600f..ef17d378 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -27,6 +27,8 @@ PODS: - Flutter - GT3Captcha-iOS - GT3Captcha-iOS (0.15.8.3) + - image_picker_ios (0.0.1): + - Flutter - media_kit_libs_ios_video (1.0.4): - Flutter - media_kit_native_event_loop (1.0.0): @@ -77,6 +79,7 @@ DEPENDENCIES: - flutter_volume_controller (from `.symlinks/plugins/flutter_volume_controller/ios`) - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`) - gt3_flutter_plugin (from `.symlinks/plugins/gt3_flutter_plugin/ios`) + - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) @@ -124,6 +127,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/fluttertoast/ios" gt3_flutter_plugin: :path: ".symlinks/plugins/gt3_flutter_plugin/ios" + image_picker_ios: + :path: ".symlinks/plugins/image_picker_ios/ios" media_kit_libs_ios_video: :path: ".symlinks/plugins/media_kit_libs_ios_video/ios" media_kit_native_event_loop: @@ -173,6 +178,7 @@ SPEC CHECKSUMS: FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 + image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e diff --git a/lib/http/api.dart b/lib/http/api.dart index df0b9c85..e6c09870 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -592,4 +592,7 @@ class Api { /// 直播间记录 static const String liveRoomEntry = '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction'; + + /// 图片上传 + static const String uploadImage = '/x/dynamic/feed/draw/upload_bfs'; } diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 880f9072..ae27ede2 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -1,3 +1,6 @@ +import 'dart:io'; +import 'package:dio/dio.dart'; +import 'package:image_picker/image_picker.dart'; import '../models/video/reply/data.dart'; import '../models/video/reply/emote.dart'; import 'api.dart'; @@ -115,4 +118,44 @@ class ReplyHttp { }; } } + + // 图片上传 + static Future uploadImage({required XFile xFile, String type = 'im'}) async { + var formData = FormData.fromMap({ + 'file_up': await xFileToMultipartFile(xFile), + 'biz': type, + 'csrf': await Request.getCsrf(), + 'build': 0, + 'mobi_app': 'web', + }); + var res = await Request().post( + Api.uploadImage, + data: formData, + ); + if (res.data['code'] == 0) { + var data = res.data['data']; + data['img_src'] = data['image_url']; + data['img_width'] = data['image_width']; + data['img_height'] = data['image_height']; + data.remove('image_url'); + data.remove('image_width'); + data.remove('image_height'); + return { + 'status': true, + 'data': data, + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } + + static Future xFileToMultipartFile(XFile xFile) async { + var file = File(xFile.path); + var bytes = await file.readAsBytes(); + return MultipartFile.fromBytes(bytes, filename: xFile.name); + } } diff --git a/lib/http/video.dart b/lib/http/video.dart index 160f5db2..12c0dce7 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:developer'; +import 'package:dio/dio.dart'; import 'package:hive/hive.dart'; import '../common/constants.dart'; import '../models/common/reply_type.dart'; @@ -343,18 +345,35 @@ class VideoHttp { required String message, int? root, int? parent, + List>? pictures, }) async { if (message == '') { return {'status': false, 'data': [], 'msg': '请输入评论内容'}; } - var res = await Request().post(Api.replyAdd, queryParameters: { - 'type': type.index, + var params = { + 'plat': 1, 'oid': oid, - 'root': root == null || root == 0 ? '' : root, - 'parent': parent == null || parent == 0 ? '' : parent, + 'type': type.index, + // 'root': root == null || root == 0 ? '' : root, + // 'parent': parent == null || parent == 0 ? '' : parent, 'message': message, + 'at_name_to_mid': {}, + if (pictures != null) 'pictures': jsonEncode(pictures), + 'gaia_source': 'main_web', 'csrf': await Request.getCsrf(), - }); + }; + Map sign = await WbiSign().makSign(params); + params.remove('wts'); + params.remove('w_rid'); + FormData formData = FormData.fromMap({...params}); + var res = await Request().post( + Api.replyAdd, + queryParameters: { + 'w_rid': sign['w_rid'], + 'wts': sign['wts'], + }, + data: formData, + ); log(res.toString()); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 0f04455f..72a87d89 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -1,13 +1,18 @@ import 'dart:async'; +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:image_picker/image_picker.dart'; import 'package:pilipala/http/dynamics.dart'; +import 'package:pilipala/http/reply.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/emote.dart'; import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/emote/index.dart'; +import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart'; +import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'toolbar_icon_button.dart'; @@ -44,6 +49,9 @@ class _VideoReplyNewDialogState extends State RxBool isForward = false.obs; RxBool showForward = false.obs; RxString message = ''.obs; + final ImagePicker _picker = ImagePicker(); + RxList imageList = [''].obs; + List> pictures = []; @override void initState() { @@ -60,6 +68,7 @@ class _VideoReplyNewDialogState extends State if (routePath.startsWith('/video')) { showForward.value = true; } + imageList.clear(); } _autoFocus() async { @@ -90,6 +99,7 @@ class _VideoReplyNewDialogState extends State message: widget.replyItem != null && widget.replyItem!.root != 0 ? ' 回复 @${widget.replyItem!.member!.uname!} : ${message.value}' : message.value, + pictures: pictures, ); if (result['status']) { SmartDialog.showToast(result['data']['success_toast']); @@ -125,6 +135,62 @@ class _VideoReplyNewDialogState extends State ); } + void onChooseImage() async { + if (mounted) { + try { + final XFile? pickedFile = + await _picker.pickImage(source: ImageSource.gallery); + print('选择图片成功: ${pickedFile}'); + var res = await ReplyHttp.uploadImage(xFile: pickedFile!); + if (res['status']) { + imageList.add(res['data']['img_src']); + pictures.add(res['data']); + print('imageList: $imageList'); + print('pictures: $pictures'); + } + } catch (e) { + print('选择图片失败: $e'); + } + } + } + + void onPreviewImg(picList, initIndex, context) { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); + } + @override void didChangeMetrics() { super.didChangeMetrics(); @@ -175,10 +241,11 @@ class _VideoReplyNewDialogState extends State ), child: Column( mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, children: [ ConstrainedBox( constraints: const BoxConstraints( - maxHeight: 200, + maxHeight: 250, minHeight: 120, ), child: Container( @@ -209,6 +276,64 @@ class _VideoReplyNewDialogState extends State ), ), ), + Obx( + () => Padding( + padding: const EdgeInsets.fromLTRB(12, 10, 12, 10), + child: SizedBox( + height: 65, // 固定高度以避免无限扩展 + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: imageList.length, + itemBuilder: (context, index) { + final url = imageList[index]; + return url != '' + ? Container( + width: 65, + height: 65, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .secondaryContainer, + borderRadius: + const BorderRadius.all(Radius.circular(6))), + child: InkWell( + onTap: () => + onPreviewImg(imageList, index, context), + onLongPress: () { + imageList.removeAt(index); + }, + child: CachedNetworkImage( + imageUrl: url, + width: 65, + height: 65, + fit: BoxFit.cover, + ), + ), + ) + : const SizedBox(); + }, + separatorBuilder: (context, index) => + const SizedBox(width: 8.0), + ), + ), + ), + ), + Obx( + () => Visibility( + visible: imageList.isNotEmpty, + child: Padding( + padding: const EdgeInsets.fromLTRB(12, 0, 12, 10), + child: Text( + '点击预览,长按删除', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: 12, + ), + ), + ), + ), + ), Divider( height: 1, color: Theme.of(context).dividerColor.withOpacity(0.1), @@ -240,7 +365,7 @@ class _VideoReplyNewDialogState extends State toolbarType: toolbarType, selected: toolbarType == 'input', ), - const SizedBox(width: 20), + const SizedBox(width: 10), ToolbarIconButton( onPressed: () { if (toolbarType == 'input') { @@ -254,6 +379,13 @@ class _VideoReplyNewDialogState extends State toolbarType: toolbarType, selected: toolbarType == 'emote', ), + const SizedBox(width: 10), + ToolbarIconButton( + onPressed: onChooseImage, + icon: const Icon(Icons.photo, size: 22), + toolbarType: toolbarType, + selected: toolbarType == 'picture', + ), const SizedBox(width: 6), Obx( () => showForward.value diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index c3b56ecd..1b712752 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,6 +7,7 @@ #include "generated_plugin_registrant.h" #include +#include #include #include #include @@ -16,6 +17,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) flutter_volume_controller_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterVolumeControllerPlugin"); flutter_volume_controller_plugin_register_with_registrar(flutter_volume_controller_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 70cdeb4b..29b0d451 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + file_selector_linux flutter_volume_controller media_kit_libs_linux media_kit_video diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 8af2f922..f52be498 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import audio_session import connectivity_plus import device_info_plus import dynamic_color +import file_selector_macos import flutter_volume_controller import media_kit_libs_macos_video import media_kit_video @@ -27,6 +28,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin")) MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin")) MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin")) diff --git a/pubspec.lock b/pubspec.lock index b9d48c78..23a8f1ef 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -513,6 +513,38 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "7.0.0" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "045d372bf19b02aeb69cacf8b4009555fb5f6f0b7ad8016e5f46dd1387ddd492" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.2+1" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: cb284e267f8e2a45a904b5c094d2ba51d0aabfc20b1538ab786d9ef7dc2bf75c + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.4+1" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.6.2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "2ad726953f6e8affbc4df8dc78b77c3b4a060967a291e528ef72ae846c60fb69" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.9.3+2" fixnum: dependency: transitive description: @@ -758,6 +790,70 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.1.3" + image_picker: + dependency: "direct main" + description: + name: image_picker + sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.2" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "0f57fee1e8bfadf8cc41818bbcd7f72e53bb768a54d9496355d5e8a5681a19f1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.12+1" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "65d94623e15372c5c51bebbcb820848d7bcb323836e12dfdba60b5d3a8b39e50" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.5" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: "6703696ad49f5c3c8356d576d7ace84d1faf459afb07accbb0fae780753ff447" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.8.12" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "4ed1d9bb36f7cd60aa6e6cd479779cc56a4cb4e4de8f49d487b1aaad831300fa" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.1+1" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: "3f5ad1e8112a9a6111c46d0b57a7be2286a9a07fc6e1976fdf5be2bd31d4ff62" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.1+1" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "9ec26d410ff46f483c5519c29c02ef0e02e13a543f882b152d4bfd2f06802f80" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.10.0" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.2.1+1" intl: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 617b0c7d..79c16273 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -151,6 +151,8 @@ dependencies: brotli: ^0.6.0 # 文本语法高亮 re_highlight: ^0.0.3 + # 图片选择器 + image_picker: ^1.1.2 dev_dependencies: flutter_test: diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index d2cff3b2..29721071 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -21,6 +22,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); FlutterVolumeControllerPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlutterVolumeControllerPluginCApi")); MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar( diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 5d25e134..d3c91196 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -5,6 +5,7 @@ list(APPEND FLUTTER_PLUGIN_LIST connectivity_plus dynamic_color + file_selector_windows flutter_volume_controller media_kit_libs_windows_video media_kit_video From b60e693e2e4ff1044724f9fde55f57dfc8b743df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 17 Oct 2024 23:24:45 +0800 Subject: [PATCH 331/438] mod: reply api --- lib/http/api.dart | 2 +- lib/http/reply.dart | 19 ++-- lib/models/video/reply/data.dart | 94 +++++++++++++++++++- lib/pages/dynamics/detail/controller.dart | 14 +-- lib/pages/html/controller.dart | 14 +-- lib/pages/video/detail/reply/controller.dart | 26 ++---- 6 files changed, 123 insertions(+), 46 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index d7d60160..3d2a7efa 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -104,7 +104,7 @@ class Api { // 评论列表 // https://api.bilibili.com/x/v2/reply/main?csrf=6e22efc1a47225ea25f901f922b5cfdd&mode=3&oid=254175381&pagination_str=%7B%22offset%22:%22%22%7D&plat=1&seek_rpid=0&type=11 - static const String replyList = '/x/v2/reply'; + static const String replyList = '/x/v2/reply/main'; // 楼中楼 static const String replyReplyList = '/x/v2/reply/reply'; diff --git a/lib/http/reply.dart b/lib/http/reply.dart index c07d9e81..4b8061ad 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -1,3 +1,5 @@ +import 'dart:convert'; + import '../models/video/reply/data.dart'; import '../models/video/reply/emote.dart'; import 'api.dart'; @@ -6,17 +8,16 @@ import 'init.dart'; class ReplyHttp { static Future replyList({ required int oid, - required int pageNum, + required String nextOffset, required int type, int? ps, int sort = 1, }) async { var res = await Request().get(Api.replyList, data: { 'oid': oid, - 'pn': pageNum, 'type': type, - 'sort': sort, - 'ps': ps ?? 20 + 'pagination_str': jsonEncode({'offset': nextOffset}), + 'mode': sort + 2, }); if (res.data['code'] == 0) { return { @@ -52,19 +53,13 @@ class ReplyHttp { if (res.data['code'] == 0) { return { 'status': true, - 'data': ReplyData.fromJson(res.data['data']), + 'data': ReplyReplyData.fromJson(res.data['data']), }; } else { - Map errMap = { - -400: '请求错误', - -404: '无此项', - 12002: '评论区已关闭', - 12009: '评论主体的type不合法', - }; return { 'status': false, 'date': [], - 'msg': errMap[res.data['code']] ?? '请求异常', + 'msg': res.data['message'], }; } } diff --git a/lib/models/video/reply/data.dart b/lib/models/video/reply/data.dart index cc419777..6e069c50 100644 --- a/lib/models/video/reply/data.dart +++ b/lib/models/video/reply/data.dart @@ -6,6 +6,98 @@ import 'upper.dart'; class ReplyData { ReplyData({ + this.cursor, + this.config, + this.replies, + this.topReplies, + this.upper, + }); + + ReplyCursor? cursor; + ReplyConfig? config; + late List? replies; + late List? topReplies; + ReplyUpper? upper; + + ReplyData.fromJson(Map json) { + cursor = ReplyCursor.fromJson(json['cursor']); + config = ReplyConfig.fromJson(json['config']); + replies = json['replies'] != null + ? json['replies'] + .map( + (item) => ReplyItemModel.fromJson(item, json['upper']['mid'])) + .toList() + : []; + topReplies = json['top_replies'] != null + ? json['top_replies'] + .map((item) => ReplyItemModel.fromJson( + item, json['upper']['mid'], + isTopStatus: true)) + .toList() + : []; + upper = ReplyUpper.fromJson(json['upper']); + } +} + +class ReplyCursor { + ReplyCursor({ + this.isBegin, + this.prev, + this.next, + this.isEnd, + this.mode, + this.modeText, + this.allCount, + this.supportMode, + this.name, + this.paginationReply, + this.sessionId, + }); + + bool? isBegin; + int? prev; + int? next; + bool? isEnd; + int? mode; + String? modeText; + int? allCount; + List? supportMode; + String? name; + PaginationReply? paginationReply; + String? sessionId; + + ReplyCursor.fromJson(Map json) { + isBegin = json['is_begin']; + prev = json['prev']; + next = json['next']; + isEnd = json['is_end']; + mode = json['mode']; + modeText = json['mode_text']; + allCount = json['all_count']; + supportMode = json['support_mode'].cast(); + name = json['name']; + paginationReply = json['pagination_reply'] != null + ? PaginationReply.fromJson(json['pagination_reply']) + : null; + sessionId = json['session_id']; + } +} + +class PaginationReply { + PaginationReply({ + this.nextOffset, + this.prevOffset, + }); + String? nextOffset; + String? prevOffset; + PaginationReply.fromJson(Map json) { + nextOffset = json['next_offset']; + prevOffset = json['prev_offset']; + } +} + +class ReplyReplyData { + ReplyReplyData({ this.page, this.config, this.replies, @@ -19,7 +111,7 @@ class ReplyData { late List? topReplies; ReplyUpper? upper; - ReplyData.fromJson(Map json) { + ReplyReplyData.fromJson(Map json) { page = ReplyPage.fromJson(json['page']); config = ReplyConfig.fromJson(json['config']); replies = json['replies'] != null diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index f34de061..f2e990cd 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -14,7 +14,7 @@ class DynamicDetailController extends GetxController { int? type; dynamic item; int? floor; - int currentPage = 0; + String nextOffset = ""; bool isLoadingMore = false; RxString noMore = ''.obs; RxList replyList = [].obs; @@ -49,25 +49,25 @@ class DynamicDetailController extends GetxController { Future queryReplyList({reqType = 'init'}) async { if (reqType == 'init') { - currentPage = 0; + nextOffset = ""; } var res = await ReplyHttp.replyList( oid: oid!, - pageNum: currentPage + 1, + nextOffset: nextOffset, type: type!, sort: _sortType.index, ); if (res['status']) { List replies = res['data'].replies; - acount.value = res['data'].page.acount; + acount.value = res['data'].cursor.allCount; + nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; if (replies.isNotEmpty) { - currentPage++; noMore.value = '加载中...'; - if (replies.length < 20) { + if (res['data'].cursor.isEnd == true) { noMore.value = '没有更多了'; } } else { - noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; + noMore.value = nextOffset == "" ? '还没有评论' : '没有更多了'; } if (reqType == 'init') { // 添加置顶回复 diff --git a/lib/pages/html/controller.dart b/lib/pages/html/controller.dart index 1175ce29..7ba41966 100644 --- a/lib/pages/html/controller.dart +++ b/lib/pages/html/controller.dart @@ -15,7 +15,7 @@ class HtmlRenderController extends GetxController { RxInt oid = (-1).obs; late Map response; int? floor; - int currentPage = 0; + String nextOffset = ""; bool isLoadingMore = false; RxString noMore = ''.obs; RxList replyList = [].obs; @@ -52,21 +52,21 @@ class HtmlRenderController extends GetxController { Future queryReplyList({reqType = 'init'}) async { var res = await ReplyHttp.replyList( oid: oid.value, - pageNum: currentPage + 1, + nextOffset: nextOffset, type: type, sort: _sortType.index, ); if (res['status']) { List replies = res['data'].replies; - acount.value = res['data'].page.acount; + acount.value = res['data'].cursor.allCount; + nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; if (replies.isNotEmpty) { - currentPage++; noMore.value = '加载中...'; - if (replies.length < 20) { + if (res['data'].cursor.isEnd == true) { noMore.value = '没有更多了'; } } else { - noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; + noMore.value = nextOffset == "" ? '还没有评论' : '没有更多了'; } if (reqType == 'init') { // 添加置顶回复 @@ -102,7 +102,7 @@ class HtmlRenderController extends GetxController { } sortTypeTitle.value = _sortType.titles; sortTypeLabel.value = _sortType.labels; - currentPage = 0; + nextOffset = ""; replyList.clear(); queryReplyList(reqType: 'init'); } diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index c1929434..bb1d6a97 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -21,11 +21,9 @@ class VideoReplyController extends GetxController { // rpid 请求楼中楼回复 String? rpid; RxList replyList = [].obs; - // 当前页 - int currentPage = 0; + String nextOffset = ""; bool isLoadingMore = false; RxString noMore = ''.obs; - int ps = 20; RxInt count = 0.obs; // 当前回复的回复 ReplyItemModel? currentReplyItem; @@ -57,7 +55,7 @@ class VideoReplyController extends GetxController { } isLoadingMore = true; if (type == 'init') { - currentPage = 0; + nextOffset = ''; noMore.value = ''; } if (noMore.value == '没有更多了') { @@ -66,28 +64,20 @@ class VideoReplyController extends GetxController { } final res = await ReplyHttp.replyList( oid: aid!, - pageNum: currentPage + 1, - ps: ps, + nextOffset: nextOffset, type: ReplyType.video.index, sort: _sortType.index, ); if (res['status']) { final List replies = res['data'].replies; + nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; if (replies.isNotEmpty) { noMore.value = '加载中...'; - - /// 第一页回复数小于20 - if (currentPage == 0 && replies.length < 18) { - noMore.value = '没有更多了'; - } - currentPage++; - - if (replyList.length == res['data'].page.acount) { + if (res['data'].cursor.isEnd == true) { noMore.value = '没有更多了'; } } else { - // 未登录状态replies可能返回null - noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; + noMore.value = nextOffset == "" ? '还没有评论' : '没有更多了'; } if (type == 'init') { // 添加置顶回复 @@ -99,7 +89,7 @@ class VideoReplyController extends GetxController { } } replies.insertAll(0, res['data'].topReplies); - count.value = res['data'].page.count; + count.value = res['data'].cursor.allCount; replyList.value = replies; } else { replyList.addAll(replies); @@ -130,7 +120,7 @@ class VideoReplyController extends GetxController { } sortTypeTitle.value = _sortType.titles; sortTypeLabel.value = _sortType.labels; - currentPage = 0; + nextOffset = ""; noMore.value = ''; replyList.clear(); queryReplyList(type: 'init'); From fa79b92f826be0811d62cd312e736659496c3653 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 18 Oct 2024 00:09:34 +0800 Subject: [PATCH 332/438] opt: http --- lib/common/constants.dart | 2 -- lib/http/html.dart | 12 ------- lib/http/init.dart | 30 +++++++++------- lib/http/interceptor.dart | 18 +--------- lib/http/user.dart | 72 --------------------------------------- 5 files changed, 18 insertions(+), 116 deletions(-) diff --git a/lib/common/constants.dart b/lib/common/constants.dart index cac13688..0607206c 100644 --- a/lib/common/constants.dart +++ b/lib/common/constants.dart @@ -15,6 +15,4 @@ class Constants { // 59b43e04ad6965f34319062b478f83dd TV端 static const String appSec = '59b43e04ad6965f34319062b478f83dd'; static const String thirdSign = '04224646d1fea004e79606d3b038c84a'; - static const String thirdApi = - 'https://www.mcbbs.net/template/mcbbs/image/special_photo_bg.png'; } diff --git a/lib/http/html.dart b/lib/http/html.dart index 100887e5..87adacb9 100644 --- a/lib/http/html.dart +++ b/lib/http/html.dart @@ -21,7 +21,6 @@ class HtmlHttp { } try { Document rootTree = parse(response.data); - // log(response.data.body.toString()); Element body = rootTree.body!; Element appDom = body.querySelector('#app')!; Element authorHeader = appDom.querySelector('.fixed-author-header')!; @@ -52,7 +51,6 @@ class HtmlHttp { .className .split(' ')[1] .split('-')[2]; - // List imgList = opusDetail.querySelectorAll('bili-album__preview__picture__img'); return { 'status': true, 'avatar': avatar, @@ -76,20 +74,10 @@ class HtmlHttp { Element body = rootTree.body!; Element appDom = body.querySelector('#app')!; Element authorHeader = appDom.querySelector('.up-left')!; - // 头像 - // String avatar = - // authorHeader.querySelector('.bili-avatar-img')!.attributes['data-src']!; - // print(avatar); - // avatar = 'https:${avatar.split('@')[0]}'; String uname = authorHeader.querySelector('.up-name')!.text.trim(); // 动态详情 Element opusDetail = appDom.querySelector('.article-content')!; // 发布时间 - // String updateTime = - // opusDetail.querySelector('.opus-module-author__pub__text')!.text; - // print(updateTime); - - // String opusContent = opusDetail.querySelector('#read-article-holder')!.innerHtml; RegExp digitRegExp = RegExp(r'\d+'); diff --git a/lib/http/init.dart b/lib/http/init.dart index abe8d019..3117666e 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -8,7 +8,6 @@ import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:dio_cookie_manager/dio_cookie_manager.dart'; -// import 'package:dio_http2_adapter/dio_http2_adapter.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/utils/id_utils.dart'; import '../utils/storage.dart'; @@ -171,15 +170,6 @@ class Request { dio = Dio(options); - /// fix 第三方登录 302重定向 跟iOS代理问题冲突 - // ..httpClientAdapter = Http2Adapter( - // ConnectionManager( - // idleTimeout: const Duration(milliseconds: 10000), - // onClientCreate: (_, ClientSetting config) => - // config.onBadCertificate = (_) => true, - // ), - // ); - /// 设置代理 if (enableSystemProxy) { dio.httpClientAdapter = IOHttpClientAdapter( @@ -247,11 +237,26 @@ class Request { } } + /* + * get请求 + */ + getWithoutCookie(url, {data}) { + return get( + url, + data: data, + options: Options( + headers: { + 'cookie': 'buvid3= ; b_nut= ; sid= ', + 'user-agent': headerUa(type: 'pc'), + }, + ), + ); + } + /* * post请求 */ post(url, {data, queryParameters, options, cancelToken, extra}) async { - // print('post-data: $data'); Response response; try { response = await dio.post( @@ -262,7 +267,6 @@ class Request { options ?? Options(contentType: Headers.formUrlEncodedContentType), cancelToken: cancelToken, ); - // print('post success: ${response.data}'); return response; } on DioException catch (e) { Response errResponse = Response( @@ -318,7 +322,7 @@ class Request { } } else { headerUa = - 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.2 Safari/605.1.15'; + 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36'; } return headerUa; } diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 259a3bf9..9f9bc1c1 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -3,8 +3,6 @@ import 'package:connectivity_plus/connectivity_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:hive/hive.dart'; -import '../utils/storage.dart'; class ApiInterceptor extends Interceptor { @override @@ -19,21 +17,7 @@ class ApiInterceptor extends Interceptor { @override void onResponse(Response response, ResponseInterceptorHandler handler) { try { - if (response.statusCode == 302) { - final List locations = response.headers['location']!; - if (locations.isNotEmpty) { - if (locations.first.startsWith('https://www.mcbbs.net')) { - final Uri uri = Uri.parse(locations.first); - final String? accessKey = uri.queryParameters['access_key']; - final String? mid = uri.queryParameters['mid']; - try { - Box localCache = GStrorage.localCache; - localCache.put(LocalCacheKey.accessKey, - {'mid': mid, 'value': accessKey}); - } catch (_) {} - } - } - } + // 在响应之后处理数据 } catch (err) { print('ApiInterceptor: $err'); } diff --git a/lib/http/user.dart b/lib/http/user.dart index 26b79523..b4e52cde 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -1,10 +1,6 @@ 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'; import '../models/user/fav_folder.dart'; @@ -218,25 +214,6 @@ class UserHttp { } } - // 获取用户凭证 失效 - static Future thirdLogin() async { - var res = await Request().get( - 'https://passport.bilibili.com/login/app/third', - data: { - 'appkey': Constants.appKey, - 'api': Constants.thirdApi, - 'sign': Constants.thirdSign, - }, - ); - try { - if (res.data['code'] == 0 && res.data['data']['has_login'] == 1) { - Request().get(res.data['data']['confirm_uri']); - } - } catch (err) { - SmartDialog.showNotify(msg: '获取用户凭证: $err', notifyType: NotifyType.error); - } - } - // 清空稍后再看 static Future toViewClear() async { var res = await Request().post( @@ -283,30 +260,6 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } - // // 相互关系查询 - // static Future relationSearch(int mid) async { - // Map params = await WbiSign().makSign({ - // 'mid': mid, - // 'token': '', - // 'platform': 'web', - // 'web_location': 1550101, - // }); - // var res = await Request().get( - // Api.relationSearch, - // data: { - // 'mid': mid, - // 'w_rid': params['w_rid'], - // 'wts': params['wts'], - // }, - // ); - // if (res.data['code'] == 0) { - // // relation 主动状态 - // // 被动状态 - // return {'status': true, 'data': res.data['data']}; - // } else { - // return {'status': false, 'msg': res.data['message']}; - // } - // } // 搜索历史记录 static Future searchHistory( @@ -436,31 +389,6 @@ class UserHttp { } } - // 稍后再看播放全部 - // 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'