Merge branch 'main' into design

This commit is contained in:
guozhigq
2024-11-02 22:25:24 +08:00
12 changed files with 99 additions and 77 deletions

View File

@ -5,7 +5,7 @@ import 'package:pilipala/common/constants.dart';
import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart';
import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/models/video_detail_res.dart';
import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/utils.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:scrollview_observer/scrollview_observer.dart';
import '../models/common/video_episode_type.dart'; import '../models/common/video_episode_type.dart';
import 'widgets/badge.dart'; import 'widgets/badge.dart';
import 'widgets/stat/danmu.dart'; import 'widgets/stat/danmu.dart';
@ -83,7 +83,8 @@ class PagesBottomSheet extends StatefulWidget {
} }
class _PagesBottomSheetState extends State<PagesBottomSheet> { class _PagesBottomSheetState extends State<PagesBottomSheet> {
final ItemScrollController _itemScrollController = ItemScrollController(); final ScrollController _listScrollController = ScrollController();
late ListObserverController _listObserverController;
final ScrollController _scrollController = ScrollController(); final ScrollController _scrollController = ScrollController();
late int currentIndex; late int currentIndex;
@ -92,10 +93,17 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
super.initState(); super.initState();
currentIndex = currentIndex =
widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid); widget.episodes.indexWhere((dynamic e) => e.cid == widget.currentCid);
_listObserverController =
ListObserverController(controller: _listScrollController);
if (widget.dataType == VideoEpidoesType.videoEpisode) {
_listObserverController.initialIndexModel = ObserverIndexPositionModel(
index: currentIndex,
isFixedHeight: true,
);
}
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance.addPostFrameCallback((_) {
if (widget.dataType == VideoEpidoesType.videoEpisode) { if (widget.dataType != VideoEpidoesType.videoEpisode) {
_itemScrollController.jumpTo(index: currentIndex);
} else {
double itemHeight = (widget.isFullScreen double itemHeight = (widget.isFullScreen
? 400 ? 400
: Get.size.width - 3 * StyleString.safeSpace) / : Get.size.width - 3 * StyleString.safeSpace) /
@ -118,6 +126,12 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
return '选集'; return '选集';
} }
@override
void dispose() {
_listObserverController.controller?.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return StatefulBuilder( return StatefulBuilder(
@ -136,27 +150,30 @@ class _PagesBottomSheetState extends State<PagesBottomSheet> {
Expanded( Expanded(
child: Material( child: Material(
child: widget.dataType == VideoEpidoesType.videoEpisode child: widget.dataType == VideoEpidoesType.videoEpisode
? ScrollablePositionedList.builder( ? ListViewObserver(
itemScrollController: _itemScrollController, controller: _listObserverController,
itemCount: widget.episodes.length + 1, child: ListView.builder(
itemBuilder: (BuildContext context, int index) { controller: _listScrollController,
bool isLastItem = index == widget.episodes.length; itemCount: widget.episodes.length + 1,
bool isCurrentIndex = currentIndex == index; itemBuilder: (BuildContext context, int index) {
return isLastItem bool isLastItem = index == widget.episodes.length;
? SizedBox( bool isCurrentIndex = currentIndex == index;
height: return isLastItem
MediaQuery.of(context).padding.bottom + ? SizedBox(
20, height:
) MediaQuery.of(context).padding.bottom +
: EpisodeListItem( 20,
episode: widget.episodes[index], )
index: index, : EpisodeListItem(
isCurrentIndex: isCurrentIndex, episode: widget.episodes[index],
dataType: widget.dataType, index: index,
changeFucCall: widget.changeFucCall, isCurrentIndex: isCurrentIndex,
isFullScreen: widget.isFullScreen, dataType: widget.dataType,
); changeFucCall: widget.changeFucCall,
}, isFullScreen: widget.isFullScreen,
);
},
),
) )
: Padding( : Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(

View File

@ -359,8 +359,8 @@ class VideoHttp {
'plat': 1, 'plat': 1,
'oid': oid, 'oid': oid,
'type': type.index, 'type': type.index,
// 'root': root == null || root == 0 ? '' : root, 'root': root == null || root == 0 ? '' : root,
// 'parent': parent == null || parent == 0 ? '' : parent, 'parent': parent == null || parent == 0 ? '' : parent,
'message': message, 'message': message,
'at_name_to_mid': {}, 'at_name_to_mid': {},
if (pictures != null) 'pictures': jsonEncode(pictures), if (pictures != null) 'pictures': jsonEncode(pictures),
@ -379,8 +379,8 @@ class VideoHttp {
}, },
data: formData, data: formData,
); );
log(res.toString());
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
log(res.toString());
return {'status': true, 'data': res.data['data']}; return {'status': true, 'data': res.data['data']};
} else { } else {
return {'status': false, 'data': [], 'msg': res.data['message']}; return {'status': false, 'data': [], 'msg': res.data['message']};

View File

@ -7,7 +7,6 @@ import 'package:hive/hive.dart';
import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/bangumi/info.dart';
import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/index.dart';
import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/storage.dart';
import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
import '../../../common/pages_bottom_sheet.dart'; import '../../../common/pages_bottom_sheet.dart';
import '../../../models/common/video_episode_type.dart'; import '../../../models/common/video_episode_type.dart';
import '../introduction/controller.dart'; import '../introduction/controller.dart';
@ -44,7 +43,6 @@ class _BangumiPanelState extends State<BangumiPanel> {
late int cid; late int cid;
String heroTag = Get.arguments['heroTag']; String heroTag = Get.arguments['heroTag'];
late final VideoDetailController videoDetailCtr; late final VideoDetailController videoDetailCtr;
final ItemScrollController itemScrollController = ItemScrollController();
late PersistentBottomSheetController? _bottomSheetController; late PersistentBottomSheetController? _bottomSheetController;
@override @override

View File

@ -26,6 +26,7 @@ class DynamicDetailController extends GetxController {
RxString sortTypeLabel = ReplySortType.time.labels.obs; RxString sortTypeLabel = ReplySortType.time.labels.obs;
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
RxInt replyReqCode = 200.obs; RxInt replyReqCode = 200.obs;
bool isEnd = false;
@override @override
void onInit() { void onInit() {
@ -48,7 +49,7 @@ class DynamicDetailController extends GetxController {
} }
Future queryReplyList({reqType = 'init'}) async { Future queryReplyList({reqType = 'init'}) async {
if (isLoadingMore) { if (isLoadingMore || noMore.value == '没有更多了' || isEnd) {
return; return;
} }
isLoadingMore = true; isLoadingMore = true;
@ -56,10 +57,6 @@ class DynamicDetailController extends GetxController {
nextOffset = ''; nextOffset = '';
noMore.value = ''; noMore.value = '';
} }
if (noMore.value == '没有更多了') {
isLoadingMore = false;
return;
}
var res = await ReplyHttp.replyList( var res = await ReplyHttp.replyList(
oid: oid!, oid: oid!,
nextOffset: nextOffset, nextOffset: nextOffset,
@ -68,15 +65,14 @@ class DynamicDetailController extends GetxController {
); );
if (res['status']) { if (res['status']) {
List<ReplyItemModel> replies = res['data'].replies; List<ReplyItemModel> replies = res['data'].replies;
isEnd = res['data'].cursor.isEnd ?? false;
acount.value = res['data'].cursor.allCount; acount.value = res['data'].cursor.allCount;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) { if (replies.isNotEmpty) {
noMore.value = '加载中...'; noMore.value = isEnd ? '没有更多了' : '加载中...';
if (res['data'].cursor.isEnd == true) {
noMore.value = '没有更多了';
}
} else { } else {
noMore.value = nextOffset == "" ? '还没有评论' : '没有更多了'; noMore.value =
replyList.isEmpty && nextOffset == "" ? '还没有评论' : '没有更多了';
} }
if (reqType == 'init') { if (reqType == 'init') {
// 添加置顶回复 // 添加置顶回复
@ -122,4 +118,9 @@ class DynamicDetailController extends GetxController {
var res = await HtmlHttp.reqHtml(id, 'opus'); var res = await HtmlHttp.reqHtml(id, 'opus');
oid = res['commentId']; oid = res['commentId'];
} }
// 上拉加载
Future onLoad() async {
queryReplyList(reqType: 'onLoad');
}
} }

View File

@ -140,7 +140,7 @@ class _DynamicDetailPageState extends State<DynamicDetailPage>
if (scrollController.position.pixels >= if (scrollController.position.pixels >=
scrollController.position.maxScrollExtent - 300) { scrollController.position.maxScrollExtent - 300) {
EasyThrottle.throttle('replylist', const Duration(seconds: 2), () { EasyThrottle.throttle('replylist', const Duration(seconds: 2), () {
_dynamicDetailController.queryReplyList(reqType: 'onLoad'); _dynamicDetailController.onLoad();
}); });
} }

View File

@ -34,6 +34,7 @@ class VideoReplyController extends GetxController {
Box setting = GStrorage.setting; Box setting = GStrorage.setting;
RxInt replyReqCode = 200.obs; RxInt replyReqCode = 200.obs;
bool isEnd = false;
@override @override
void onInit() { void onInit() {
@ -49,8 +50,8 @@ class VideoReplyController extends GetxController {
sortTypeLabel.value = _sortType.labels; sortTypeLabel.value = _sortType.labels;
} }
Future queryReplyList({type = 'init'}) async { Future<dynamic> queryReplyList({type = 'init'}) async {
if (isLoadingMore) { if (isLoadingMore || noMore.value == '没有更多了' || isEnd) {
return; return;
} }
isLoadingMore = true; isLoadingMore = true;
@ -58,10 +59,6 @@ class VideoReplyController extends GetxController {
nextOffset = ''; nextOffset = '';
noMore.value = ''; noMore.value = '';
} }
if (noMore.value == '没有更多了') {
isLoadingMore = false;
return;
}
final res = await ReplyHttp.replyList( final res = await ReplyHttp.replyList(
oid: aid!, oid: aid!,
nextOffset: nextOffset, nextOffset: nextOffset,
@ -70,14 +67,13 @@ class VideoReplyController extends GetxController {
); );
if (res['status']) { if (res['status']) {
final List<ReplyItemModel> replies = res['data'].replies; final List<ReplyItemModel> replies = res['data'].replies;
isEnd = res['data'].cursor.isEnd ?? false;
nextOffset = res['data'].cursor.paginationReply.nextOffset ?? ""; nextOffset = res['data'].cursor.paginationReply.nextOffset ?? "";
if (replies.isNotEmpty) { if (replies.isNotEmpty) {
noMore.value = '加载中...'; noMore.value = isEnd ? '没有更多了' : '加载中...';
if (res['data'].cursor.isEnd == true) {
noMore.value = '没有更多了';
}
} else { } else {
noMore.value = nextOffset == "" ? '还没有评论' : '没有更多了'; noMore.value =
replyList.isEmpty && nextOffset == "" ? '还没有评论' : '没有更多了';
} }
if (type == 'init') { if (type == 'init') {
// 添加置顶回复 // 添加置顶回复
@ -105,6 +101,14 @@ class VideoReplyController extends GetxController {
queryReplyList(type: 'onLoad'); queryReplyList(type: 'onLoad');
} }
// 下拉刷新
Future onRefresh() async {
nextOffset = "";
noMore.value = '';
isEnd = false;
queryReplyList(type: 'init');
}
// 排序搜索评论 // 排序搜索评论
queryBySort() { queryBySort() {
EasyThrottle.throttle('queryBySort', const Duration(seconds: 1), () { EasyThrottle.throttle('queryBySort', const Duration(seconds: 1), () {
@ -118,6 +122,8 @@ class VideoReplyController extends GetxController {
break; break;
default: default:
} }
isLoadingMore = false;
isEnd = false;
sortTypeTitle.value = _sortType.titles; sortTypeTitle.value = _sortType.titles;
sortTypeLabel.value = _sortType.labels; sortTypeLabel.value = _sortType.labels;
nextOffset = ""; nextOffset = "";

View File

@ -137,7 +137,7 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
super.build(context); super.build(context);
return RefreshIndicator( return RefreshIndicator(
onRefresh: () async { onRefresh: () async {
return await _videoReplyController.queryReplyList(type: 'init'); return await _videoReplyController.onRefresh();
}, },
child: Stack( child: Stack(
children: [ children: [
@ -312,13 +312,13 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
); );
}, },
).then( ).then(
(value) => { (value) {
// 完成评论,数据添加 // 完成评论,数据添加
if (value != null && value['data'] != null) if (value != null && value['data'] != null) {
{ _videoReplyController.replyList
_videoReplyController.replyList .add(value['data']);
.add(value['data']) _videoReplyController.replyList.refresh();
} }
}, },
); );
}, },

View File

@ -16,7 +16,7 @@ class VideoReplyReplyController extends GetxController {
ReplyItemModel? rootReply; ReplyItemModel? rootReply;
RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs; RxList<ReplyItemModel> replyList = <ReplyItemModel>[].obs;
// 当前页 // 当前页
int currentPage = 0; int currentPage = 1;
bool isLoadingMore = false; bool isLoadingMore = false;
RxString noMore = ''.obs; RxString noMore = ''.obs;
// 当前回复的回复 // 当前回复的回复
@ -25,12 +25,12 @@ class VideoReplyReplyController extends GetxController {
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
currentPage = 0; currentPage = 1;
} }
Future queryReplyList({type = 'init', currentReply}) async { Future queryReplyList({type = 'init', currentReply}) async {
if (type == 'init') { if (type == 'init') {
currentPage = 0; currentPage = 1;
} }
if (isLoadingMore) { if (isLoadingMore) {
return; return;
@ -39,7 +39,7 @@ class VideoReplyReplyController extends GetxController {
final res = await ReplyHttp.replyReplyList( final res = await ReplyHttp.replyReplyList(
oid: aid!, oid: aid!,
root: rpid!, root: rpid!,
pageNum: currentPage + 1, pageNum: currentPage,
type: (replyType ?? ReplyType.video).index, type: (replyType ?? ReplyType.video).index,
); );
if (res['status']) { if (res['status']) {
@ -50,12 +50,12 @@ class VideoReplyReplyController extends GetxController {
if (replies.length == res['data'].page.count) { if (replies.length == res['data'].page.count) {
noMore.value = '没有更多了'; noMore.value = '没有更多了';
} }
currentPage++; // currentPage++;
} else { } else {
// 未登录状态replies可能返回null // 未登录状态replies可能返回null
noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; noMore.value = currentPage == 1 ? '还没有评论' : '没有更多了';
} }
if (type == 'init') { if (type == 'init' && currentPage == 1) {
replyList.value = replies; replyList.value = replies;
} else { } else {
// 每次回复之后,翻页请求有且只有相同的一条回复数据 // 每次回复之后,翻页请求有且只有相同的一条回复数据
@ -79,6 +79,7 @@ class VideoReplyReplyController extends GetxController {
replyList.insert(0, currentReply); replyList.insert(0, currentReply);
} }
} }
currentPage += 1;
isLoadingMore = false; isLoadingMore = false;
return res; return res;
} }

View File

@ -183,8 +183,7 @@ class _VideoReplyReplyPanelState extends State<VideoReplyReplyPanel> {
Expanded( Expanded(
child: RefreshIndicator( child: RefreshIndicator(
onRefresh: () async { onRefresh: () async {
setState(() {}); _videoReplyReplyController.currentPage = 1;
_videoReplyReplyController.currentPage = 0;
return await _videoReplyReplyController.queryReplyList( return await _videoReplyReplyController.queryReplyList(
currentReply: widget.currentReply, currentReply: widget.currentReply,
); );

View File

@ -212,9 +212,9 @@ class PiliSchame {
} }
} }
static Future<void> biliScheme(Uri value) async { static Future<void> biliScheme(SchemeEntity value) async {
final String host = value.host; final String host = value.host!;
final String path = value.path; final String path = value.path!;
switch (host) { switch (host) {
case 'root': case 'root':
Navigator.popUntil( Navigator.popUntil(

View File

@ -1434,14 +1434,14 @@ packages:
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.1.3" version: "0.1.3"
scrollable_positioned_list: scrollview_observer:
dependency: "direct main" dependency: "direct main"
description: description:
name: scrollable_positioned_list name: scrollview_observer
sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" sha256: fa408bcfd41e19da841eb53fc471f8f952d5ef818b854d2505c4bb3f0c876381
url: "https://pub.flutter-io.cn" url: "https://pub.flutter-io.cn"
source: hosted source: hosted
version: "0.3.8" version: "1.22.0"
sentry: sentry:
dependency: transitive dependency: transitive
description: description:

View File

@ -134,7 +134,7 @@ dependencies:
# 极验 # 极验
gt3_flutter_plugin: ^0.1.0 gt3_flutter_plugin: ^0.1.0
uuid: ^3.0.7 uuid: ^3.0.7
scrollable_positioned_list: ^0.3.8 scrollview_observer: ^1.22.0
catcher_2: ^1.2.6 catcher_2: ^1.2.6
logger: ^2.3.0 logger: ^2.3.0
path: ^1.9.0 path: ^1.9.0