diff --git a/README.md b/README.md index 228d17bb..af462d6f 100644 --- a/README.md +++ b/README.md @@ -26,15 +26,50 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 ```bash -[✓] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale - zh-Hans-CN) +[!] Flutter (Channel [user-branch], 3.19.6, on macOS 14.6.1 23G93 darwin-arm64, + locale zh-Hans-CN) + ! Flutter version 3.19.6 on channel [user-branch] at + /Users/rr/Documents/sdk/flutter + Currently on an unknown channel. Run `flutter channel` to switch to an + official channel. + If that doesn't fix the issue, reinstall Flutter by following instructions + at https://flutter.dev/docs/get-started/install. + ! Upstream repository unknown source is not a standard remote. + Set environment variable "FLUTTER_GIT_URL" to unknown source to dismiss + this error. + • Framework revision 54e66469a9 (8 months ago), 2024-04-17 13:08:03 -0700 + • Engine revision c4cd48e186 + • Dart version 3.3.4 + • DevTools version 2.31.1 + • Pub download mirror https://pub.flutter-io.cn + • Flutter download mirror https://storage.flutter-io.cn + • If those were intentional, you can disregard the above warnings; however + it is recommended to use "git" directly to perform update checks and + upgrades. + [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) + • Android SDK at /Users/rr/Library/Android/sdk + • Platform android-34, build-tools 34.0.0 + • Java binary at: /Applications/Android + Studio.app/Contents/jbr/Contents/Home/bin/java + • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11) + • All Android licenses accepted. + [✓] Xcode - develop for iOS and macOS (Xcode 15.1) + • Xcode at /Applications/Xcode.app/Contents/Developer + • Build 15C65 + • CocoaPods version 1.14.3 + [✓] Chrome - develop for the web -[✓] Android Studio (version 2022.3) -[✓] VS Code (version 1.87.2) -[✓] Connected device (3 available) -[✓] Network resources + • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome + +[✓] Android Studio (version 2024.2) + • Android Studio at /Applications/Android Studio.app/Contents + • Flutter plugin can be installed from: + 🔨 https://plugins.jetbrains.com/plugin/9212-flutter + • Dart plugin can be installed from: + 🔨 https://plugins.jetbrains.com/plugin/6351-dart + • Java version OpenJDK Runtime Environment (build 21.0.3+-79915917-b509.11) ``` diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/common/widgets/group_panel.dart similarity index 96% rename from lib/pages/video/detail/introduction/widgets/group_panel.dart rename to lib/common/widgets/group_panel.dart index 89b0c9a8..932dec6f 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/common/widgets/group_panel.dart @@ -1,12 +1,10 @@ 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/common/widgets/http_error.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/member/tags.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; class GroupPanel extends StatefulWidget { final int? mid; @@ -18,7 +16,6 @@ class GroupPanel extends StatefulWidget { } class _GroupPanelState extends State { - final Box localCache = GStorage.localCache; late Future _futureBuilderFuture; late List tagsList; bool showDefault = true; @@ -137,7 +134,7 @@ class _GroupPanelState extends State { left: 20, right: 20, top: 12, - bottom: MediaQuery.of(context).padding.bottom + 12, + bottom: MediaQuery.paddingOf(context).bottom + 12, ), child: Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/lib/http/init.dart b/lib/http/init.dart index 9c0f368c..428cdaba 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -1,13 +1,11 @@ // ignore_for_file: avoid_print import 'dart:async'; -import 'dart:developer'; import 'dart:io'; 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:hive/hive.dart'; -import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/utils/id_utils.dart'; import '../utils/storage.dart'; import '../utils/utils.dart'; @@ -39,18 +37,7 @@ class Request { dio.interceptors.add(cookieManager); final List cookie = await cookieManager.cookieJar .loadForRequest(Uri.parse(HttpString.baseUrl)); - final UserInfoData? userInfo = userInfoCache.get('userInfoCache'); - if (userInfo != null && userInfo.mid != null) { - final List cookie2 = await cookieManager.cookieJar - .loadForRequest(Uri.parse(HttpString.tUrl)); - if (cookie2.isEmpty) { - try { - await Request().get(HttpString.tUrl); - } catch (e) { - log("setCookie, ${e.toString()}"); - } - } - } + final userInfo = userInfoCache.get('userInfoCache'); setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); String baseUrlType = 'default'; if (setting.get(SettingBoxKey.enableGATMode, defaultValue: false)) { @@ -69,10 +56,10 @@ class Request { static Future getCsrf() async { List cookies = await cookieManager.cookieJar .loadForRequest(Uri.parse(HttpString.baseUrl)); - String token = ''; - if (cookies.where((e) => e.name == 'bili_jct').isNotEmpty) { - token = cookies.firstWhere((e) => e.name == 'bili_jct').value; - } + String token = cookies + .firstWhere((e) => e.name == 'bili_jct', + orElse: () => Cookie('bili_jct', '')) + .value; return token; } diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index f26cec06..c8fdcbad 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -3,8 +3,8 @@ import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/follow/result.dart'; import 'package:pilipala/pages/follow/index.dart'; -import 'package:pilipala/pages/video/detail/introduction/widgets/group_panel.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/follow.dart'; import 'package:pilipala/utils/utils.dart'; class FollowItem extends StatelessWidget { @@ -47,28 +47,11 @@ class FollowItem extends StatelessWidget { height: 34, child: TextButton( onPressed: () async { - await showModalBottomSheet( + int followStatus = await FollowUtils( context: context, - useSafeArea: true, - isScrollControlled: true, - builder: (BuildContext context) { - return DraggableScrollableSheet( - initialChildSize: 0.6, - minChildSize: 0, - maxChildSize: 1, - snap: true, - expand: false, - snapSizes: const [0.6], - builder: (BuildContext context, - ScrollController scrollController) { - return GroupPanel( - mid: item.mid!, - scrollController: scrollController, - ); - }, - ); - }, - ); + followStatus: 2, + mid: item.mid!, + ).showFollowSheet(); }, style: TextButton.styleFrom( padding: const EdgeInsets.fromLTRB(15, 0, 15, 0), diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 9e7fb339..b43eda64 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,14 +1,19 @@ import 'dart:async'; import 'dart:io'; +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:dio_cookie_manager/dio_cookie_manager.dart'; 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:pilipala/http/constants.dart'; +import 'package:pilipala/http/index.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/utils/login.dart'; +import 'package:pilipala/utils/utils.dart'; class LoginPageController extends GetxController { final GlobalKey mobFormKey = GlobalKey(); @@ -341,4 +346,32 @@ class LoginPageController extends GetxController { Get.back(); } } + + // cookie登录 + Future loginInByCookie({ + required String cookiesStr, + String domain = HttpString.baseUrl, + }) async { + final List cookiesStrList = cookiesStr.split('; '); + final List cookiesList = cookiesStrList.map((cookie) { + final cookieArr = cookie.split('='); + return Cookie(cookieArr[0], cookieArr[1]); + }).toList(); + + final String cookiePath = await Utils.getCookiePath(); + final cookieJar = PersistCookieJar( + ignoreExpires: true, + storage: FileStorage(cookiePath), + ); + CookieManager cookieManager = CookieManager(cookieJar); + Request.cookieManager = cookieManager; + await Request.cookieManager.cookieJar + .saveFromResponse(Uri.parse(HttpString.baseUrl), cookiesList); + try { + Request.dio.options.headers['cookie'] = cookiesStr; + } catch (err) { + debugPrint(err.toString()); + } + LoginUtils.confirmLogin('', null); + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 4fe21792..8ba012ea 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -14,6 +14,144 @@ class LoginPage extends StatefulWidget { class _LoginPageState extends State { final LoginPageController _loginPageCtr = Get.put(LoginPageController()); + // 浏览器登录 + void loginInByWeb() { + Get.offNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + } + + // 二维码方式登录 + void loginInByWebQrcode() { + 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.white, + ); + } 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, + ), + ), + ) + ], + ); + }); + }, + ).then((value) { + _loginPageCtr.validTimer!.cancel(); + }); + } + + // cookie登录 + // cookie登录 + void loginInByCookie() async { + var cookies = ''; + final outline = Theme.of(context).colorScheme.outline; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Cookie登录'), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('请将主站cookie粘贴到下方输入框中,点击「确认」即可完成登录。(记得清空粘贴板~)'), + const SizedBox(height: 12), + TextField( + minLines: 1, + maxLines: 3, + decoration: InputDecoration( + labelText: 'cookie', + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(6.0), + ), + ), + onChanged: (e) => cookies = e, + ), + ], + ), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text('取消', style: TextStyle(color: outline))), + TextButton( + onPressed: () async { + if (cookies.isEmpty) { + return; + } + await _loginPageCtr.loginInByCookie(cookiesStr: cookies); + if (context.mounted) { + Navigator.of(context).pop(); + } + }, + child: const Text('确认')) + ], + ); + }, + ); + } + @override void dispose() { _loginPageCtr.validTimer?.cancel(); @@ -43,100 +181,17 @@ class _LoginPageState extends State { actions: [ IconButton( tooltip: '浏览器打开', - onPressed: () { - Get.offNamed( - '/webview', - parameters: { - 'url': 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); - }, + onPressed: loginInByWeb, icon: const Icon(Icons.language, size: 20), ), + IconButton( + tooltip: 'cookie登录', + onPressed: loginInByCookie, + icon: const Icon(Icons.cookie_outlined, size: 20), + ), 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.white, - ); - } 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, - ), - ), - ) - ], - ); - }); - }, - ).then((value) { - _loginPageCtr.validTimer!.cancel(); - }); - }, + onPressed: loginInByWebQrcode, icon: const Icon(Icons.qr_code, size: 20), ), const SizedBox(width: 22), diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 3c63d560..bb5be0f1 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -1,15 +1,14 @@ -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/member.dart'; import 'package:pilipala/http/user.dart'; -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/models/user/info.dart'; +import 'package:pilipala/utils/follow.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; @@ -29,6 +28,12 @@ class MemberController extends GetxController { RxList recentCoinsList = [].obs; RxList recentLikeList = [].obs; RxBool isOwner = false.obs; + final Map attributeTextMap = { + 1: '悄悄关注', + 2: '已关注', + 6: '已互粉', + 128: '已拉黑', + }; @override void onInit() { @@ -85,11 +90,12 @@ class MemberController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - if (attribute.value == 128) { - modifyRelation('block'); - } else { - modifyRelation('follow'); - } + attribute.value = await FollowUtils( + context: Get.context!, + followStatus: attribute.value, + mid: mid, + ).showFollowSheet(); + attributeText.value = attributeTextMap[attribute.value] ?? '关注'; } // 关系查询 @@ -99,16 +105,10 @@ class MemberController extends GetxController { var res = await UserHttp.hasFollow(mid); if (res['status']) { attribute.value = res['data']['attribute']; - final Map attributeTextMap = { - 1: '悄悄关注', - 2: '已关注', - 6: '已互关', - 128: '已拉黑', - }; - attributeText.value = attributeTextMap[attribute.value] ?? '关注'; if (res['data']['special'] == 1) { attributeText.value = '特别关注'; } + attributeText.value = attributeTextMap[attribute.value] ?? '关注'; } } @@ -118,66 +118,15 @@ class MemberController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - modifyRelation('block'); - } - -// 合并关注/取关和拉黑逻辑 - Future modifyRelation(String actionType) async { - 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( + final String actionType = attribute.value == 128 ? 'remove' : 'block'; + attribute.value = await FollowUtils( context: Get.context!, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text(contentText), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: Text( - '点错了', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: mid, - act: act, - reSrc: 11, - ); - SmartDialog.dismiss(); - if (res['status']) { - 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('确定'), - ) - ], - ); - }, - ); + followStatus: attribute.value, + mid: mid, + ).modifyRelationFetch(actionType, isDirect: true); + if (attribute.value != -1) { + attributeText.value = attributeTextMap[attribute.value] ?? '关注'; + } } void shareUser() { diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 5c84b3bc..6fe4af5b 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -17,6 +17,7 @@ import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/follow.dart'; import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -26,7 +27,6 @@ import '../../../../common/pages_bottom_sheet.dart'; import '../../../../models/common/video_episode_type.dart'; import '../../../../utils/drawer.dart'; import '../related/index.dart'; -import 'widgets/group_panel.dart'; class VideoIntroController extends GetxController { VideoIntroController({required this.bvid}); @@ -50,7 +50,7 @@ class VideoIntroController extends GetxController { List addMediaIdsNew = []; List delMediaIdsNew = []; // 关注状态 默认未关注 - RxMap followStatus = {}.obs; + RxInt followStatus = (-1).obs; RxInt lastPlayCid = 0.obs; UserInfoData? userInfo; RxList videoTags = [].obs; @@ -337,113 +337,23 @@ class VideoIntroController extends GetxController { } var result = await VideoHttp.hasFollow(mid: videoDetail.value.owner!.mid!); if (result['status']) { - followStatus.value = result['data']; + followStatus.value = result['data']['attribute']; } return result; } // 关注/取关up - Future actionRelationMod() async { + Future actionRelationMod(BuildContext context) async { feedBack(); if (userInfo == null) { SmartDialog.showToast('账号未登录'); return; } - final int currentStatus = followStatus['attribute']; - if (currentStatus == 128) { - modifyRelation('block', currentStatus); - } else { - modifyRelation('follow', currentStatus); - } - } - - // 操作用户关系 - Future modifyRelation(String actionType, int currentStatus) async { - final int mid = videoDetail.value.owner!.mid!; - String contentText; - int act; - if (actionType == 'follow') { - contentText = currentStatus != 0 ? '确定取消关注UP主?' : '确定关注UP主?'; - act = currentStatus != 0 ? 2 : 1; - } else if (actionType == 'block') { - contentText = '确定从黑名单移除UP主?'; - act = 6; - } else { - return; - } - - showDialog( - context: Get.context!, - builder: (BuildContext context) { - final Color outline = Theme.of(Get.context!).colorScheme.outline; - return AlertDialog( - title: const Text('提示'), - content: Text(contentText), - actions: [ - TextButton( - onPressed: Navigator.of(context).pop, - child: Text('点错了', style: TextStyle(color: outline)), - ), - TextButton( - onPressed: () => modifyRelationFetch( - context, - mid, - act, - currentStatus, - actionType, - ), - child: const Text('确定'), - ) - ], - ); - }, - ); - } - - // 操作用户关系Future - Future modifyRelationFetch( - BuildContext context, - mid, - act, - currentStatus, - actionType, - ) async { - var res = await VideoHttp.relationMod(mid: mid, act: act, reSrc: 11); - if (context.mounted) { - Navigator.of(context).pop(); - } - if (res['status']) { - if (actionType == 'follow') { - final Map statusMap = { - 0: 2, - 2: 0, - }; - late int actionStatus; - actionStatus = statusMap[currentStatus] ?? 0; - followStatus['attribute'] = actionStatus; - if (currentStatus == 0 && Get.context!.mounted) { - ScaffoldMessenger.of(Get.context!).showSnackBar( - SnackBar( - content: const Text('关注成功'), - duration: const Duration(seconds: 2), - action: SnackBarAction( - label: '设置分组', - onPressed: setFollowGroup, - ), - showCloseIcon: true, - ), - ); - } else { - SmartDialog.showToast('取消关注成功'); - } - } else if (actionType == 'block') { - followStatus['attribute'] = 0; - SmartDialog.showToast('取消拉黑成功'); - } - followStatus.refresh(); - } else { - SmartDialog.showToast(res['msg']); - } + followStatus.value = await FollowUtils( + context: context, + followStatus: followStatus.value, + mid: videoDetail.value.owner!.mid!, + ).showFollowSheet(); } // 修改分P或番剧分集 @@ -568,33 +478,33 @@ class VideoIntroController extends GetxController { } // 设置关注分组 - void setFollowGroup() async { - final mediaQueryData = MediaQuery.of(Get.context!); - final contentHeight = mediaQueryData.size.height - kToolbarHeight; - final double initialChildSize = - (contentHeight - Get.width * 9 / 16) / contentHeight; - await showModalBottomSheet( - context: Get.context!, - useSafeArea: true, - isScrollControlled: true, - builder: (BuildContext context) { - return DraggableScrollableSheet( - initialChildSize: initialChildSize, - minChildSize: 0, - maxChildSize: 1, - snap: true, - expand: false, - snapSizes: [initialChildSize], - builder: (BuildContext context, ScrollController scrollController) { - return GroupPanel( - mid: videoDetail.value.owner!.mid!, - scrollController: scrollController, - ); - }, - ); - }, - ); - } + // void setFollowGroup() async { + // final mediaQueryData = MediaQuery.of(Get.context!); + // final contentHeight = mediaQueryData.size.height - kToolbarHeight; + // final double initialChildSize = + // (contentHeight - Get.width * 9 / 16) / contentHeight; + // await showModalBottomSheet( + // context: Get.context!, + // useSafeArea: true, + // isScrollControlled: true, + // builder: (BuildContext context) { + // return DraggableScrollableSheet( + // initialChildSize: initialChildSize, + // minChildSize: 0, + // maxChildSize: 1, + // snap: true, + // expand: false, + // snapSizes: [initialChildSize], + // builder: (BuildContext context, ScrollController scrollController) { + // return GroupPanel( + // mid: videoDetail.value.owner!.mid!, + // scrollController: scrollController, + // ); + // }, + // ); + // }, + // ); + // } // ai总结 Future aiConclusion() async { diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index e35b1ee3..aac1d657 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -18,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/follow.dart'; import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; @@ -458,14 +459,14 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Obx( () { final int attr = - videoIntroController.followStatus['attribute'] ?? 0; - return videoIntroController.followStatus.isEmpty + videoIntroController.followStatus.value; + return attr == -1 ? const SizedBox() : SizedBox( height: 32, child: TextButton( - onPressed: - videoIntroController.actionRelationMod, + onPressed: () => videoIntroController + .actionRelationMod(context), style: TextButton.styleFrom( padding: const EdgeInsets.only( left: 8, diff --git a/lib/utils/follow.dart b/lib/utils/follow.dart new file mode 100644 index 00000000..c081705a --- /dev/null +++ b/lib/utils/follow.dart @@ -0,0 +1,207 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:pilipala/common/widgets/drag_handle.dart'; +import 'package:pilipala/common/widgets/group_panel.dart'; +import 'package:pilipala/http/video.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; + +class FollowUtils { + final BuildContext context; + final int followStatus; + final int mid; + + FollowUtils({ + required this.context, + required this.followStatus, + required this.mid, + }); + + // static final Map> followMap = { + // // 未关注 + // 0: { + // 'desc': '确定关注UP主?', + // // 1 关注 5 拉黑 + // 'act': 1, + // }, + // // 已关注 + // 2: { + // 'desc': '确定取消关注UP主?', + // 'act': 2, + // }, + // // 已互粉 + // 6: { + // 'desc': '确定取消关注UP主?', + // 'act': 2, + // }, + // // 已拉黑 + // 128: { + // 'desc': '确定从黑名单移除UP主?', + // 'act': 6, + // }, + // }; + + static final Map> actionTypeMap = { + 'remove': { + 'desc': '确定从黑名单移除UP主?', + 'tips': '已从黑名单移除', + 'act': 6, + 'followStatus': 0, + }, + 'unFollow': { + 'desc': '确定取消关注UP主?', + 'tips': '已取消关注', + 'act': 2, + 'followStatus': 0, + }, + 'follow': { + 'desc': '确定关注UP主?', + 'tips': '关注成功', + 'act': 1, + 'followStatus': 2, + }, + 'block': { + 'desc': '确定拉黑UP主?', + 'tips': '已拉黑', + 'act': 5, + 'followStatus': 128, + }, + }; + + Future showFollowSheet() async { + var res = await showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (context) { + return Padding( + padding: + EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const DragHandle(), + if (followStatus == 128) ...[ + ListTile( + leading: const Icon(Icons.remove_circle_outline_rounded), + onTap: () => modifyRelation('remove'), + title: const Text('从黑名单移除'), + ), + ], + if ([2, 6].contains(followStatus)) ...[ + ListTile( + leading: const Icon(Icons.group_add_outlined), + onTap: () { + Navigator.of(context).pop(); + setFollowGroup(); + }, + title: const Text('设置分组'), + ), + ListTile( + leading: const Icon(Icons.heart_broken_outlined), + onTap: () => modifyRelation('unFollow'), + title: const Text('取消关注'), + ), + ], + if (followStatus == 0) ...[ + ListTile( + leading: const Icon(Icons.favorite_border_rounded), + onTap: () => modifyRelation('follow'), + title: const Text('关注up主'), + ), + ListTile( + leading: const Icon(Icons.block_rounded), + onTap: () => modifyRelation('block'), + title: const Text('拉黑up主'), + ), + ], + ], + ), + ); + }, + ); + return res ?? followStatus; + } + + // 操作用户关系 + Future modifyRelation(String actionType) async { + showDialog( + context: context, + builder: (BuildContext context) { + final Color outline = Theme.of(context).colorScheme.outline; + return AlertDialog( + title: const Text('提示'), + content: Text(actionTypeMap[actionType]!['desc']), + actions: [ + TextButton( + onPressed: Navigator.of(context).pop, + child: Text('点错了', style: TextStyle(color: outline)), + ), + TextButton( + onPressed: () => modifyRelationFetch(actionType), + child: const Text('确定'), + ) + ], + ); + }, + ); + } + + // 操作用户关系Future + Future modifyRelationFetch(String actionType, {bool? isDirect}) async { + if (isDirect != true) { + Navigator.of(context).pop(); + } + + SmartDialog.showLoading(msg: '请求中'); + var res = await VideoHttp.relationMod( + mid: mid, + act: actionTypeMap[actionType]!['act'], + reSrc: 11, + ); + SmartDialog.dismiss(); + if (res['status']) { + final int newFollowStatus = actionTypeMap[actionType]!['followStatus']; + SmartDialog.showToast(actionTypeMap[actionType]!['tips']); + if (context.mounted) { + if (isDirect != true) { + Navigator.of(context).pop(newFollowStatus); + } + } + return newFollowStatus; + } else { + SmartDialog.showToast(res['msg']); + if (context.mounted && isDirect != true) { + Navigator.of(context).pop(-1); + } + return -1; + } + } + + // 设置分组 + Future setFollowGroup() async { + final size = MediaQuery.sizeOf(context); + final contentHeight = size.height - kToolbarHeight; + final double initialChildSize = + (contentHeight - size.width * 9 / 16) / contentHeight; + await showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + builder: (BuildContext context) { + return DraggableScrollableSheet( + initialChildSize: initialChildSize, + minChildSize: 0, + maxChildSize: 1, + snap: true, + expand: false, + snapSizes: [initialChildSize], + builder: (BuildContext context, ScrollController scrollController) { + return GroupPanel( + mid: GlobalDataCache.userInfo!.mid!, + scrollController: scrollController, + ); + }, + ); + }, + ); + } +} diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 159de81d..e9fde1f2 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -12,7 +12,6 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/mine/index.dart'; -import 'package:pilipala/utils/cookie.dart'; import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:uuid/uuid.dart'; @@ -68,7 +67,6 @@ class LoginUtils { content = '${content + url}; \n'; } try { - await SetCookie.onSet(); final result = await UserHttp.userInfo(); if (result['status'] && result['data'].isLogin) { SmartDialog.showToast('登录成功');