Compare commits

...

10 Commits

Author SHA1 Message Date
5bf7b69d79 feat: 收藏搜索结果删除 2024-02-16 09:33:59 +08:00
d57f84a1d7 fix: 路由跳转传参丢失 2024-02-15 21:59:28 +08:00
32b2f0ceff Merge pull request #539 from orz12/fix-speed-dialog-cannot-dismiss
fix: 播放速度dialog无法关闭
2024-02-15 21:10:52 +08:00
bae871cfa1 Merge branch 'feature-replyItem' 2024-02-15 21:07:55 +08:00
d95fe9fe14 mod: MorePanel样式 2024-02-15 21:07:23 +08:00
eb006e4c55 Merge branch 'feature-replyItem' 2024-02-14 20:09:48 +08:00
cb88d0c9ae Merge branch 'feature-liveRoomRender' 2024-02-14 20:09:40 +08:00
3efad736ae fix: 直播闪退 issues #540 2024-02-14 19:38:55 +08:00
42ad959155 fix: 速度设置无法取消 2024-02-14 08:44:00 +08:00
cdf800c49f mod: 评论复制逻辑 issues #420 #331 #297 #152 2024-02-13 23:33:51 +08:00
9 changed files with 245 additions and 192 deletions

View File

@ -24,11 +24,13 @@ class _FavDetailPageState extends State<FavDetailPage> {
Get.put(FavDetailController());
late StreamController<bool> titleStreamC; // a
Future? _futureBuilderFuture;
late String mediaId;
@override
void initState() {
super.initState();
_futureBuilderFuture = _favDetailController.queryUserFavFolderDetail();
mediaId = Get.parameters['mediaId']!;
titleStreamC = StreamController<bool>();
_controller.addListener(
() {
@ -94,8 +96,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
),
actions: [
IconButton(
onPressed: () => Get.toNamed(
'/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'),
onPressed: () =>
Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'),
icon: const Icon(Icons.search_outlined),
),
// IconButton(

View File

@ -15,9 +15,14 @@ import '../../../common/widgets/badge.dart';
class FavVideoCardH extends StatelessWidget {
final dynamic videoItem;
final Function? callFn;
final int? searchType;
const FavVideoCardH({Key? key, required this.videoItem, this.callFn})
: super(key: key);
const FavVideoCardH({
Key? key,
required this.videoItem,
this.callFn,
this.searchType,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -107,7 +112,11 @@ class FavVideoCardH extends StatelessWidget {
},
),
),
VideoContent(videoItem: videoItem, callFn: callFn)
VideoContent(
videoItem: videoItem,
callFn: callFn,
searchType: searchType,
)
],
),
);
@ -123,7 +132,13 @@ class FavVideoCardH extends StatelessWidget {
class VideoContent extends StatelessWidget {
final dynamic videoItem;
final Function? callFn;
const VideoContent({super.key, required this.videoItem, this.callFn});
final int? searchType;
const VideoContent({
super.key,
required this.videoItem,
this.callFn,
this.searchType,
});
@override
Widget build(BuildContext context) {
@ -189,48 +204,51 @@ class VideoContent extends StatelessWidget {
),
],
),
Positioned(
right: 0,
bottom: -4,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('要取消收藏吗?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'取消',
style: TextStyle(
color:
Theme.of(context).colorScheme.outline),
)),
TextButton(
onPressed: () async {
await callFn!();
Get.back();
},
child: const Text('确定取消'),
)
],
);
},
);
},
icon: Icon(
Icons.clear_outlined,
color: Theme.of(context).colorScheme.outline,
size: 18,
),
),
),
searchType != 1
? Positioned(
right: 0,
bottom: -4,
child: IconButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.zero),
),
onPressed: () {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('要取消收藏吗?'),
actions: [
TextButton(
onPressed: () => Get.back(),
child: Text(
'取消',
style: TextStyle(
color: Theme.of(context)
.colorScheme
.outline),
)),
TextButton(
onPressed: () async {
await callFn!();
Get.back();
},
child: const Text('确定取消'),
)
],
);
},
);
},
icon: Icon(
Icons.clear_outlined,
color: Theme.of(context).colorScheme.outline,
size: 18,
),
),
)
: const SizedBox(),
],
),
),

