Merge branch 'main' into feature-m3Design

This commit is contained in:
guozhigq
2023-07-24 16:14:08 +08:00
18 changed files with 395 additions and 147 deletions

View File

@ -148,6 +148,15 @@ class Api {
// 获取历史记录 // 获取历史记录
static const String historyList = '/x/web-interface/history/cursor'; static const String historyList = '/x/web-interface/history/cursor';
// 暂停历史记录
static const String pauseHistory = '/x/v2/history/shadow/set';
// 查询历史记录暂停状态
static const String historyStatus = '/x/v2/history/shadow?jsonp=jsonp';
// 清空历史记录
static const String clearHistory = '/x/v2/history/clear';
// 热搜 // 热搜
static const String hotSearchList = static const String hotSearchList =
'https://s.search.bilibili.com/main/hotword'; 'https://s.search.bilibili.com/main/hotword';

View File

@ -1,5 +1,6 @@
import 'package:pilipala/http/api.dart'; import 'package:pilipala/http/api.dart';
import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/init.dart';
import 'package:pilipala/models/video/reply/data.dart';
class ReplyHttp { class ReplyHttp {
static Future replyList({ static Future replyList({
@ -17,7 +18,7 @@ class ReplyHttp {
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
return { return {
'status': true, 'status': true,
'data': res.data['data'], 'data': ReplyData.fromJson(res.data['data']),
}; };
} else { } else {
Map errMap = { Map errMap = {

View File

@ -112,4 +112,36 @@ class UserHttp {
return {'status': false, 'data': [], 'msg': res.data['message']}; return {'status': false, 'data': [], 'msg': res.data['message']};
} }
} }
// 暂停观看历史
static Future pauseHistory(bool switchStatus) async {
// 暂停switchStatus传true 否则false
var res = await Request().post(
Api.pauseHistory,
queryParameters: {
'switch': switchStatus,
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
},
);
return res;
}
// 观看历史暂停状态
static Future historyStatus() async {
var res = await Request().get(Api.historyStatus);
return res;
}
// 清空历史记录
static Future clearHistory() async {
var res = await Request().post(
Api.clearHistory,
queryParameters: {
'jsonp': 'jsonp',
'csrf': await Request.getCsrf(),
},
);
return res;
}
} }

View File

@ -9,6 +9,7 @@ import 'package:pilipala/pages/search/index.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/router/app_pages.dart';
import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/pages/main/view.dart';
import 'package:pilipala/utils/data.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
void main() async { void main() async {
@ -16,6 +17,7 @@ void main() async {
MediaKit.ensureInitialized(); MediaKit.ensureInitialized();
await GStrorage.init(); await GStrorage.init();
await Request.setCookie(); await Request.setCookie();
await Data.init();
runApp(const MyApp()); runApp(const MyApp());
} }

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/http_error.dart';
import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/fav/index.dart';
import 'package:pilipala/pages/fav/widgets/item.dart';
class FavPage extends StatefulWidget { class FavPage extends StatefulWidget {
const FavPage({super.key}); const FavPage({super.key});
@ -18,7 +19,11 @@ class _FavPageState extends State<FavPage> {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
centerTitle: false, centerTitle: false,
title: const Text('我的收藏'), titleSpacing: 0,
title: Text(
'我的收藏',
style: Theme.of(context).textTheme.titleMedium,
),
), ),
body: FutureBuilder( body: FutureBuilder(
future: _favController.queryFavFolder(), future: _favController.queryFavFolder(),
@ -30,31 +35,9 @@ class _FavPageState extends State<FavPage> {
() => ListView.builder( () => ListView.builder(
itemCount: _favController.favFolderData.value.list!.length, itemCount: _favController.favFolderData.value.list!.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return ListTile( return FavItem(
onTap: () => Get.toNamed( favFolderItem:
'/favDetail', _favController.favFolderData.value.list![index]);
arguments:
_favController.favFolderData.value.list![index],
parameters: {
'mediaId': _favController
.favFolderData.value.list![index].id
.toString(),
},
),
leading: const Icon(Icons.folder_special_outlined),
minLeadingWidth: 0,
title: Text(_favController
.favFolderData.value.list![index].title!),
subtitle: Text(
'${_favController.favFolderData.value.list![index].mediaCount}个内容',
style: TextStyle(
color: Theme.of(context).colorScheme.outline,
fontSize: Theme.of(context)
.textTheme
.labelSmall!
.fontSize),
),
);
}, },
), ),
); );

View File

@ -0,0 +1,96 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/utils/utils.dart';
class FavItem extends StatelessWidget {
var favFolderItem;
FavItem({super.key, required this.favFolderItem});
@override
Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(favFolderItem.fid);
return InkWell(
onTap: () => Get.toNamed(
'/favDetail',
arguments: favFolderItem,
parameters: {
'heroTag': heroTag,
'mediaId': favFolderItem.id.toString(),
},
),
child: Padding(
padding: const EdgeInsets.fromLTRB(12, 7, 12, 7),
child: LayoutBuilder(
builder: (context, boxConstraints) {
double width =
(boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2;
return SizedBox(
height: width / StyleString.aspectRatio,
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (context, boxConstraints) {
double maxWidth = boxConstraints.maxWidth;
double maxHeight = boxConstraints.maxHeight;
double PR = MediaQuery.of(context).devicePixelRatio;
return Hero(
tag: heroTag,
child: NetworkImgLayer(
src: favFolderItem.cover + '@.webp',
width: maxWidth,
height: maxHeight,
),
);
},
),
),
VideoContent(favFolderItem: favFolderItem)
],
),
);
},
),
),
);
}
}
class VideoContent extends StatelessWidget {
final favFolderItem;
const VideoContent({super.key, required this.favFolderItem});
@override
Widget build(BuildContext context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 2, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
favFolderItem.title,
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.titleSmall!.fontSize,
fontWeight: FontWeight.w500),
),
Text(
'${favFolderItem.mediaCount}个内容',
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
color: Theme.of(context).colorScheme.outline,
),
),
],
),
),
);
}
}

