diff --git a/lib/http/api.dart b/lib/http/api.dart index 13895d3a..6177beb4 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -390,6 +390,10 @@ class Api { // 0 添加至默认分组 否则使用,分割tagid static const String addUsers = '/x/relation/tags/addUsers'; + static const String addSpecial = '/x/relation/tag/special/add'; + + static const String delSpecial = '/x/relation/tag/special/del'; + // 获取指定分组下的up static const String followUpGroup = '/x/relation/tag'; diff --git a/lib/http/member.dart b/lib/http/member.dart index 04a441ca..663070d6 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -210,15 +210,44 @@ class MemberHttp { } } + static Future specialAction({ + int? fid, + bool isAdd = true, + }) async { + var res = await Request().post( + isAdd ? Api.addSpecial : Api.delSpecial, + data: { + 'fid': fid, + 'csrf': await Request.getCsrf(), + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } + // 设置分组 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(), + // 'cross_domain': true + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); if (res.data['code'] == 0) { return {'status': true, 'data': [], 'msg': '操作成功'}; } else { diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 488de8d7..0f588791 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -36,8 +36,10 @@ class MemberController extends GetxController { mid = mid ?? int.parse(Get.parameters['mid']!); userInfo = userInfoCache.get('userInfoCache'); ownerMid = userInfo != null ? userInfo.mid : -1; - face.value = Get.arguments['face'] ?? ''; - heroTag = Get.arguments['heroTag'] ?? ''; + try { + face.value = Get.arguments['face'] ?? ''; + heroTag = Get.arguments['heroTag'] ?? ''; + } catch (_) {} relationSearch(); } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 8b62d0b9..7ba79a3b 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 'dart:math'; import 'package:PiliPalaX/http/loading_state.dart'; +import 'package:PiliPalaX/http/member.dart'; import 'package:PiliPalaX/utils/utils.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -30,7 +31,8 @@ import '../../../../models/model_hot_video_item.dart'; import '../related/index.dart'; import 'widgets/group_panel.dart'; -class VideoIntroController extends GetxController { +class VideoIntroController extends GetxController + with GetTickerProviderStateMixin { // 视频bvid late String bvid; @@ -507,15 +509,140 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } + if (videoDetail.value.owner?.mid == null) { + return; + } feedBack(); int mid = videoDetail.value.owner!.mid!; - MemberController _ = Get.put(MemberController(mid: mid), - tag: mid.toString()); - await _.getInfo(); - if (context.mounted) await _.actionRelationMod(context); - followStatus['attribute'] = _.attribute.value; - followStatus.refresh(); - Get.delete(tag: mid.toString()); + if ((followStatus['attribute'] ?? 0) == 0) { + var res = await VideoHttp.relationMod( + mid: mid, + act: 1, + reSrc: 11, + ); + SmartDialog.showToast(res['status'] ? "关注成功" : res['msg']); + if (res['status']) { + followStatus['attribute'] = 2; + followStatus.refresh(); + } + } else { + /// TODO + showDialog( + context: context, + builder: (_) { + bool isSpecialFollowed = followStatus['special'] == 1; + String text = isSpecialFollowed ? '移除特别关注' : '加入特别关注'; + return AlertDialog( + clipBehavior: Clip.hardEdge, + contentPadding: const EdgeInsets.symmetric(vertical: 12), + content: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + dense: true, + onTap: () async { + Get.back(); + final res = await MemberHttp.specialAction( + fid: mid, + isAdd: !isSpecialFollowed, + ); + if (res['status']) { + followStatus['special'] = isSpecialFollowed ? 0 : 1; + List tags = followStatus['tag'] ?? []; + if (isSpecialFollowed) { + tags.remove(-10); + } else { + tags.add(-10); + } + followStatus['tag'] = tags; + followStatus.refresh(); + SmartDialog.showToast('$text成功'); + } else { + SmartDialog.showToast(res['msg']); + } + }, + title: Text( + text, + style: TextStyle(fontSize: 14), + ), + ), + ListTile( + dense: true, + onTap: () async { + Get.back(); + dynamic result = await showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + transitionAnimationController: AnimationController( + duration: const Duration(milliseconds: 200), + vsync: this, + ), + sheetAnimationStyle: AnimationStyle(curve: Curves.ease), + builder: (BuildContext context) { + return DraggableScrollableSheet( + minChildSize: 0, + maxChildSize: 1, + initialChildSize: 0.6, + snap: true, + expand: false, + snapSizes: const [0.6], + builder: (BuildContext context, + ScrollController scrollController) { + return GroupPanel( + mid: mid, + tags: followStatus['tag'], + scrollController: scrollController, + ); + }, + ); + }, + ); + await Future.delayed(const Duration(milliseconds: 500)); + if (result == true) { + queryFollowStatus(); + } + }, + title: const Text( + '设置分组', + style: TextStyle(fontSize: 14), + ), + ), + ListTile( + dense: true, + onTap: () async { + Get.back(); + var res = await VideoHttp.relationMod( + mid: mid, + act: 2, + reSrc: 11, + ); + SmartDialog.showToast( + res['status'] ? "取消关注成功" : res['msg']); + if (res['status']) { + followStatus['attribute'] = 0; + followStatus.refresh(); + } + }, + title: const Text( + '取消关注', + style: TextStyle(fontSize: 14), + ), + ), + ], + ), + ); + }, + ); + } + + // MemberController _ = Get.put(MemberController(mid: mid), + // tag: mid.toString()); + // await _.getInfo(); + // if (context.mounted) await _.actionRelationMod(context); + // followStatus['attribute'] = _.attribute.value; + // followStatus.refresh(); + // Get.delete(tag: mid.toString()); } // 修改分P或番剧分集 diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index c19846c0..faf71bee 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -10,7 +10,14 @@ import '../../../../../utils/utils.dart'; class GroupPanel extends StatefulWidget { final int? mid; - const GroupPanel({super.key, this.mid}); + final List? tags; + final ScrollController? scrollController; + const GroupPanel({ + super.key, + this.mid, + this.tags, + this.scrollController, + }); @override State createState() => _GroupPanelState(); @@ -19,12 +26,25 @@ class GroupPanel extends StatefulWidget { class _GroupPanelState extends State { late Future _futureBuilderFuture; late List tagsList; - bool showDefault = true; + bool showDefaultBtn = true; @override void initState() { super.initState(); _futureBuilderFuture = MemberHttp.followUpTags(); + () async { + dynamic result = await _futureBuilderFuture; + if (result['status']) { + tagsList = result['data']; + tagsList.removeWhere((item) => item.tagid == 0); + tagsList = tagsList.map((item) { + return item..checked = widget.tags?.contains(item.tagid) == true; + }).toList(); + setState(() { + showDefaultBtn = !tagsList.any((e) => e.checked == true); + }); + } + }(); } void onSave() async { @@ -46,111 +66,133 @@ class _GroupPanelState extends State { final res = await MemberHttp.addUsers(widget.mid, tagids); SmartDialog.showToast(res['msg']); if (res['status']) { - Get.back(); + Get.back(result: true); } } @override Widget build(BuildContext context) { - return Container( - height: Utils.getSheetHeight(context), - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - AppBar( - centerTitle: false, - elevation: 0, - leading: IconButton( - tooltip: '关闭', - 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( - 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(() {}); - }, + return NotificationListener( + onNotification: (notification) { + if (notification.extent <= 1e-5) { + Get.back(); + } + return false; + }, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(18), + color: Theme.of(context).colorScheme.surface, + ), + child: Column( + children: [ + AppBar( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.vertical( + top: Radius.circular(18), + ), + ), + centerTitle: false, + elevation: 0, + leading: IconButton( + tooltip: '关闭', + 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']) { + return ListView.builder( + controller: widget.scrollController, + itemCount: tagsList.length, + itemBuilder: (context, index) { + return ListTile( + onTap: () { + tagsList[index].checked = + !tagsList[index].checked!; + showDefaultBtn = + !tagsList.any((e) => e.checked == true); + setState(() {}); + }, + dense: true, + leading: const Icon(Icons.group_outlined), + minLeadingWidth: 0, + title: Text(tagsList[index].name ?? ''), + subtitle: tagsList[index].tip != '' + ? Text(tagsList[index].tip ?? '') + : null, + trailing: Transform.scale( + scale: 0.9, + child: Checkbox( + value: tagsList[index].checked, + onChanged: (bool? checkValue) { + tagsList[index].checked = checkValue; + showDefaultBtn = + !tagsList.any((e) => e.checked == true); + setState(() {}); + }, + ), ), + ); + }, + ); + } else { + return CustomScrollView( + controller: widget.scrollController, + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), ), - ); - }, - ); + ], + ); + } } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + // 骨架屏 + return const Center( + child: CircularProgressIndicator(), ); } - } 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, + Divider( + height: 1, + color: Theme.of(context).disabledColor.withOpacity(0.08), ), - 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, // 设置按钮背景色 + 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(showDefaultBtn ? '保存至默认分组' : '保存'), ), - child: Text(showDefault ? '保存至默认分组' : '保存'), - ), - ], + ], + ), ), - ), - ], + ], + ), ), ); }