Merge branch 'main' into design

This commit is contained in:
guozhigq
2024-10-07 23:26:39 +08:00
16 changed files with 292 additions and 284 deletions

View File

@ -592,4 +592,7 @@ class Api {
/// 直播间记录 /// 直播间记录
static const String liveRoomEntry = static const String liveRoomEntry =
'${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction'; '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction';
/// 删除评论
static const String replyDel = '/x/v2/reply/del';
} }

View File

@ -229,12 +229,25 @@ class LoginHttp {
data: formData, data: formData,
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return { if (res.data['data']['status'] == 0) {
'status': true, return {
'data': res.data['data'], 'status': true,
}; 'data': res.data['data'],
};
} else {
return {
'status': false,
'code': 1,
'data': res.data['data'],
'msg': res.data['data']['message'],
};
}
} else { } else {
return {'status': false, 'data': [], 'msg': res.data['message']}; return {
'status': false,
'data': [],
'msg': res.data['message'],
};
} }
} }

View File

@ -115,4 +115,25 @@ class ReplyHttp {
}; };
} }
} }
static Future replyDel({
required int type, //replyType
required int oid,
required int rpid,
}) async {
var res = await Request().post(
Api.replyDel,
queryParameters: {
'type': type, //type.index
'oid': oid,
'rpid': rpid,
'csrf': await Request.getCsrf(),
},
);
if (res.data['code'] == 0) {
return {'status': true, 'msg': '删除成功'};
} else {
return {'status': false, 'msg': res.data['message']};
}
}
} }

View File

@ -79,56 +79,68 @@ class _FavPageState extends State<FavPage> {
const SizedBox(width: 14), const SizedBox(width: 14),
], ],
), ),
body: FutureBuilder( body: RefreshIndicator(
future: _futureBuilderFuture, onRefresh: () async {
builder: (context, snapshot) { _favController.hasMore.value = true;
if (snapshot.connectionState == ConnectionState.done) { _favController.currentPage = 1;
Map? data = snapshot.data; setState(() {
if (data != null && data['status']) { _futureBuilderFuture = _favController.queryFavFolder(type: 'init');
return Obx( });
() => ListView.builder(
controller: scrollController,
itemCount: _favController.favFolderList.length,
itemBuilder: (context, index) {
return FavItem(
favFolderItem: _favController.favFolderList[index],
isOwner: _favController.isOwner.value,
);
},
),
);
} else {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
slivers: [
HttpError(
errMsg: data?['msg'] ?? '请求异常',
btnText: data?['code'] == -101 ? '去登录' : null,
fn: () {
if (data?['code'] == -101) {
RoutePush.loginRedirectPush();
} else {
setState(() {
_futureBuilderFuture =
_favController.queryFavFolder();
});
}
},
),
],
);
}
} else {
// 骨架屏
return ListView.builder(
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
itemCount: 10,
);
}
}, },
child: _buildBody(),
), ),
); );
} }
Widget _buildBody() {
return FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
Map? data = snapshot.data;
if (data != null && data['status']) {
return Obx(
() => ListView.builder(
controller: scrollController,
itemCount: _favController.favFolderList.length,
itemBuilder: (context, index) {
return FavItem(
favFolderItem: _favController.favFolderList[index],
isOwner: _favController.isOwner.value,
);
},
),
);
} else {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
slivers: [
HttpError(
errMsg: data?['msg'] ?? '请求异常',
btnText: data?['code'] == -101 ? '去登录' : null,
fn: () {
if (data?['code'] == -101) {
RoutePush.loginRedirectPush();
} else {
setState(() {
_futureBuilderFuture = _favController.queryFavFolder();
});
}
},
),
],
);
}
} else {
// 骨架屏
return ListView.builder(
itemBuilder: (context, index) {
return const VideoCardHSkeleton();
},
itemCount: 10,
);
}
},
);
}
} }

View File

