diff --git a/lib/common/widgets/video_popup_menu.dart b/lib/common/widgets/video_popup_menu.dart index 165037467..a2c3c7c2d 100644 --- a/lib/common/widgets/video_popup_menu.dart +++ b/lib/common/widgets/video_popup_menu.dart @@ -1,7 +1,6 @@ import 'package:PiliPlus/common/widgets/custom_icon.dart'; import 'package:PiliPlus/http/user.dart'; import 'package:PiliPlus/http/video.dart'; -import 'package:PiliPlus/models/common/account_type.dart'; import 'package:PiliPlus/models/home/rcmd/result.dart'; import 'package:PiliPlus/models/model_video.dart'; import 'package:PiliPlus/models_new/space/space_archive/item.dart'; @@ -56,11 +55,12 @@ class VideoPopupMenu extends StatelessWidget { const Icon(CustomIcons.identifier_circle, size: 16), () => Utils.copyText(videoItem.bvid!), ), - _VideoCustomAction( - '稍后再看', - const Icon(MdiIcons.clockTimeEightOutline, size: 16), - () => UserHttp.toViewLater(bvid: videoItem.bvid), - ), + if (Accounts.main.isLogin) + _VideoCustomAction( + '稍后再看', + const Icon(MdiIcons.clockTimeEightOutline, size: 16), + () => UserHttp.toViewLater(bvid: videoItem.bvid), + ), if (videoItem.cid != null && Pref.enableAi) _VideoCustomAction( 'AI总结', @@ -100,11 +100,11 @@ class VideoPopupMenu extends StatelessWidget { '不感兴趣', const Icon(MdiIcons.thumbDownOutline, size: 16), () { - String? accessKey = Accounts.get( - AccountType.recommend, - ).accessKey; - if (accessKey == null || accessKey == "") { - SmartDialog.showToast("请退出账号后重新登录"); + final rcmd = Accounts.get(.recommend); + if (rcmd.accessKey == null || rcmd.accessKey == "") { + SmartDialog.showToast( + rcmd.isLogin ? '请退出账号后重新登录' : '账号未登录', + ); return; } if (videoItem case final RcmdVideoItemAppModel item) { diff --git a/lib/http/api.dart b/lib/http/api.dart index 3f75a555c..b8114a782 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -1014,4 +1014,7 @@ abstract final class Api { static const String replyReport = '/x/v2/reply/report'; static const String dynReaction = '/x/polymer/web-dynamic/v1/detail/reaction'; + + static const String liveFeedback = + '${HttpString.liveBaseUrl}/xlive/app-interface/v2/index/feedback'; } diff --git a/lib/http/live.dart b/lib/http/live.dart index ffb07aa5d..ba3d2308b 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -765,4 +765,41 @@ abstract final class LiveHttp { return Error(res.data['message']); } } + + static Future> liveFeedback( + Object roomId, + Object id, + String type, { + int page = 1, + }) async { + final params = { + 'access_key': ?recommend.accessKey, + 'actionKey': 'appkey', + 'build': 8430300, + 'channel': 'master', + 'c_locale': 'zh_CN', + 'device': 'android', + 'disable_rcmd': 0, + 'mobi_app': 'android', + 'platform': 'android', + 's_locale': 'zh_CN', + 'statistics': Constants.statisticsApp, + 'version': '8.43.0', + 'id': id, + 'id_type': type, + 'room_id': roomId, + 'type': 'dislike', + 'page': page, + }; + AppSign.appSign(params); + final res = await Request().get( + Api.liveFeedback, + queryParameters: params, + ); + if (res.data['code'] == 0) { + return const Success(null); + } else { + return Error(res.data['message']); + } + } } diff --git a/lib/models_new/live/live_feed_index/card_data_list_item.dart b/lib/models_new/live/live_feed_index/card_data_list_item.dart index 41c68fc04..4effde327 100644 --- a/lib/models_new/live/live_feed_index/card_data_list_item.dart +++ b/lib/models_new/live/live_feed_index/card_data_list_item.dart @@ -1,3 +1,4 @@ +import 'package:PiliPlus/models_new/live/live_feed_index/feedback.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/watched_show.dart'; import 'package:PiliPlus/utils/parse_string.dart'; @@ -14,6 +15,7 @@ class CardLiveItem { int? areaV2Id; int? areaV2ParentId; WatchedShow? watchedShow; + List? feedback; CardLiveItem({ this.roomid, @@ -27,6 +29,7 @@ class CardLiveItem { this.areaV2Id, this.areaV2ParentId, this.watchedShow, + this.feedback, }) : _systemCover = nonNullOrEmptyString(systemCover); factory CardLiveItem.fromJson(Map json) => CardLiveItem( @@ -43,5 +46,8 @@ class CardLiveItem { watchedShow: json['watched_show'] == null ? null : WatchedShow.fromJson(json['watched_show'] as Map), + feedback: (json['feedback'] as List?) + ?.map((x) => Feedback.fromJson(x)) + .toList(), ); } diff --git a/lib/models_new/live/live_feed_index/feedback.dart b/lib/models_new/live/live_feed_index/feedback.dart new file mode 100644 index 000000000..745ef7e5e --- /dev/null +++ b/lib/models_new/live/live_feed_index/feedback.dart @@ -0,0 +1,47 @@ +class Feedback { + Feedback({ + this.title, + this.subtitle, + this.type, + this.reasons, + }); + + final String? title; + final String? subtitle; + final String? type; + final List? reasons; + + factory Feedback.fromJson(Map json) { + return Feedback( + title: json['title'], + subtitle: json['subtitle'], + type: json['type'], + reasons: (json['reasons'] as List?) + ?.map((x) => Reason.fromJson(x)) + .toList(), + ); + } +} + +class Reason { + Reason({ + this.id, + this.name, + this.idType, + this.reasonId, + }); + + final int? id; + final String? name; + final String? idType; + final int? reasonId; + + factory Reason.fromJson(Map json) { + return Reason( + id: json['id'], + name: json['name'], + idType: json['id_type'], + reasonId: json['reason_id'], + ); + } +} diff --git a/lib/pages/live/widgets/live_item_app.dart b/lib/pages/live/widgets/live_item_app.dart index b362900ca..c63514b0f 100644 --- a/lib/pages/live/widgets/live_item_app.dart +++ b/lib/pages/live/widgets/live_item_app.dart @@ -1,10 +1,16 @@ import 'package:PiliPlus/common/style.dart'; import 'package:PiliPlus/common/widgets/image/image_save.dart'; import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; +import 'package:PiliPlus/http/live.dart'; import 'package:PiliPlus/models_new/live/live_feed_index/card_data_list_item.dart'; +import 'package:PiliPlus/models_new/live/live_feed_index/feedback.dart'; +import 'package:PiliPlus/pages/search/widgets/search_text.dart'; +import 'package:PiliPlus/utils/extension/iterable_ext.dart'; import 'package:PiliPlus/utils/page_utils.dart'; import 'package:PiliPlus/utils/platform_utils.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; // 视频卡片 - 垂直布局 class LiveCardVApp extends StatelessWidget { @@ -19,57 +25,139 @@ class LiveCardVApp extends StatelessWidget { @override Widget build(BuildContext context) { + final theme = Theme.of(context); void onLongPress() => imageSaveDialog( title: item.title, cover: showFirstFrame ? item.systemCover : item.cover, ); - return Card( - clipBehavior: Clip.hardEdge, - child: InkWell( - onTap: () => PageUtils.toLiveRoom(item.roomid), - onLongPress: onLongPress, - onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress, - child: Column( - children: [ - AspectRatio( - aspectRatio: Style.aspectRatio, - child: LayoutBuilder( - builder: (context, boxConstraints) { - double maxWidth = boxConstraints.maxWidth; - double maxHeight = boxConstraints.maxHeight; - return Stack( - clipBehavior: Clip.none, - children: [ - NetworkImgLayer( - src: showFirstFrame ? item.systemCover : item.cover, - width: maxWidth, - height: maxHeight, - type: .emote, - ), - Positioned( - left: 0, - right: 0, - bottom: 0, - child: AnimatedOpacity( - opacity: 1, - duration: const Duration(milliseconds: 200), - child: videoStat(context), + return Stack( + children: [ + Card( + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => PageUtils.toLiveRoom(item.roomid), + onLongPress: onLongPress, + onSecondaryTap: PlatformUtils.isMobile ? null : onLongPress, + child: Column( + children: [ + AspectRatio( + aspectRatio: Style.aspectRatio, + child: LayoutBuilder( + builder: (context, boxConstraints) => Stack( + clipBehavior: Clip.none, + children: [ + NetworkImgLayer( + src: showFirstFrame ? item.systemCover : item.cover, + width: boxConstraints.maxWidth, + height: boxConstraints.maxHeight, + type: .emote, ), - ), - ], - ); - }, + Positioned( + left: 0, + right: 0, + bottom: 0, + child: AnimatedOpacity( + opacity: 1, + duration: const Duration(milliseconds: 200), + child: videoStat(), + ), + ), + ], + ), + ), + ), + liveContent(theme), + ], + ), + ), + ), + if (!item.feedback.isNullOrEmpty) + Positioned( + right: -5, + bottom: -2, + width: 29, + height: 29, + child: IconButton( + padding: .zero, + onPressed: () { + Widget actionButton(Reason r) => SearchText( + text: r.name!, + onTap: (_) async { + Get.back(); + SmartDialog.showLoading(msg: '正在提交'); + final res = await LiveHttp.liveFeedback( + item.roomid!, + r.id!, + r.idType!, + ); + SmartDialog.dismiss(); + if (res.isSuccess) { + SmartDialog.showToast('提交成功'); + } else { + res.toast(); + } + }, + ); + + final feedback = item.feedback!; + showDialog( + context: context, + builder: (context) { + return SimpleDialog( + contentPadding: const .fromLTRB(24, 16, 24, 19), + children: [ + for (var i in feedback) ...[ + const SizedBox(height: 5), + Text.rich( + TextSpan( + children: [ + TextSpan( + text: i.title, + style: theme.textTheme.titleMedium, + ), + TextSpan( + text: '\n${i.subtitle}', + style: TextStyle( + color: theme.colorScheme.outline, + ), + ), + ], + ), + ), + const SizedBox(height: 5), + Wrap( + spacing: 8.0, + runSpacing: 8.0, + children: i.reasons!.map(actionButton).toList(), + ), + ], + const Divider(), + Center( + child: FilledButton.tonal( + onPressed: Get.back, + style: FilledButton.styleFrom( + visualDensity: VisualDensity.compact, + ), + child: const Text('取消'), + ), + ), + ], + ); + }, + ); + }, + icon: Icon( + Icons.more_vert_outlined, + size: 17, + color: theme.colorScheme.outline, ), ), - liveContent(context), - ], - ), - ), + ), + ], ); } - Widget liveContent(BuildContext context) { - final theme = Theme.of(context); + Widget liveContent(ThemeData theme) { return Expanded( flex: 1, child: Padding( @@ -79,29 +167,24 @@ class LiveCardVApp extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${item.title}', + item.title.toString(), textAlign: TextAlign.start, - style: const TextStyle( - letterSpacing: 0.3, - ), + style: const TextStyle(letterSpacing: 0.3), maxLines: 2, overflow: TextOverflow.ellipsis, ), - Row( - children: [ - Expanded( - child: Text( - '${item.uname}', - textAlign: TextAlign.start, - style: TextStyle( - fontSize: theme.textTheme.labelMedium!.fontSize, - color: theme.colorScheme.outline, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), + Align( + alignment: .topLeft, + child: Text( + item.uname.toString(), + textAlign: TextAlign.start, + style: TextStyle( + fontSize: theme.textTheme.labelMedium!.fontSize, + color: theme.colorScheme.outline, ), - ], + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), ], ), @@ -109,7 +192,7 @@ class LiveCardVApp extends StatelessWidget { ); } - Widget videoStat(BuildContext context) { + Widget videoStat() { return Container( height: 50, padding: const EdgeInsets.only(top: 26, left: 10, right: 10), @@ -117,10 +200,7 @@ class LiveCardVApp extends StatelessWidget { gradient: LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black54, - ], + colors: [Colors.transparent, Colors.black54], tileMode: TileMode.mirror, ), ), @@ -128,7 +208,7 @@ class LiveCardVApp extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - '${item.areaName}', + item.areaName.toString(), style: const TextStyle(fontSize: 11, color: Colors.white), ), if (item.watchedShow?.textLarge case final textLarge?) diff --git a/lib/utils/accounts/account_manager/account_mgr.dart b/lib/utils/accounts/account_manager/account_mgr.dart index 78621c43c..7fbc07bb3 100644 --- a/lib/utils/accounts/account_manager/account_mgr.dart +++ b/lib/utils/accounts/account_manager/account_mgr.dart @@ -177,7 +177,7 @@ class AccountManager extends Interceptor { 'site/getCoin', ]; String url = err.requestOptions.uri.toString(); - if (kDebugMode) debugPrint('🌹🌹ApiInterceptor: $url'); + if (kDebugMode) debugPrint('🌹🌹ApiInterceptor: $url\n$err'); if (skipShow.any((i) => url.contains(i)) || (url.contains('skipSegments') && err.requestOptions.method == 'GET')) { // skip diff --git a/lib/utils/accounts/api_type.dart b/lib/utils/accounts/api_type.dart index 9551f8b0e..b282e623b 100644 --- a/lib/utils/accounts/api_type.dart +++ b/lib/utils/accounts/api_type.dart @@ -88,6 +88,7 @@ abstract final class ApiType { Api.dynTopicRcmd, Api.topicFeed, Api.topicTop, + Api.liveFeedback, }, // progress AccountType.video: {