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/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/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/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/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('未登录'), ); } } 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/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/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/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 f26eff77..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!; @@ -281,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/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/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 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