feat: show total season/series

Closes #164

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-01-14 15:22:56 +08:00
parent ba8d7b871c
commit 5983670c83
10 changed files with 303 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/http/member.dart';
import 'package:PiliPlus/pages/common/common_controller.dart';
class SeasonSeriesController extends CommonController {
SeasonSeriesController(this.mid);
final int mid;
@override
void onInit() {
super.onInit();
queryData();
}
@override
bool customHandleResponse(Success response) {
Map data = response.response;
List list = ((data['seasons_list'] as List?) ?? []) +
((data['series_list'] as List?) ?? []);
if (currentPage != 0 && loadingState.value is Success) {
list.insertAll(0, (loadingState.value as Success).response);
}
isEnd = list.length >= ((data['page']['total'] as int?) ?? 0);
loadingState.value = LoadingState.success(list);
return true;
}
@override
Future<LoadingState> customGetData() => MemberHttp.seasonSeriesList(
mid: mid,
pn: currentPage,
);
}

View File

@@ -0,0 +1,107 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/loading_widget.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/controller.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/widget/season_series_card.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SeasonSeriesPage extends StatefulWidget {
const SeasonSeriesPage({
super.key,
required this.mid,
this.heroTag,
});
final int mid;
final String? heroTag;
@override
State<SeasonSeriesPage> createState() => _SeasonSeriesPageState();
}
class _SeasonSeriesPageState extends State<SeasonSeriesPage> {
late final _controller = Get.put(
SeasonSeriesController(widget.mid),
tag: widget.heroTag,
);
@override
Widget build(BuildContext context) {
return Obx(() => _buildBody(_controller.loadingState.value));
}
Widget _buildBody(LoadingState loadingState) {
return switch (loadingState) {
Loading() => loadingWidget,
Success() => (loadingState.response as List?)?.isNotEmpty == true
? CustomScrollView(
slivers: [
SliverPadding(
padding: EdgeInsets.only(
top: StyleString.safeSpace - 5,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: SliverGrid(
gridDelegate: SliverGridDelegateWithExtentAndRatio(
mainAxisSpacing: 2,
maxCrossAxisExtent: Grid.mediumCardWidth * 2,
childAspectRatio: StyleString.aspectRatio * 2.2,
),
delegate: SliverChildBuilderDelegate(
(context, index) {
if (index == loadingState.response.length - 1) {
_controller.onLoadMore();
}
return SeasonSeriesCard(
item: loadingState.response[index],
onTap: () {
dynamic item = loadingState.response[index];
bool isSeason = item['meta']['season_id'] != null;
dynamic id = isSeason
? item['meta']['season_id']
: item['meta']['series_id'];
Get.to(
Scaffold(
appBar: AppBar(
title: Text(item['meta']['name']),
),
body: MemberVideo(
type: isSeason
? ContributeType.season
: ContributeType.series,
heroTag: widget.heroTag,
mid: widget.mid,
seasonId: isSeason ? id : null,
seriesId: isSeason ? null : id,
title: item['meta']['name'],
),
),
);
},
);
},
childCount: loadingState.response.length,
),
),
),
],
)
: scrollErrorWidget(
callback: () {
_controller.onReload();
},
),
Error() => scrollErrorWidget(
errMsg: loadingState.errMsg,
callback: () {
_controller.onReload();
},
),
LoadingState() => throw UnimplementedError(),
};
}
}

View File

@@ -0,0 +1,106 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/common/widgets/badge.dart';
import 'package:PiliPlus/common/widgets/image_save.dart';
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute.dart';
import 'package:PiliPlus/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SeasonSeriesCard extends StatelessWidget {
const SeasonSeriesCard({
super.key,
required this.item,
required this.onTap,
});
final dynamic item;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return InkWell(
onLongPress: () {
imageSaveDialog(
context: context,
title: item['meta']['name'],
cover: item['meta']['cover'],
);
},
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: StyleString.safeSpace,
vertical: 5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: StyleString.aspectRatio,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints boxConstraints) {
final double maxWidth = boxConstraints.maxWidth;
final double maxHeight = boxConstraints.maxHeight;
return Stack(
children: [
NetworkImgLayer(
src: item['meta']['cover'],
width: maxWidth,
height: maxHeight,
),
PBadge(
text:
'${item['meta']['season_id'] != null ? '合集' : '列表'}: ${item['meta']['total']}',
bottom: 6.0,
right: 6.0,
),
],
);
},
),
),
videoContent(context)
],
),
),
);
}
Widget videoContent(context) {
return Expanded(
child: Padding(
padding: const EdgeInsets.fromLTRB(10, 0, 6, 0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
item['meta']['name'],
textAlign: TextAlign.start,
style: TextStyle(
fontSize: Theme.of(context).textTheme.bodyMedium!.fontSize,
height: 1.42,
letterSpacing: 0.3,
),
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
const Spacer(),
Text(
Utils.dateFormat(item['meta']['ptime']),
maxLines: 1,
style: TextStyle(
fontSize: Theme.of(context).textTheme.labelSmall!.fontSize,
height: 1,
color: Theme.of(context).colorScheme.outline,
overflow: TextOverflow.clip,
),
),
const Spacer(),
],
),
),
);
}
}

View File

@@ -1,5 +1,6 @@
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/article/member_article.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/audio/member_audio.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/season_series/season_series_page.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/content/video/member_video.dart';
import 'package:PiliPlus/pages/member/new/content/member_contribute/member_contribute_ctr.dart';
import 'package:flutter/material.dart';
@@ -110,6 +111,10 @@ class _MemberContributeState extends State<MemberContribute>
seriesId: item.seriesId,
title: item.title,
),
'ugcSeason' => SeasonSeriesPage(
mid: widget.mid,
heroTag: widget.heroTag,
),
_ => Center(child: Text(item.title!))
},
)

View File

@@ -32,6 +32,18 @@ class MemberContributeCtr extends CommonController
if (contribute.items?.isNullOrEmpty == false &&
contribute.items!.length > 1) {
items = contribute.items;
if (_ctr.ugcSeasonCount != null) {
int currentSeasonCount =
items!.where((item) => item.param == 'season_video').length;
if (currentSeasonCount < _ctr.ugcSeasonCount!) {
items!.add(
Item(
param: 'ugcSeason',
title: '全部合集/列表',
),
);
}
}
tabs = items!.map((item) => Tab(text: item.title)).toList();
tabController = TabController(
vsync: this,

View File

@@ -31,6 +31,7 @@ class MemberControllerNew extends CommonController
List<Tab2>? tab2;
RxInt contributeInitialIndex = 0.obs;
double? top;
int? ugcSeasonCount;
@override
void onInit() {
@@ -54,6 +55,7 @@ class MemberControllerNew extends CommonController
tab2 = response.response.tab2;
live = response.response?.live;
silence = response.response?.card?.silence;
ugcSeasonCount = response.response?.ugcSeason?.count;
if (response.response?.card?.endTime != null) {
if (response.response.card.endTime == 0) {
endTime = ': 永久封禁';