View File

@ -4,30 +4,42 @@ import 'package:pilipala/http/user.dart';
import 'package:pilipala/http/video.dart'; import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/user/fav_detail.dart'; import 'package:pilipala/models/user/fav_detail.dart';
import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/utils/id_utils.dart';
class FavDetailController extends GetxController { class FavDetailController extends GetxController {
FavFolderItemData? item; FavFolderItemData? item;
Rx<FavDetailData> favDetailData = FavDetailData().obs; Rx<FavDetailData> favDetailData = FavDetailData().obs;
int? mediaId; int? mediaId;
late String heroTag;
int currentPage = 1;
bool isLoadingMore = false;
RxMap favInfo = {}.obs;
RxList<FavDetailItemData> favList = [FavDetailItemData()].obs;
@override @override
void onInit() { void onInit() {
item = Get.arguments; item = Get.arguments;
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']!;
} }
super.onInit(); super.onInit();
} }
Future<dynamic> queryUserFavFolderDetail() async { Future<dynamic> queryUserFavFolderDetail({type = 'init'}) async {
print('🐯🐯虎');
var res = await await UserHttp.userFavFolderDetail( var res = await await UserHttp.userFavFolderDetail(
pn: 1, pn: currentPage,
ps: 15, ps: 20,
mediaId: mediaId!, mediaId: mediaId!,
); );
favDetailData.value = res['data']; if (res['status']) {
favInfo.value = res['data'].info;
if (currentPage == 1 && type == 'init') {
favList.value = res['data'].medias;
} else if (type == 'onload') {
favList.addAll(res['data'].medias);
}
}
return res; return res;
} }
@ -49,4 +61,8 @@ class FavDetailController extends GetxController {
} }
} }
} }
onLoad() {
queryUserFavFolderDetail(type: 'onload');
}
} }