@ -10,6 +10,7 @@ import 'package:pilipala/utils/utils.dart';
class FavDetailController extends GetxController { class FavDetailController extends GetxController {
FavFolderItemData? item; FavFolderItemData? item;
RxString title = ''.obs;
int? mediaId; int? mediaId;
late String heroTag; late String heroTag;
@ -24,6 +25,7 @@ class FavDetailController extends GetxController {
@override @override
void onInit() { void onInit() {
item = Get.arguments; item = Get.arguments;
title.value = item!.title!;
if (Get.parameters.keys.isNotEmpty) { if (Get.parameters.keys.isNotEmpty) {
mediaId = int.parse(Get.parameters['mediaId']!); mediaId = int.parse(Get.parameters['mediaId']!);
heroTag = Get.parameters['heroTag']!; heroTag = Get.parameters['heroTag']!;
@ -117,16 +119,18 @@ class FavDetailController extends GetxController {
} }
onEditFavFolder() async { onEditFavFolder() async {
Get.toNamed( var res = await Get.toNamed(
'/favEdit', '/favEdit',
arguments: { arguments: {
'mediaId': mediaId.toString(), 'mediaId': mediaId.toString(),
'title': item!.title, 'title': item!.title,
'intro': item!.intro, 'intro': item!.intro,
'cover': item!.cover, 'cover': item!.cover,
'privacy': item!.attr, 'privacy': [23, 1].contains(item!.attr) ? 1 : 0,
}, },
); );
title.value = res['title'];
print(title);
} }
Future toViewPlayAll() async { Future toViewPlayAll() async {

View File

@ -80,9 +80,11 @@ class _FavDetailPageState extends State<FavDetailPage> {
Column( Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Obx(
_favDetailController.item!.title!, () => Text(
style: Theme.of(context).textTheme.titleMedium, _favDetailController.title.value,
style: Theme.of(context).textTheme.titleMedium,
),
), ),
Text( Text(
'${_favDetailController.mediaCount}条视频', '${_favDetailController.mediaCount}条视频',
@ -156,14 +158,16 @@ class _FavDetailPageState extends State<FavDetailPage> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Obx(
_favDetailController.item!.title!, () => Text(
style: TextStyle( _favDetailController.title.value,
fontSize: Theme.of(context) style: TextStyle(
.textTheme fontSize: Theme.of(context)
.titleMedium! .textTheme
.fontSize, .titleMedium!
fontWeight: FontWeight.bold), .fontSize,
fontWeight: FontWeight.bold),
),
), ),
const SizedBox(height: 4), const SizedBox(height: 4),
Text( Text(

View File

@ -56,7 +56,7 @@ class FavEditController extends GetxController {
); );
if (res['status']) { if (res['status']) {
SmartDialog.showToast('编辑成功'); SmartDialog.showToast('编辑成功');
Get.back(); Get.back(result: {'title': title});
} else { } else {
SmartDialog.showToast(res['msg']); SmartDialog.showToast(res['msg']);
} }

View File

@ -45,6 +45,7 @@ class LoginPageController extends GetxController {
RxInt validSeconds = 180.obs; RxInt validSeconds = 180.obs;
Timer? validTimer; Timer? validTimer;
late String qrcodeKey; late String qrcodeKey;
RxBool passwordVisible = false.obs;
// 监听pageView切换 // 监听pageView切换
void onPageChange(int index) { void onPageChange(int index) {
@ -128,7 +129,14 @@ class LoginPageController extends GetxController {
if (res['status']) { if (res['status']) {
await LoginUtils.confirmLogin('', null); await LoginUtils.confirmLogin('', null);
} else { } else {
SmartDialog.showToast(res['msg']); await SmartDialog.showToast(res['msg']);
if (res.containsKey('code') && res['code'] == 1) {
Get.toNamed('/webview', parameters: {
'url': res['data']['data']['url'],
'type': 'url',
'pageTitle': '登录验证',
});
}
} }
} else { } else {
SmartDialog.showToast(webKeyRes['msg']); SmartDialog.showToast(webKeyRes['msg']);

View File

@ -269,25 +269,46 @@ class _LoginPageState extends State<LoginPage> {
), ),
Container( Container(
margin: const EdgeInsets.only(top: 38, bottom: 15), margin: const EdgeInsets.only(top: 38, bottom: 15),
child: TextFormField( child: Obx(() => TextFormField(
controller: _loginPageCtr.passwordTextController, controller:
focusNode: _loginPageCtr.passwordTextFieldNode, _loginPageCtr.passwordTextController,
keyboardType: TextInputType.visiblePassword, focusNode:
decoration: InputDecoration( _loginPageCtr.passwordTextFieldNode,
isDense: true, keyboardType: TextInputType.visiblePassword,
labelText: '输入密码', obscureText:
border: OutlineInputBorder( _loginPageCtr.passwordVisible.value,
borderRadius: BorderRadius.circular(6.0), decoration: InputDecoration(
), isDense: true,
), labelText: '输入密码',
// 校验用户名 border: OutlineInputBorder(
validator: (v) { borderRadius: BorderRadius.circular(6.0),
return v!.trim().isNotEmpty ? null : "密码不能为空"; ),
}, suffixIcon: IconButton(
onSaved: (val) { icon: Icon(
print(val); _loginPageCtr.passwordVisible.value
}, ? Icons.visibility
), : Icons.visibility_off,
color: Theme.of(context)
.colorScheme
.primary,
),
onPressed: () {
_loginPageCtr.passwordVisible.value =
!_loginPageCtr
.passwordVisible.value;
},
),
),
// 校验用户名
validator: (v) {
return v!.trim().isNotEmpty
? null
: "密码不能为空";
},
onSaved: (val) {
print(val);
},
)),
), ),
const Spacer(), const Spacer(),
Row( Row(

View File

@ -157,7 +157,7 @@ class _OpusPageState extends State<OpusPage> {
Container( Container(
alignment: TextHelper.getAlignment(paragraph.align), alignment: TextHelper.getAlignment(paragraph.align),
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
child: Text.rich( child: SelectableText.rich(
TextSpan( TextSpan(
children: paragraph.text?.nodes?.map((node) { children: paragraph.text?.nodes?.map((node) {
return TextHelper.buildTextSpan( return TextHelper.buildTextSpan(

View File

@ -20,7 +20,7 @@ class ReadPageController extends GetxController {
super.onInit(); super.onInit();
title.value = Get.parameters['title'] ?? ''; title.value = Get.parameters['title'] ?? '';
id = Get.parameters['id']!; id = Get.parameters['id']!;
articleType = Get.parameters['articleType']!; articleType = Get.parameters['articleType'] ?? 'read';
url = 'https://www.bilibili.com/read/cv$id'; url = 'https://www.bilibili.com/read/cv$id';
scrollController.addListener(_scrollListener); scrollController.addListener(_scrollListener);
fetchViewInfo(); fetchViewInfo();

View File

@ -126,7 +126,6 @@ class _ReadPageState extends State<ReadPage> {
Widget _buildContent(ReadDataModel cvData) { Widget _buildContent(ReadDataModel cvData) {
final List<String> picList = _extractPicList(cvData); final List<String> picList = _extractPicList(cvData);
final List<String> imgList = extractDataSrc(cvData.readInfo!.content!); final List<String> imgList = extractDataSrc(cvData.readInfo!.content!);
return Padding( return Padding(
padding: EdgeInsets.fromLTRB( padding: EdgeInsets.fromLTRB(
16, 0, 16, MediaQuery.of(context).padding.bottom + 40), 16, 0, 16, MediaQuery.of(context).padding.bottom + 40),
@ -163,9 +162,11 @@ class _ReadPageState extends State<ReadPage> {
padding: const EdgeInsets.only(bottom: 20), padding: const EdgeInsets.only(bottom: 20),
child: _buildAuthorWidget(cvData), child: _buildAuthorWidget(cvData),
), ),
HtmlRender( SelectionArea(
htmlContent: cvData.readInfo!.content!, child: HtmlRender(
imgList: imgList, htmlContent: cvData.readInfo!.content!,
imgList: imgList,
),
), ),
], ],
); );
@ -206,7 +207,7 @@ class _ReadPageState extends State<ReadPage> {
return Container( return Container(
alignment: TextHelper.getAlignment(paragraph.align), alignment: TextHelper.getAlignment(paragraph.align),
margin: const EdgeInsets.only(bottom: 10), margin: const EdgeInsets.only(bottom: 10),
child: Text.rich( child: SelectableText.rich(
TextSpan( TextSpan(
children: paragraph.text?.nodes?.map((node) { children: paragraph.text?.nodes?.map((node) {
return TextHelper.buildTextSpan(node, paragraph.align, context); return TextHelper.buildTextSpan(node, paragraph.align, context);

View File

@ -3,7 +3,6 @@ import 'dart:async';
import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:bottom_sheet/bottom_sheet.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/constants.dart';
@ -154,11 +153,10 @@ class VideoIntroController extends GetxController {
} }
if (hasLike.value && hasCoin.value && hasFav.value) { if (hasLike.value && hasCoin.value && hasFav.value) {
// 已点赞、投币、收藏 // 已点赞、投币、收藏
SmartDialog.showToast('🙏 UP已经收到了'); SmartDialog.showToast('UP已经收到了');
return false; return false;
} }
var result = await VideoHttp.oneThree(bvid: bvid); var result = await VideoHttp.oneThree(bvid: bvid);
print('🤣🦴:${result["data"]}');
if (result['status']) { if (result['status']) {
hasLike.value = result["data"]["like"]; hasLike.value = result["data"]["like"];
hasCoin.value = result["data"]["coin"]; hasCoin.value = result["data"]["coin"];
@ -604,4 +602,34 @@ class VideoIntroController extends GetxController {
).buildShowContent(Get.context!), ).buildShowContent(Get.context!),
); );
} }
//
oneThreeDialog() {
showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('是否一键三连'),
actions: [
TextButton(
onPressed: () => navigator!.pop(),
child: Text(
'取消',
style: TextStyle(
color: Theme.of(Get.context!).colorScheme.outline),
),
),
TextButton(
onPressed: () async {
actionOneThree();
navigator!.pop();
},
child: const Text('确认'),
)
],
);
},
);
}
} }

View File

@ -1,7 +1,4 @@
import 'dart:ffi';
import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:bottom_sheet/bottom_sheet.dart';
import 'package:easy_debounce/easy_throttle.dart';
import 'package:expandable/expandable.dart'; import 'package:expandable/expandable.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@ -151,11 +148,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
RxBool isExpand = false.obs; RxBool isExpand = false.obs;
late ExpandableController _expandableCtr; late ExpandableController _expandableCtr;
// 一键三连动画
late AnimationController _controller;
late Animation<double> _scaleTransition;
final RxDouble _progress = 0.0.obs;
void Function()? handleState(Future<dynamic> Function() action) { void Function()? handleState(Future<dynamic> Function() action) {
return isProcessing return isProcessing
? null ? null
@ -178,26 +170,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
owner = widget.videoDetail!.owner; owner = widget.videoDetail!.owner;
enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true);
_expandableCtr = ExpandableController(initialExpanded: false); _expandableCtr = ExpandableController(initialExpanded: false);
/// 一键三连动画
_controller = AnimationController(
duration: const Duration(milliseconds: 1500),
reverseDuration: const Duration(milliseconds: 300),
vsync: this,
);
_scaleTransition = Tween<double>(begin: 0.5, end: 1.5).animate(_controller)
..addListener(() async {
_progress.value =
double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3));
if (_progress.value == 1) {
if (_controller.status == AnimationStatus.completed) {
await videoIntroController.actionOneThree();
}
_progress.value = 0;
_scaleTransition.removeListener(() {});
_controller.stop();
}
});
} }
// 收藏 // 收藏
@ -279,8 +251,6 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
@override @override
void dispose() { void dispose() {
_expandableCtr.dispose(); _expandableCtr.dispose();
_controller.dispose();
_scaleTransition.removeListener(() {});
super.dispose(); super.dispose();
} }
@ -573,131 +543,34 @@ class _VideoInfoState extends State<VideoInfo> with TickerProviderStateMixin {
Widget actionGrid(BuildContext context, videoIntroController) { Widget actionGrid(BuildContext context, videoIntroController) {
final actionTypeSort = GlobalDataCache().actionTypeSort; final actionTypeSort = GlobalDataCache().actionTypeSort;
Widget progressWidget(progress) {
return SizedBox(
width: const IconThemeData.fallback().size! + 5,
height: const IconThemeData.fallback().size! + 5,
child: CircularProgressIndicator(
value: progress.value,
strokeWidth: 2,
),
);
}
Map<String, Widget> menuListWidgets = { Map<String, Widget> menuListWidgets = {
'like': Obx( 'like': Obx(
() { () => ActionItem(
bool likeStatus = videoIntroController.hasLike.value; icon: const Icon(FontAwesomeIcons.thumbsUp),
ColorScheme colorScheme = Theme.of(context).colorScheme; selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp),
return Stack( onTap: handleState(videoIntroController.actionLikeVideo),
children: [ onLongPress: () => videoIntroController.oneThreeDialog(),
Positioned( selectStatus: videoIntroController.hasLike.value,
top: ((Get.size.width - 24) / 5) / 2 - text: widget.videoDetail!.stat!.like!.toString(),
(const IconThemeData.fallback().size!), ),
left: ((Get.size.width - 24) / 5) / 2 -
(const IconThemeData.fallback().size! + 5) / 2,
child: progressWidget(_progress)),
InkWell(
onTapDown: (details) {
feedBack();
if (videoIntroController.userInfo == null) {
SmartDialog.showToast('账号未登录');
return;
}
_controller.forward();
},
onTapUp: (TapUpDetails details) {
if (_progress.value == 0) {
feedBack();
EasyThrottle.throttle(
'my-throttler', const Duration(milliseconds: 200), () {
videoIntroController.actionLikeVideo();
});
}
_controller.reverse();
},
onTapCancel: () {
_controller.reverse();
},
borderRadius: StyleString.mdRadius,
child: SizedBox(
width: (Get.size.width - 24) / 5,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const SizedBox(height: 4),
AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
transitionBuilder:
(Widget child, Animation<double> animation) {
return ScaleTransition(
scale: animation, child: child);
},
child: Icon(
key: ValueKey<bool>(likeStatus),
likeStatus
? FontAwesomeIcons.solidThumbsUp
: FontAwesomeIcons.thumbsUp,
color: likeStatus
? colorScheme.primary
: colorScheme.outline,
size: 21,
),
),
const SizedBox(height: 6),
Text(
widget.videoDetail!.stat!.like!.toString(),
style: TextStyle(
color: likeStatus ? colorScheme.primary : null,
fontSize:
Theme.of(context).textTheme.labelSmall!.fontSize,
),
)
],
),
),
),
],
);
},
), ),
'coin': Obx( 'coin': Obx(
() => Stack( () => ActionItem(
children: [ icon: const Icon(FontAwesomeIcons.b),
Positioned( selectIcon: const Icon(FontAwesomeIcons.b),
top: ((Get.size.width - 24) / 5) / 2 - onTap: handleState(videoIntroController.actionCoinVideo),
(const IconThemeData.fallback().size!), selectStatus: videoIntroController.hasCoin.value,
left: ((Get.size.width - 24) / 5) / 2 - text: widget.videoDetail!.stat!.coin!.toString(),
(const IconThemeData.fallback().size! + 5) / 2,
child: progressWidget(_progress)),
ActionItem(
icon: const Icon(FontAwesomeIcons.b),
selectIcon: const Icon(FontAwesomeIcons.b),
onTap: handleState(videoIntroController.actionCoinVideo),
selectStatus: videoIntroController.hasCoin.value,
text: widget.videoDetail!.stat!.coin!.toString(),
),
],
), ),
), ),
'collect': Obx( 'collect': Obx(
() => Stack( () => ActionItem(
children: [ icon: const Icon(FontAwesomeIcons.star),
Positioned( selectIcon: const Icon(FontAwesomeIcons.solidStar),
top: ((Get.size.width - 24) / 5) / 2 - onTap: () => showFavBottomSheet(),
(const IconThemeData.fallback().size!), onLongPress: () => showFavBottomSheet(type: 'longPress'),
left: ((Get.size.width - 24) / 5) / 2 - selectStatus: videoIntroController.hasFav.value,
(const IconThemeData.fallback().size! + 5) / 2, text: widget.videoDetail!.stat!.favorite!.toString(),
child: progressWidget(_progress)),
ActionItem(
icon: const Icon(FontAwesomeIcons.star),
selectIcon: const Icon(FontAwesomeIcons.solidStar),
onTap: () => showFavBottomSheet(),
onLongPress: () => showFavBottomSheet(type: 'longPress'),
selectStatus: videoIntroController.hasFav.value,
text: widget.videoDetail!.stat!.favorite!.toString(),
),
],
), ),
), ),
'watchLater': ActionItem( 'watchLater': ActionItem(

View File

@ -9,6 +9,7 @@ import 'package:get/get.dart';
import 'package:hive/hive.dart'; import 'package:hive/hive.dart';
import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/badge.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/http/reply.dart';
import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/models/video/reply/item.dart';
import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/main/index.dart';
@ -18,6 +19,7 @@ import 'package:pilipala/plugin/pl_gallery/index.dart';
import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/plugin/pl_popup/index.dart';
import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/app_scheme.dart';
import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/feed_back.dart';
import 'package:pilipala/utils/global_data_cache.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/url_utils.dart';
@ -48,6 +50,8 @@ class ReplyItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final bool isOwner = int.parse(replyItem!.member!.mid!) ==
(GlobalDataCache().userInfo?.mid ?? -1);
return Material( return Material(
child: InkWell( child: InkWell(
// 点击整个评论区 评论详情/回复 // 点击整个评论区 评论详情/回复
@ -73,6 +77,7 @@ class ReplyItem extends StatelessWidget {
return MorePanel( return MorePanel(
item: replyItem, item: replyItem,
mainFloor: true, mainFloor: true,
isOwner: isOwner,
); );
}, },
); );
@ -698,14 +703,11 @@ InlineSpan buildContent(
'', '',
); );
} else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) { } else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) {
Get.toNamed( Get.toNamed('/read', parameters: {
'/webview', 'title': title,
parameters: { 'id': Utils.matchNum(matchStr).first.toString(),
'url': 'https://www.bilibili.com/read/$matchStr', 'articleType': 'read',
'type': 'url', });
'pageTitle': title
},
);
} else { } else {
Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); Uri uri = Uri.parse(matchStr.replaceAll('/?', '?'));
SchemeEntity scheme = SchemeEntity( SchemeEntity scheme = SchemeEntity(
@ -1004,10 +1006,12 @@ InlineSpan buildContent(
class MorePanel extends StatelessWidget { class MorePanel extends StatelessWidget {
final dynamic item; final dynamic item;
final bool mainFloor; final bool mainFloor;
final bool isOwner;
const MorePanel({ const MorePanel({
super.key, super.key,
required this.item, required this.item,
this.mainFloor = false, this.mainFloor = false,
this.isOwner = false,
}); });
Future<dynamic> menuActionHandler(String type) async { Future<dynamic> menuActionHandler(String type) async {
@ -1043,9 +1047,43 @@ class MorePanel extends StatelessWidget {
// case 'report': // case 'report':
// SmartDialog.showToast('举报'); // SmartDialog.showToast('举报');
// break; // break;
// case 'delete': case 'delete':
// SmartDialog.showToast('删除'); // 删除评论提示
// break; await showDialog(
context: Get.context!,
builder: (context) {
return AlertDialog(
title: const Text('删除评论'),
content: const Text('删除评论后,评论下所有回复将被删除,确定删除吗?'),
actions: <Widget>[
TextButton(
onPressed: () => Get.back(),
child: Text('取消',
style: TextStyle(
color: Theme.of(context).colorScheme.outline)),
),
TextButton(
onPressed: () async {
Get.back();
var result = await ReplyHttp.replyDel(
type: item.type!,
oid: item.oid!,
rpid: item.rpid!,
);
if (result['status']) {
SmartDialog.showToast('评论删除成功,需手动刷新');
Get.back();
} else {
SmartDialog.showToast(result['msg']);
}
},
child: const Text('确定'),
),
],
);
},
);
break;
default: default:
} }
} }
@ -1054,6 +1092,7 @@ class MorePanel extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
ColorScheme colorScheme = Theme.of(context).colorScheme; ColorScheme colorScheme = Theme.of(context).colorScheme;
TextTheme textTheme = Theme.of(context).textTheme; TextTheme textTheme = Theme.of(context).textTheme;
Color errorColor = colorScheme.error;
return Container( return Container(
padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom),
child: Column( child: Column(
@ -1106,12 +1145,14 @@ class MorePanel extends StatelessWidget {
// leading: Icon(Icons.report_outlined, color: errorColor), // leading: Icon(Icons.report_outlined, color: errorColor),
// title: Text('举报', style: TextStyle(color: errorColor)), // title: Text('举报', style: TextStyle(color: errorColor)),
// ), // ),
// ListTile( if (isOwner)
// onTap: () async => await menuActionHandler('del'), ListTile(
// minLeadingWidth: 0, onTap: () async => await menuActionHandler('delete'),
// leading: Icon(Icons.delete_outline, color: errorColor), minLeadingWidth: 0,
// title: Text('删除', style: TextStyle(color: errorColor)), leading: Icon(Icons.delete_outline, color: errorColor),
// ), title: Text('删除评论',
style: textTheme.titleSmall!.copyWith(color: errorColor)),
),
], ],
), ),
); );

View File

@ -68,10 +68,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
late final AppLifecycleListener _lifecycleListener; late final AppLifecycleListener _lifecycleListener;
late double statusHeight; late double statusHeight;
// 稍后再看控制器
// late AnimationController _laterCtr;
// late Animation<Offset> _laterOffsetAni;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -108,7 +104,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
} }
WidgetsBinding.instance.addObserver(this); WidgetsBinding.instance.addObserver(this);
lifecycleListener(); lifecycleListener();
// watchLaterControllerInit();
} }
// 获取视频资源,初始化播放器 // 获取视频资源,初始化播放器
@ -242,8 +237,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
appbarStream.close(); appbarStream.close();
WidgetsBinding.instance.removeObserver(this); WidgetsBinding.instance.removeObserver(this);
_lifecycleListener.dispose(); _lifecycleListener.dispose();
// _laterCtr.dispose();
// _laterOffsetAni.removeListener(() {});
super.dispose(); super.dispose();
} }
@ -297,6 +290,7 @@ class _VideoDetailPageState extends State<VideoDetailPage>
plPlayerController?.play(); plPlayerController?.play();
} }
plPlayerController?.addStatusLister(playerListener); plPlayerController?.addStatusLister(playerListener);
appbarStream.add(0);
super.didPopNext(); super.didPopNext();
} }
@ -490,21 +484,6 @@ class _VideoDetailPageState extends State<VideoDetailPage>
); );
} }
/// 稍后再看控制器初始化
// void watchLaterControllerInit() {
// _laterCtr = AnimationController(
// duration: const Duration(milliseconds: 300),
// vsync: this,
// );
// _laterOffsetAni = Tween<Offset>(
// begin: const Offset(0.0, 1.0),
// end: Offset.zero,
// ).animate(CurvedAnimation(
// parent: _laterCtr,
// curve: Curves.easeInOut,
// ));
// }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sizeContext = MediaQuery.sizeOf(context); final sizeContext = MediaQuery.sizeOf(context);