View File

@ -1,8 +1,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/user/fav_detail.dart';
import '../../http/video.dart';
class FavSearchController extends GetxController {
final ScrollController scrollController = ScrollController();
Rx<TextEditingController> controller = TextEditingController().obs;
@ -72,4 +75,21 @@ class FavSearchController extends GetxController {
if (!hasMore) return;
searchFav(type: 'onLoad');
}
onCancelFav(int id) async {
var result = await VideoHttp.favVideo(
aid: id, addIds: '', delIds: mediaId.toString());
if (result['status']) {
if (result['data']['prompt']) {
List dataList = favList;
for (var i in dataList) {
if (i.id == id) {
dataList.remove(i);
break;
}
}
SmartDialog.showToast('取消收藏');
}
}
}
}

View File

@ -8,9 +8,7 @@ import 'package:pilipala/pages/fav_detail/widget/fav_video_card.dart';
import 'controller.dart';
class FavSearchPage extends StatefulWidget {
final int? sourceType;
final int? mediaId;
const FavSearchPage({super.key, this.sourceType, this.mediaId});
const FavSearchPage({super.key});
@override
State<FavSearchPage> createState() => _FavSearchPageState();
@ -19,11 +17,12 @@ class FavSearchPage extends StatefulWidget {
class _FavSearchPageState extends State<FavSearchPage> {
final FavSearchController _favSearchCtr = Get.put(FavSearchController());
late ScrollController scrollController;
late int searchType;
@override
void initState() {
super.initState();
searchType = int.parse(Get.parameters['searchType']!);
scrollController = _favSearchCtr.scrollController;
scrollController.addListener(
() {
@ -100,7 +99,11 @@ class _FavSearchPageState extends State<FavSearchPage> {
} else {
return FavVideoCardH(
videoItem: _favSearchCtr.favList[index],
callFn: () => null,
searchType: searchType,
callFn: () => searchType != 1
? _favSearchCtr
.onCancelFav(_favSearchCtr.favList[index].id!)
: {},
);
}
},

View File

@ -3,7 +3,6 @@ import 'package:pilipala/http/constants.dart';
import 'package:pilipala/http/live.dart';
import 'package:pilipala/models/live/room_info.dart';
import 'package:pilipala/plugin/pl_player/index.dart';
import '../../models/live/room_info_h5.dart';
class LiveRoomController extends GetxController {
@ -16,13 +15,6 @@ class LiveRoomController extends GetxController {
RxBool volumeOff = false.obs;
PlPlayerController plPlayerController =
PlPlayerController.getInstance(videoType: 'live');
// MeeduPlayerController meeduPlayerController = MeeduPlayerController(
// colorTheme: Theme.of(Get.context!).colorScheme.primary,
// pipEnabled: true,
// controlsStyle: ControlsStyle.live,
// enabledButtons: const EnabledButtons(pip: true),
// );
Rx<RoomInfoH5Model> roomInfoH5 = RoomInfoH5Model().obs;
@override
@ -39,8 +31,6 @@ class LiveRoomController extends GetxController {
cover = liveItem.cover;
}
}
queryLiveInfo();
queryLiveInfoH5();
}
playerInit(source) async {

View File

@ -29,22 +29,18 @@ class _LiveRoomPageState extends State<LiveRoomPage> {
@override
void initState() {
super.initState();
plPlayerController = _liveRoomController.plPlayerController;
plPlayerController!.onPlayerStatusChanged.listen(
(PlayerStatus status) {
if (status == PlayerStatus.playing) {
isShowCover = false;
setState(() {});
}
},
);
if (Platform.isAndroid) {
floating = Floating();
}
_futureBuilder = _liveRoomController.queryLiveInfoH5();
videoSourceInit();
_futureBuilderFuture = _liveRoomController.queryLiveInfo();
}
Future<void> videoSourceInit() async {
_futureBuilder = _liveRoomController.queryLiveInfoH5();
plPlayerController = _liveRoomController.plPlayerController;
}
@override
void dispose() {
plPlayerController!.dispose();

View File

@ -105,7 +105,7 @@ class _MemberPageState extends State<MemberPage>
actions: [
IconButton(
onPressed: () => Get.toNamed(
'/memberSearch?mid=${Get.parameters['mid']}&uname=${_memberController.memberInfo.value.name!}'),
'/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'),
icon: const Icon(Icons.search_outlined),
),
PopupMenuButton(

View File

@ -1,5 +1,6 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
@ -46,6 +47,17 @@ class ReplyItem extends StatelessWidget {
replyReply!(replyItem);
}
},
onLongPress: () {
feedBack();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(item: replyItem);
},
);
},
child: Column(
children: [
Padding(
@ -121,98 +133,6 @@ class ReplyItem extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// 头像、昵称
// SizedBox(
// width: double.infinity,
// child: Stack(
// children: [
// GestureDetector(
// behavior: HitTestBehavior.opaque,
// onTap: () {
// feedBack();
// Get.toNamed('/member?mid=${replyItem!.mid}', arguments: {
// 'face': replyItem!.member!.avatar!,
// 'heroTag': heroTag
// });
// },
// child: Row(
// crossAxisAlignment: CrossAxisAlignment.center,
// mainAxisSize: MainAxisSize.min,
// children: <Widget>[
// lfAvtar(context, heroTag),
// const SizedBox(width: 12),
// Text(
// replyItem!.member!.uname!,
// style: TextStyle(
// color: replyItem!.member!.vip!['vipStatus'] > 0
// ? const Color.fromARGB(255, 251, 100, 163)
// : Theme.of(context).colorScheme.outline,
// fontSize: 13,
// ),
// ),
// const SizedBox(width: 6),
// Image.asset(
// 'assets/images/lv/lv${replyItem!.member!.level}.png',
// height: 11,
// ),
// const SizedBox(width: 6),
// if (replyItem!.isUp!)
// const PBadge(
// text: 'UP',
// size: 'small',
// stack: 'normal',
// fs: 9,
// ),
// ],
// ),
// ),
// Positioned(
// top: 0,
// left: 0,
// right: 0,
// child: Container(
// width: double.infinity,
// height: 45,
// decoration: BoxDecoration(
// image: replyItem!.member!.userSailing!.cardbg != null
// ? DecorationImage(
// alignment: Alignment.centerRight,
// fit: BoxFit.fitHeight,
// image: NetworkImage(
// replyItem!.member!.userSailing!.cardbg!['image'],
// ),
// )
// : null,
// ),
// ),
// ),
// if (replyItem!.member!.userSailing!.cardbg != null &&
// replyItem!.member!.userSailing!.cardbg!['fan']['number'] > 0)
// Positioned(
// top: 10,
// left: Get.size.width / 7 * 5.8,
// child: DefaultTextStyle(
// style: TextStyle(
// fontFamily: 'fansCard',
// fontSize: 9,
// color: Theme.of(context).colorScheme.primary,
// ),
// child: Column(
// crossAxisAlignment: CrossAxisAlignment.start,
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// const Text('NO.'),
// Text(
// replyItem!.member!.userSailing!.cardbg!['fan']
// ['num_desc'],
// ),
// ],
// ),
// ),
// ),
// ],
// ),
// ),
/// fix Stack内GestureDetector onTap无效
GestureDetector(
behavior: HitTestBehavior.opaque,
@ -289,30 +209,26 @@ class ReplyItem extends StatelessWidget {
// title
Container(
margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4),
child: SelectableRegion(
focusNode: FocusNode(),
selectionControls: MaterialTextSelectionControls(),
child: Text.rich(
style: const TextStyle(height: 1.75),
maxLines:
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
overflow: TextOverflow.ellipsis,
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
child: Text.rich(
style: const TextStyle(height: 1.75),
maxLines:
replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999,
overflow: TextOverflow.ellipsis,
TextSpan(
children: [
if (replyItem!.isTop!)
const WidgetSpan(
alignment: PlaceholderAlignment.top,
child: PBadge(
text: 'TOP',
size: 'small',
stack: 'normal',
type: 'line',
fs: 9,
),
buildContent(context, replyItem!, replyReply, null),
],
),
),
buildContent(context, replyItem!, replyReply, null),
],
),
),
),
@ -445,6 +361,17 @@ class ReplyItemRow extends StatelessWidget {
InkWell(
// 一楼点击评论展开评论详情
onTap: () => replyReply!(replyItem),
onLongPress: () {
feedBack();
showModalBottomSheet(
context: context,
useRootNavigator: true,
isScrollControlled: true,
builder: (context) {
return MorePanel(item: replies![i]);
},
);
},
child: Container(
width: double.infinity,
padding: EdgeInsets.fromLTRB(
@ -636,8 +563,8 @@ InlineSpan buildContent(
},
),
);
} else if (RegExp(r'^\b(?:\d+[:])?[0-5]?[0-9][:][0-5]?[0-9]\b$').hasMatch(matchStr)) {
} else if (RegExp(r'^\b(?:\d+[:])?[0-5]?[0-9][:][0-5]?[0-9]\b$')
.hasMatch(matchStr)) {
matchStr = matchStr.replaceAll('', ':');
spanChilds.add(
TextSpan(
@ -955,3 +882,100 @@ InlineSpan buildContent(
// spanChilds.add(TextSpan(text: matchMember));
return TextSpan(children: spanChilds);
}
class MorePanel extends StatelessWidget {
final dynamic item;
const MorePanel({super.key, required this.item});
Future<dynamic> menuActionHandler(String type) async {
String message = item.content.message ?? item.content;
switch (type) {
case 'copyAll':
await Clipboard.setData(ClipboardData(text: message));
SmartDialog.showToast('已复制');
Get.back();
break;
case 'copyFreedom':
Get.back();
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('自由复制'),
content: SelectableText(message),
);
},
);
break;
// case 'block':
// SmartDialog.showToast('加入黑名单');
// break;
// case 'report':
// SmartDialog.showToast('举报');
// break;
// case 'delete':
// SmartDialog.showToast('删除');
// break;
default:
}
}
@override
Widget build(BuildContext context) {
Color errorColor = Theme.of(context).colorScheme.error;
return Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
InkWell(
onTap: () => Get.back(),
child: Container(
height: 35,
padding: const EdgeInsets.only(bottom: 2),
child: Center(
child: Container(
width: 32,
height: 3,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.outline,
borderRadius: const BorderRadius.all(Radius.circular(3))),
),
),
),
),
ListTile(
onTap: () async => await menuActionHandler('copyAll'),
minLeadingWidth: 0,
leading: const Icon(Icons.copy_all_outlined, size: 19),
title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall),
),
ListTile(
onTap: () async => await menuActionHandler('copyFreedom'),
minLeadingWidth: 0,
leading: const Icon(Icons.copy_outlined, size: 19),
title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall),
),
// ListTile(
// onTap: () async => await menuActionHandler('block'),
// minLeadingWidth: 0,
// leading: Icon(Icons.block_outlined, color: errorColor),
// title: Text('加入黑名单', style: TextStyle(color: errorColor)),
// ),
// ListTile(
// onTap: () async => await menuActionHandler('report'),
// minLeadingWidth: 0,
// leading: Icon(Icons.report_outlined, color: errorColor),
// title: Text('举报', style: TextStyle(color: errorColor)),
// ),
// ListTile(
// onTap: () async => await menuActionHandler('del'),
// minLeadingWidth: 0,
// leading: Icon(Icons.delete_outline, color: errorColor),
// title: Text('删除', style: TextStyle(color: errorColor)),
// ),
],
),
);
}
}

View File

@ -438,7 +438,7 @@ class _HeaderControlState extends State<HeaderControl> {
}),
actions: <Widget>[
TextButton(
onPressed: () => SmartDialog.dismiss(),
onPressed: () => Get.back(),
child: Text(
'取消',
style: TextStyle(color: Theme.of(context).colorScheme.outline),