refa: member fav

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-12-03 12:52:23 +08:00
parent 9c8e5b53e7
commit b7a277a57c
3 changed files with 255 additions and 137 deletions

View File

@@ -32,6 +32,7 @@ enum CDNService {
const CDNService(this.desc, [this.host]); const CDNService(this.desc, [this.host]);
} }
// from https://rec.danmuji.org/dev/cdn-info/ // from https://rec.danmuji.org/dev/cdn-info/
// { // {
// 'cn-ahwh-ct-': {'01': 16}, // 'cn-ahwh-ct-': {'01': 16},

View File

@@ -11,21 +11,37 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
class MemberFavoriteCtr class MemberFavoriteCtr
extends CommonDataController<List<SpaceFavData>?, dynamic> { extends CommonDataController<List<SpaceFavData>?, List<SpaceFavData>?> {
MemberFavoriteCtr({ MemberFavoriteCtr({
required this.mid, required this.mid,
}); });
final int mid; final int mid;
Rx<SpaceFavData> first = SpaceFavData().obs; late int favPage = 2;
Rx<SpaceFavData> second = SpaceFavData().obs; bool _favExpand = true;
final RxBool favEnd = true.obs;
final Rx<SpaceFavData> favState = SpaceFavData().obs;
RxBool firstEnd = true.obs; late int subPage = 2;
RxBool secondEnd = true.obs; bool _subExpand = true;
final RxBool subEnd = true.obs;
final Rx<SpaceFavData> subState = SpaceFavData().obs;
late int page = 2; bool isExpand(bool isFav) {
late int page1 = 2; return isFav ? _favExpand : _subExpand;
}
void setExpand(bool isFav) {
if (isFav) {
flag = _favExpand;
_favExpand = !_favExpand;
} else {
_subExpand = !_subExpand;
}
}
bool flag = false;
@override @override
void onInit() { void onInit() {
@@ -35,8 +51,8 @@ class MemberFavoriteCtr
@override @override
Future<void> onRefresh() { Future<void> onRefresh() {
page = 2; favPage = 2;
page1 = 2; subPage = 2;
return super.onRefresh(); return super.onRefresh();
} }
@@ -47,13 +63,13 @@ class MemberFavoriteCtr
) { ) {
try { try {
List<SpaceFavData> res = response.response!; List<SpaceFavData> res = response.response!;
first.value = res.first; favState.value = res.first;
second.value = res[1]; subState.value = res[1];
firstEnd.value = favEnd.value =
(res.first.mediaListResponse?.count ?? -1) <= (res.first.mediaListResponse?.count ?? -1) <=
(res.first.mediaListResponse?.list?.length ?? -1); (res.first.mediaListResponse?.list?.length ?? -1);
secondEnd.value = subEnd.value =
(res[1].mediaListResponse?.count ?? -1) <= (res[1].mediaListResponse?.count ?? -1) <=
(res[1].mediaListResponse?.list?.length ?? -1); (res[1].mediaListResponse?.list?.length ?? -1);
} catch (e) { } catch (e) {
@@ -63,62 +79,76 @@ class MemberFavoriteCtr
return true; return true;
} }
Future<void> userfavFolder() async { Future<void> userFavFolder() async {
var res = await Request().get( try {
final res = await Request().get(
Api.userFavFolder, Api.userFavFolder,
queryParameters: { queryParameters: {
'pn': page, 'pn': favPage,
'ps': 20, 'ps': 20,
'up_mid': mid, 'up_mid': mid,
}, },
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
page++; favPage++;
firstEnd.value = res.data['data']['has_more'] == false; final data = res.data['data'];
if (res.data['data'] != null) { if (data != null) {
List<SpaceFavItemModel> list = favEnd.value = data['has_more'] == false;
(res.data['data']?['list'] as List<dynamic>?) final list = (data['list'] as List<dynamic>?)
?.map((item) => SpaceFavItemModel.fromJson(item)) ?.map((item) => SpaceFavItemModel.fromJson(item))
.toList() ?? .toList();
<SpaceFavItemModel>[]; if (list != null && list.isNotEmpty) {
first favState
..value.mediaListResponse?.list?.addAll(list) ..value.mediaListResponse!.list!.addAll(list)
..refresh(); ..refresh();
} else { } else {
firstEnd.value = true; favEnd.value = true;
} }
} else { } else {
SmartDialog.showToast(res.data['message'] ?? '账号未登录'); favEnd.value = true;
}
} else {
SmartDialog.showToast(res.data['message']);
}
} catch (e) {
SmartDialog.showToast(e.toString());
} }
} }
Future<void> userSubFolder() async { Future<void> userSubFolder() async {
var res = await Request().get( try {
final res = await Request().get(
Api.userSubFolder, Api.userSubFolder,
queryParameters: { queryParameters: {
'up_mid': mid, 'up_mid': mid,
'ps': 20, 'ps': 20,
'pn': page1, 'pn': subPage,
'platform': 'web', 'platform': 'web',
}, },
); );
if (res.data['code'] == 0) { if (res.data['code'] == 0) {
page++; subPage++;
secondEnd.value = res.data['data']['has_more'] == false; final data = res.data['data'];
if (res.data['data'] != null) { if (data != null) {
List<SpaceFavItemModel> list = subEnd.value = data['has_more'] == false;
(res.data['data']?['list'] as List<dynamic>?) final list = (data['list'] as List<dynamic>?)
?.map((item) => SpaceFavItemModel.fromJson(item)) ?.map((item) => SpaceFavItemModel.fromJson(item))
.toList() ?? .toList();
<SpaceFavItemModel>[]; if (list != null && list.isNotEmpty) {
second subState
..value.mediaListResponse?.list?.addAll(list) ..value.mediaListResponse!.list!.addAll(list)
..refresh(); ..refresh();
} else { } else {
secondEnd.value = true; subEnd.value = true;
} }
} else { } else {
SmartDialog.showToast(res.data['message'] ?? '账号未登录'); subEnd.value = true;
}
} else {
SmartDialog.showToast(res.data['message']);
}
} catch (e) {
SmartDialog.showToast(e.toString());
} }
} }

View File

@@ -1,4 +1,5 @@
import 'package:PiliPlus/common/skeleton/video_card_h.dart'; import 'package:PiliPlus/common/skeleton/video_card_h.dart';
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart'; import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/http/loading_state.dart';
@@ -40,7 +41,7 @@ class _MemberFavoriteState extends State<MemberFavorite>
return refreshIndicator( return refreshIndicator(
onRefresh: _controller.onRefresh, onRefresh: _controller.onRefresh,
child: CustomScrollView( child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(), physics: _FavScrollPhysics(controller: _controller),
slivers: [ slivers: [
SliverPadding( SliverPadding(
padding: EdgeInsets.only( padding: EdgeInsets.only(
@@ -55,7 +56,10 @@ class _MemberFavoriteState extends State<MemberFavorite>
); );
} }
Widget _buildBody(ThemeData theme, LoadingState loadingState) { Widget _buildBody(
ThemeData theme,
LoadingState<List<SpaceFavData>?> loadingState,
) {
return switch (loadingState) { return switch (loadingState) {
Loading() => SliverPadding( Loading() => SliverPadding(
padding: const EdgeInsets.only(top: 7), padding: const EdgeInsets.only(top: 7),
@@ -65,19 +69,21 @@ class _MemberFavoriteState extends State<MemberFavorite>
itemCount: 10, itemCount: 10,
), ),
), ),
Success(:var response) => Success(:final response) =>
(response as List?)?.isNotEmpty == true response?.isNotEmpty == true
? SliverMainAxisGroup( ? SliverMainAxisGroup(
slivers: [ slivers: [
SliverToBoxAdapter( _buildItem(
child: Obx( theme,
() => _buildItem(theme, _controller.first.value, true), data: _controller.favState,
), isEnd: _controller.favEnd,
), isFav: true,
SliverToBoxAdapter(
child: Obx(
() => _buildItem(theme, _controller.second.value, false),
), ),
_buildItem(
theme,
data: _controller.subState,
isEnd: _controller.subEnd,
isFav: false,
), ),
], ],
) )
@@ -89,23 +95,49 @@ class _MemberFavoriteState extends State<MemberFavorite>
}; };
} }
Theme _buildItem(ThemeData theme, SpaceFavData data, bool isFirst) { Widget _buildItem(
return Theme( ThemeData theme, {
data: theme.copyWith( required Rx<SpaceFavData> data,
dividerColor: Colors.transparent, required RxBool isEnd,
), required bool isFav,
child: ExpansionTile( }) {
dense: true, return SliverMainAxisGroup(
initiallyExpanded: true, slivers: [
title: Text.rich( SliverPersistentHeader(
pinned: true,
delegate: CustomSliverPersistentHeaderDelegate(
child: Material(
color: theme.colorScheme.surface,
child: Builder(
builder: (context) {
return InkWell(
onTap: () {
_controller.setExpand(isFav);
(context as Element).markNeedsBuild();
data.refresh();
},
child: Container(
height: 45,
alignment: .centerLeft,
padding: const .only(left: 12),
child: Text.rich(
TextSpan( TextSpan(
children: [ children: [
WidgetSpan(
alignment: .middle,
child: Icon(
_controller.isExpand(isFav)
? Icons.expand_less
: Icons.expand_more,
color: theme.colorScheme.outline,
),
),
TextSpan( TextSpan(
text: data.name, text: ' ${data.value.name}',
style: const TextStyle(fontSize: 14), style: const TextStyle(fontSize: 14),
), ),
TextSpan( TextSpan(
text: ' ${data.mediaListResponse?.count}', text: ' ${data.value.mediaListResponse?.count}',
style: TextStyle( style: TextStyle(
fontSize: 13, fontSize: 13,
color: theme.colorScheme.outline, color: theme.colorScheme.outline,
@@ -114,53 +146,108 @@ class _MemberFavoriteState extends State<MemberFavorite>
], ],
), ),
), ),
controlAffinity: ListTileControlAffinity.leading, ),
children: [ );
...?data.mediaListResponse?.list?.map( },
(item) => SizedBox( ),
),
bgColor: null,
),
),
Obx(() {
final list = data.value.mediaListResponse?.list;
if (!_controller.isExpand(isFav)) {
return const SliverToBoxAdapter();
}
final end = isEnd.value;
if (list != null && list.isNotEmpty) {
return SliverList.builder(
itemCount: list.length + (end ? 0 : 1),
itemBuilder: (context, index) {
if (!end && index == list.length) {
return Obx(
() => isEnd.value
? const SizedBox.shrink()
: _buildLoadMoreItem(theme, isFav),
);
}
final item = list[index];
return SizedBox(
height: 98, height: 98,
child: MemberFavItem( child: MemberFavItem(
item: item, item: item,
callback: (res) { callback: (res) {
if (res == true) { if (res == true) {
_controller _controller.favState
..first.value.mediaListResponse?.list?.remove(item) ..value.mediaListResponse?.list?.remove(item)
..first.refresh(); ..refresh();
} }
}, },
), ),
), );
), },
Obx( );
() => }
(isFirst return const SliverToBoxAdapter();
? _controller.firstEnd.value }),
: _controller.secondEnd.value)
? const SizedBox.shrink()
: _buildLoadMoreItem(theme, isFirst),
),
], ],
),
); );
} }
ListTile _buildLoadMoreItem(ThemeData theme, bool isFirst) { Widget _buildLoadMoreItem(ThemeData theme, bool isFav) {
return ListTile( return Padding(
dense: true, padding: const .only(top: 7),
child: InkWell(
onTap: () { onTap: () {
if (isFirst) { if (isFav) {
_controller.userfavFolder(); _controller.userFavFolder();
} else { } else {
_controller.userSubFolder(); _controller.userSubFolder();
} }
}, },
title: Text( child: Container(
height: 40,
alignment: .center,
child: Text(
'查看更多内容', '查看更多内容',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle( style: TextStyle(color: theme.colorScheme.primary),
color: theme.colorScheme.primary, ),
), ),
), ),
); );
} }
} }
class _FavScrollPhysics extends AlwaysScrollableScrollPhysics {
const _FavScrollPhysics({super.parent, required this.controller});
final MemberFavoriteCtr controller;
@override
_FavScrollPhysics applyTo(ScrollPhysics? ancestor) {
return _FavScrollPhysics(
parent: buildParent(ancestor),
controller: controller,
);
}
@override
double adjustPositionForNewDimensions({
required ScrollMetrics oldPosition,
required ScrollMetrics newPosition,
required bool isScrolling,
required double velocity,
}) {
if (controller.flag) {
controller.flag = false;
return 0;
}
return super.adjustPositionForNewDimensions(
oldPosition: oldPosition,
newPosition: newPosition,
isScrolling: isScrolling,
velocity: velocity,
);
}
}