View File

@ -34,6 +34,14 @@ class _FavDetailPageState extends State<FavDetailPage> {
} else if (_controller.offset <= 160) { } else if (_controller.offset <= 160) {
titleStreamC.add(false); titleStreamC.add(false);
} }
if (_controller.position.pixels >=
_controller.position.maxScrollExtent - 200) {
if (!_favDetailController.isLoadingMore) {
_favDetailController.isLoadingMore = true;
_favDetailController.onLoad();
}
}
}, },
); );
} }
@ -109,9 +117,8 @@ class _FavDetailPageState extends State<FavDetailPage> {
// mainAxisAlignment: MainAxisAlignment.center, // mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
SizedBox( Hero(
width: 180, tag: _favDetailController.heroTag,
height: 110,
child: NetworkImgLayer( child: NetworkImgLayer(
width: 180, width: 180,
height: 110, height: 110,
@ -156,7 +163,7 @@ class _FavDetailPageState extends State<FavDetailPage> {
padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14), padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14),
child: Obx( child: Obx(
() => Text( () => Text(
'${_favDetailController.favDetailData.value.medias != null ? _favDetailController.favDetailData.value.medias!.length : '-'}条视频', '${_favDetailController.favInfo['media_count'] ?? '-'}条视频',
style: TextStyle( style: TextStyle(
fontSize: fontSize:
Theme.of(context).textTheme.labelMedium!.fontSize, Theme.of(context).textTheme.labelMedium!.fontSize,
@ -184,12 +191,9 @@ class _FavDetailPageState extends State<FavDetailPage> {
() => SliverList( () => SliverList(
delegate: SliverChildBuilderDelegate((context, index) { delegate: SliverChildBuilderDelegate((context, index) {
return FavVideoCardH( return FavVideoCardH(
videoItem: _favDetailController videoItem: _favDetailController.favList[index],
.favDetailData.value.medias![index],
); );
}, }, childCount: _favDetailController.favList.length),
childCount: _favDetailController
.favDetailData.value.medias!.length),
), ),
); );
} }

View File

@ -1,16 +1,22 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/user.dart';
import 'package:pilipala/models/user/history.dart'; import 'package:pilipala/models/user/history.dart';
import 'package:pilipala/utils/storage.dart';
class HistoryController extends GetxController { class HistoryController extends GetxController {
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
RxList<HisListItem> historyList = [HisListItem()].obs; RxList<HisListItem> historyList = [HisListItem()].obs;
bool isLoadingMore = false; bool isLoadingMore = false;
RxBool pauseStatus = false.obs;
Box localCache = GStrorage.localCache;
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
historyStatus();
} }
Future queryHistoryList({type = 'init'}) async { Future queryHistoryList({type = 'init'}) async {
@ -40,4 +46,77 @@ class HistoryController extends GetxController {
Future onRefresh() async { Future onRefresh() async {
queryHistoryList(type: 'onRefresh'); queryHistoryList(type: 'onRefresh');
} }
// 暂停观看历史
Future onPauseHistory() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content:
Text(!pauseStatus.value ? '啊叻?你要暂停历史记录功能吗?' : '啊叻?要恢复历史记录功能吗?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: const Text('取消')),
TextButton(
onPressed: () async {
SmartDialog.showLoading(msg: '请求中');
var res = await UserHttp.pauseHistory(!pauseStatus.value);
SmartDialog.dismiss();
if (res.data['code'] == 0) {
SmartDialog.showToast(
!pauseStatus.value ? '暂停观看历史' : '恢复观看历史');
pauseStatus.value = !pauseStatus.value;
}
SmartDialog.dismiss();
},
child: Text(!pauseStatus.value ? '确认暂停' : '确认恢复'),
)
],
);
},
);
}
// 观看历史暂停状态
Future historyStatus() async {
var res = await UserHttp.historyStatus();
pauseStatus.value = res.data['data'];
localCache.put(LocalCacheKey.historyStatus, res.data['data']);
}
// 清空观看历史
Future onClearHistory() async {
SmartDialog.show(
useSystem: true,
animationType: SmartAnimationType.centerFade_otherSlide,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('提示'),
content: const Text('啊叻?你要清空历史记录功能吗?'),
actions: [
TextButton(
onPressed: () => SmartDialog.dismiss(),
child: const Text('取消')),
TextButton(
onPressed: () async {
SmartDialog.showLoading(msg: '请求中');
var res = await UserHttp.clearHistory();
SmartDialog.dismiss();
if (res.data['code'] == 0) {
SmartDialog.showToast('清空观看历史');
}
SmartDialog.dismiss();
historyList.clear();
},
child: const Text('确认清空'),
)
],
);
},
);
}
} }

View File

@ -39,8 +39,43 @@ class _HistoryPageState extends State<HistoryPage> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('观看记录'), titleSpacing: 0,
centerTitle: false, centerTitle: false,
title: Text(
'观看记录',
style: Theme.of(context).textTheme.titleMedium,
),
actions: [
PopupMenuButton<String>(
onSelected: (String type) {
// 处理菜单项选择的逻辑
switch (type) {
case 'pause':
_historyController.onPauseHistory();
break;
case 'clear':
_historyController.onClearHistory();
break;
default:
}
},
itemBuilder: (BuildContext context) => <PopupMenuEntry<String>>[
PopupMenuItem<String>(
value: 'pause',
child: Obx(
() => Text(!_historyController.pauseStatus.value
? '暂停观看记录'
: '恢复观看记录'),
),
),
const PopupMenuItem<String>(
value: 'clear',
child: Text('清空观看记录'),
),
],
),
const SizedBox(width: 6),
],
), ),
body: RefreshIndicator( body: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
@ -57,13 +92,23 @@ class _HistoryPageState extends State<HistoryPage> {
Map data = snapshot.data; Map data = snapshot.data;
if (data['status']) { if (data['status']) {
return Obx( return Obx(
() => SliverList( () => _historyController.historyList.isEmpty
delegate: SliverChildBuilderDelegate((context, index) { ? const SliverToBoxAdapter(
return HistoryItem( child: Center(
videoItem: _historyController.historyList[index], child: Text('没数据'),
); ),
}, childCount: _historyController.historyList.length), )
), : SliverList(
delegate: SliverChildBuilderDelegate(
(context, index) {
return HistoryItem(
videoItem:
_historyController.historyList[index],
);
},
childCount:
_historyController.historyList.length),
),
); );
} else { } else {
return HttpError( return HttpError(

View File

@ -3,6 +3,7 @@ import 'package:get/get.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/models/user/fav_folder.dart';
import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/media/index.dart';
import 'package:pilipala/utils/utils.dart';
class MediaPage extends StatelessWidget { class MediaPage extends StatelessWidget {
const MediaPage({super.key}); const MediaPage({super.key});
@ -169,12 +170,14 @@ class FavFolderItem extends StatelessWidget {
int? index; int? index;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
String heroTag = Utils.makeHeroTag(item!.fid);
return Container( return Container(
margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14),
child: GestureDetector( child: GestureDetector(
onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { onTap: () => Get.toNamed('/favDetail',
'mediaId': item!.id.toString(), arguments: item,
}), parameters: {'mediaId': item!.id.toString(), 'heroTag': heroTag}),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@ -199,10 +202,13 @@ class FavFolderItem extends StatelessWidget {
), ),
child: LayoutBuilder( child: LayoutBuilder(
builder: (context, BoxConstraints box) { builder: (context, BoxConstraints box) {
return NetworkImgLayer( return Hero(
src: item!.cover, tag: heroTag,
width: box.maxWidth, child: NetworkImgLayer(
height: box.maxHeight, src: item!.cover,
width: box.maxWidth,
height: box.maxHeight,
),
); );
}, },
), ),

View File

@ -54,6 +54,7 @@ class VideoDetailController extends GetxController
RxString bgCover = ''.obs; RxString bgCover = ''.obs;
Box user = GStrorage.user; Box user = GStrorage.user;
Box localCache = GStrorage.localCache;
@override @override
void onInit() { void onInit() {
@ -149,6 +150,9 @@ class VideoDetailController extends GetxController
if (user.get(UserBoxKey.userMid) == null) { if (user.get(UserBoxKey.userMid) == null) {
return; return;
} }
if (localCache.get(LocalCacheKey.historyStatus) == true) {
return;
}
Duration progress = meeduPlayerController.position.value; Duration progress = meeduPlayerController.position.value;
await VideoHttp.heartBeat( await VideoHttp.heartBeat(
bvid: bvid, bvid: bvid,

View File

@ -1,26 +1,21 @@
import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
import 'package:pilipala/http/reply.dart'; import 'package:pilipala/http/reply.dart';
import 'package:pilipala/http/video.dart';
import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart';
import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/common/reply_type.dart';
import 'package:pilipala/models/video/reply/data.dart';
import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/models/video/reply/item.dart';
class VideoReplyController extends GetxController { class VideoReplyController extends GetxController {
VideoReplyController( VideoReplyController(
this.aid, this.aid,
this.rpid, this.rpid,
this.level, this.replyLevel,
); );
final ScrollController scrollController = ScrollController(); final ScrollController scrollController = ScrollController();
// 视频aid 请求时使用的oid // 视频aid 请求时使用的oid
int? aid; int? aid;
// 层级 2为楼中楼 // 层级 2为楼中楼
String? level; String? replyLevel;
// rpid 请求楼中楼回复 // rpid 请求楼中楼回复
String? rpid; String? rpid;
RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs; RxList<ReplyItemModel> replyList = [ReplyItemModel()].obs;
@ -30,12 +25,6 @@ class VideoReplyController extends GetxController {
RxString noMore = ''.obs; RxString noMore = ''.obs;
// 当前回复的回复 // 当前回复的回复
ReplyItemModel? currentReplyItem; ReplyItemModel? currentReplyItem;
// 回复来源
String replySource = 'main';
// 根评论 id 回复楼中楼回复使用
int? rPid;
// 默认回复主楼
String replyLevel = '0';
ReplySortType sortType = ReplySortType.time; ReplySortType sortType = ReplySortType.time;
RxString sortTypeTitle = ReplySortType.time.titles.obs; RxString sortTypeTitle = ReplySortType.time.titles.obs;
@ -43,51 +32,44 @@ class VideoReplyController extends GetxController {
Future queryReplyList({type = 'init'}) async { Future queryReplyList({type = 'init'}) async {
isLoadingMore = true; isLoadingMore = true;
var res = level == '1' var res = replyLevel == '1'
? await ReplyHttp.replyList( ? await ReplyHttp.replyList(
oid: aid!, oid: aid!,
pageNum: currentPage + 1, pageNum: ++currentPage,
type: ReplyType.video.index, type: ReplyType.video.index,
sort: sortType.index, sort: sortType.index,
) )
: await ReplyHttp.replyReplyList( : await ReplyHttp.replyReplyList(
oid: aid!, root: rpid!, pageNum: currentPage + 1, type: 1); oid: aid!,
root: rpid!,
pageNum: ++currentPage,
type: ReplyType.video.index,
);
if (res['status']) { if (res['status']) {
res['data'] = ReplyData.fromJson(res['data']); List<ReplyItemModel> replies = res['data'].replies;
if (res['data'].replies.isNotEmpty) { if (replies.isNotEmpty) {
currentPage = currentPage + 1;
noMore.value = '加载中'; noMore.value = '加载中';
if (replyList.length == res['data'].page.acount) { if (replyList.length == res['data'].page.acount) {
noMore.value = '没有更多了'; noMore.value = '没有更多了';
} }
} else { } else {
if (currentPage == 0) { // 未登录状态replies可能返回null
noMore.value = '还没有评论'; noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了';
} else {
noMore.value = '没有更多了';
return;
}
} }
if (type == 'init') { if (type == 'init') {
List<ReplyItemModel> replies = res['data'].replies;
// 添加置顶回复 // 添加置顶回复
if (res['data'].upper.top != null) { if (res['data'].upper.top != null) {
bool flag = false; bool flag = res['data']
for (var i = 0; i < res['data'].topReplies.length; i++) { .topReplies
if (res['data'].topReplies[i].rpid == res['data'].upper.top.rpid) { .any((reply) => reply.rpid == res['data'].upper.top.rpid);
flag = true;
}
}
if (!flag) { if (!flag) {
replies.insert(0, res['data'].upper.top); replies.insert(0, res['data'].upper.top);
} }
} }
replies.insertAll(0, res['data'].topReplies); replies.insertAll(0, res['data'].topReplies);
res['data'].replies = replies; replyList.value = replies;
replyList.value = res['data'].replies!;
} else { } else {
replyList.addAll(res['data'].replies!); replyList.addAll(replies);
res['data'].replies.addAll(replyList);
} }
} }
isLoadingMore = false; isLoadingMore = false;

View File

@ -6,7 +6,6 @@ import 'package:get/get.dart';
import 'package:pilipala/common/skeleton/video_reply.dart'; import 'package:pilipala/common/skeleton/video_reply.dart';
import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/http_error.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/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/pages/video/detail/replyNew/index.dart'; import 'package:pilipala/pages/video/detail/replyNew/index.dart';
import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/id_utils.dart';
@ -14,14 +13,14 @@ import 'controller.dart';
import 'widgets/reply_item.dart'; import 'widgets/reply_item.dart';
class VideoReplyPanel extends StatefulWidget { class VideoReplyPanel extends StatefulWidget {
String? bvid; final String? bvid;
int rpid; final int rpid;
String? level; final String? replyLevel;
Key? key;
VideoReplyPanel({ const VideoReplyPanel({
this.bvid, this.bvid,
this.rpid = 0, this.rpid = 0,
this.level, this.replyLevel,
super.key, super.key,
}); });
@ -46,18 +45,13 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
void initState() { void initState() {
int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0;
super.initState(); super.initState();
replyLevel = widget.level ?? '1'; replyLevel = widget.replyLevel ?? '1';
if (widget.level != null && widget.level == '2') { if (replyLevel == '2') {
_videoReplyController = Get.put( _videoReplyController = Get.put(
VideoReplyController(oid, widget.rpid.toString(), '2'), VideoReplyController(oid, widget.rpid.toString(), replyLevel),
tag: widget.rpid.toString()); tag: widget.rpid.toString());
_videoReplyController.rPid = widget.rpid;
} else { } else {
// fix 评论加载不对称 _videoReplyController = Get.put(VideoReplyController(oid, '', replyLevel),
// int oid = Get.parameters['bvid'] != null
// ? IdUtils.bv2av(Get.parameters['bvid']!)
// : 0;
_videoReplyController = Get.put(VideoReplyController(oid, '', '1'),
tag: Get.arguments['heroTag']); tag: Get.arguments['heroTag']);
} }
@ -101,20 +95,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
} }
} }
void _showReply(source, {ReplyItemModel? replyItem, replyLevel}) async {
// source main 直接回复 floor 楼中楼回复
if (source == 'floor') {
_videoReplyController.currentReplyItem = replyItem;
_videoReplyController.replySource = source;
_videoReplyController.replyLevel = replyLevel ?? '1';
} else {
_videoReplyController.replyLevel = '0';
}
// await Future.delayed(const Duration(microseconds: 100));
// _videoReplyController.wakeUpReply();
}
// 展示二级回复 // 展示二级回复
void replyReply(replyItem) { void replyReply(replyItem) {
VideoDetailController videoDetailCtr = VideoDetailController videoDetailCtr =
@ -136,7 +116,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
Widget build(BuildContext context) { Widget build(BuildContext context) {
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
setState(() {});
_videoReplyController.currentPage = 0; _videoReplyController.currentPage = 0;
return await _videoReplyController.queryReplyList(); return await _videoReplyController.queryReplyList();
}, },
@ -204,18 +183,15 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
: SliverList( : SliverList(
delegate: SliverChildBuilderDelegate( delegate: SliverChildBuilderDelegate(
(context, index) { (context, index) {
double bottom =
MediaQuery.of(context).padding.bottom;
if (index == if (index ==
_videoReplyController _videoReplyController
.replyList.length) { .replyList.length) {
return Container( return Container(
padding: EdgeInsets.only( padding:
bottom: MediaQuery.of(context) EdgeInsets.only(bottom: bottom),
.padding height: bottom + 100,
.bottom),
height: MediaQuery.of(context)
.padding
.bottom +
100,
child: Center( child: Center(
child: Obx(() => Text( child: Obx(() => Text(
_videoReplyController _videoReplyController
@ -265,8 +241,6 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
child: SlideTransition( child: SlideTransition(
position: Tween<Offset>( position: Tween<Offset>(
begin: const Offset(0, 2), begin: const Offset(0, 2),
// 评论内容为空/不足一屏
// begin: const Offset(0, 0),
end: const Offset(0, 0), end: const Offset(0, 0),
).animate(CurvedAnimation( ).animate(CurvedAnimation(
parent: fabAnimationCtr, parent: fabAnimationCtr,

View File

@ -21,11 +21,6 @@ class VideoReplyReplyController extends GetxController {
// 当前回复的回复 // 当前回复的回复
ReplyItemModel? currentReplyItem; ReplyItemModel? currentReplyItem;
// 根评论 id 回复楼中楼回复使用
int? rPid;
// 默认回复主楼
String replyLevel = '0';
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();

View File

@ -11,14 +11,14 @@ import 'package:pilipala/utils/storage.dart';
import 'controller.dart'; import 'controller.dart';
class VideoReplyReplyPanel extends StatefulWidget { class VideoReplyReplyPanel extends StatefulWidget {
int? oid; final int? oid;
int? rpid; final int? rpid;
Function? closePanel; final Function? closePanel;
ReplyItemModel? firstFloor; final ReplyItemModel? firstFloor;
String? source; final String? source;
ReplyType? replyType; final ReplyType? replyType;
VideoReplyReplyPanel({ const VideoReplyReplyPanel({
this.oid, this.oid,
this.rpid, this.rpid,
this.closePanel, this.closePanel,
@ -91,7 +91,6 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
onPressed: () { onPressed: () {
_videoReplyReplyController.currentPage = 0; _videoReplyReplyController.currentPage = 0;
_videoReplyReplyController.rPid = 0;
widget.closePanel!(); widget.closePanel!();
Navigator.pop(context); Navigator.pop(context);
}, },

16
lib/utils/data.dart Normal file
View File

@ -0,0 +1,16 @@
import 'package:hive/hive.dart';
import 'package:pilipala/http/user.dart';
import 'storage.dart';
class Data {
static Future init() async {
await historyStatus();
}
static Future historyStatus() async {
Box localCache = GStrorage.localCache;
var res = await UserHttp.historyStatus();
localCache.put(LocalCacheKey.historyStatus, res.data['data']);
}
}

View File

@ -59,3 +59,8 @@ class UserBoxKey {
class SettingBoxKey { class SettingBoxKey {
static const String themeMode = 'themeMode'; static const String themeMode = 'themeMode';
} }
class LocalCacheKey {
// 历史记录暂停状态 默认false
static const String historyStatus = 'historyStatus';
}