mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
@@ -1,81 +0,0 @@
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/member_article_ctr.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/widget/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberArticle extends StatefulWidget {
|
||||
const MemberArticle({
|
||||
super.key,
|
||||
required this.heroTag,
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final String? heroTag;
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
State<MemberArticle> createState() => _MemberArticleState();
|
||||
}
|
||||
|
||||
class _MemberArticleState extends State<MemberArticle>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(
|
||||
MemberArticleCtr(mid: widget.mid),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return MemberArticleItem(
|
||||
item: loadingState.response![index],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/models/space_article/data.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class MemberArticleCtr extends CommonListController<Data, Item> {
|
||||
MemberArticleCtr({
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final int mid;
|
||||
|
||||
int count = -1;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
List<Item>? getDataList(Data response) {
|
||||
return response.item;
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (length >= count) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
count = response.response.count ?? -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() =>
|
||||
MemberHttp.spaceArticle(mid: mid, page: currentPage);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/common/widgets/stat/stat.dart';
|
||||
import 'package:PiliPlus/models/space_article/item.dart';
|
||||
import 'package:PiliPlus/utils/app_scheme.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MemberArticleItem extends StatelessWidget {
|
||||
const MemberArticleItem({super.key, required this.item});
|
||||
|
||||
final Item item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final outline = theme.colorScheme.outline;
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (item.uri?.isNotEmpty == true) {
|
||||
PiliScheme.routePushFromUrl(item.uri!);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
imageSaveDialog(
|
||||
title: item.title,
|
||||
cover: item.originImageUrls?.firstOrNull,
|
||||
);
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (item.originImageUrls?.firstOrNull?.isNotEmpty == true) ...[
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder:
|
||||
(BuildContext context, BoxConstraints boxConstraints) {
|
||||
return NetworkImgLayer(
|
||||
src: item.originImageUrls!.first,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (item.title?.isNotEmpty == true) ...[
|
||||
Expanded(
|
||||
child: Text(
|
||||
item.title!,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: theme.textTheme.bodyMedium!.fontSize,
|
||||
height: 1.42,
|
||||
letterSpacing: 0.3,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
Text(
|
||||
'${item.publishTimeText}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
height: 1,
|
||||
color: outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Row(
|
||||
children: [
|
||||
StatView(
|
||||
context: context,
|
||||
value: item.stats?.view ?? 0,
|
||||
goto: 'picture',
|
||||
textColor: outline,
|
||||
),
|
||||
const SizedBox(width: 16),
|
||||
StatView(
|
||||
context: context,
|
||||
goto: 'reply',
|
||||
value: item.stats?.reply ?? 0,
|
||||
textColor: outline,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MemberAudio extends StatefulWidget {
|
||||
const MemberAudio({
|
||||
super.key,
|
||||
required this.heroTag,
|
||||
});
|
||||
|
||||
final String? heroTag;
|
||||
|
||||
@override
|
||||
State<MemberAudio> createState() => _MemberAudioState();
|
||||
}
|
||||
|
||||
class _MemberAudioState extends State<MemberAudio>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Center(
|
||||
child: Text('Audio'),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/bangumi/member_bangumi_ctr.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberBangumi extends StatefulWidget {
|
||||
const MemberBangumi({
|
||||
super.key,
|
||||
required this.heroTag,
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final String? heroTag;
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
State<MemberBangumi> createState() => _MemberBangumiState();
|
||||
}
|
||||
|
||||
class _MemberBangumiState extends State<MemberBangumi>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(
|
||||
MemberBangumiCtr(
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
left: StyleString.safeSpace,
|
||||
right: StyleString.safeSpace,
|
||||
top: StyleString.safeSpace,
|
||||
bottom: StyleString.safeSpace +
|
||||
MediaQuery.of(context).padding.bottom +
|
||||
80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth / 3 * 2,
|
||||
childAspectRatio: 0.75,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(52),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return BangumiCardVMemberHome(
|
||||
bangumiItem: loadingState.response![index],
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart' as space;
|
||||
|
||||
class MemberBangumiCtr extends CommonListController<Data, Item> {
|
||||
MemberBangumiCtr({
|
||||
required this.mid,
|
||||
required this.heroTag,
|
||||
});
|
||||
|
||||
final int mid;
|
||||
final String? heroTag;
|
||||
int? count;
|
||||
late final _ctr = Get.find<MemberControllerNew>(tag: heroTag);
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
dynamic response = (_ctr.loadingState.value as Success).response;
|
||||
if (response is space.Data) {
|
||||
currentPage = 2;
|
||||
dynamic res = response.season;
|
||||
loadingState.value = LoadingState.success(res.item);
|
||||
count = res.count;
|
||||
isEnd = res.item!.length >= count;
|
||||
} else {
|
||||
queryData();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
List<Item>? getDataList(Data response) {
|
||||
return response.item;
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (count != null && length >= count!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.spaceArchive(
|
||||
type: ContributeType.bangumi,
|
||||
mid: mid,
|
||||
pn: currentPage,
|
||||
);
|
||||
}
|
||||
@@ -1,157 +0,0 @@
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_fav/datum.dart';
|
||||
import 'package:PiliPlus/models/space_fav/list.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/member_favorite_ctr.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/widget/item.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavorite extends StatefulWidget {
|
||||
const MemberFavorite({
|
||||
super.key,
|
||||
required this.heroTag,
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final String? heroTag;
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
State<MemberFavorite> createState() => _MemberFavoriteState();
|
||||
}
|
||||
|
||||
class _MemberFavoriteState extends State<MemberFavorite>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(
|
||||
MemberFavoriteCtr(mid: widget.mid),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState loadingState) {
|
||||
final theme = Theme.of(context);
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => (loadingState.response as List?)?.isNotEmpty == true
|
||||
? refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: Obx(
|
||||
() => _buildItem(theme, _controller.first.value, true),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: Obx(
|
||||
() => _buildItem(theme, _controller.second.value, false),
|
||||
),
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 80 + MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
_buildItem(ThemeData theme, Datum data, bool isFirst) {
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
dividerColor: Colors.transparent,
|
||||
),
|
||||
child: ExpansionTile(
|
||||
dense: true,
|
||||
initiallyExpanded: true,
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: data.name,
|
||||
style: TextStyle(fontSize: 14),
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ${data.mediaListResponse?.count}',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
children: [
|
||||
...(data.mediaListResponse?.list as List<FavList>).map(
|
||||
(item) => SizedBox(
|
||||
height: 98,
|
||||
child: MemberFavItem(
|
||||
item: item,
|
||||
callback: (res) {
|
||||
if (res == true) {
|
||||
_controller.first.value.mediaListResponse?.list
|
||||
?.remove(item);
|
||||
_controller.first.refresh();
|
||||
} else {
|
||||
Future.delayed(const Duration(milliseconds: 100), () {
|
||||
_controller.onRefresh();
|
||||
});
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => (isFirst
|
||||
? _controller.firstEnd.value
|
||||
: _controller.secondEnd.value)
|
||||
? const SizedBox.shrink()
|
||||
: _buildLoadMoreItem(theme, isFirst),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
_buildLoadMoreItem(ThemeData theme, bool isFirst) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
onTap: () {
|
||||
if (isFirst) {
|
||||
_controller.userfavFolder();
|
||||
} else {
|
||||
_controller.userSubFolder();
|
||||
}
|
||||
},
|
||||
title: Text(
|
||||
'查看更多内容',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
import 'package:PiliPlus/http/api.dart';
|
||||
import 'package:PiliPlus/http/init.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space_fav/datum.dart';
|
||||
import 'package:PiliPlus/models/space_fav/list.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavoriteCtr extends CommonDataController {
|
||||
MemberFavoriteCtr({
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final int mid;
|
||||
|
||||
Rx<Datum> first = Datum().obs;
|
||||
Rx<Datum> second = Datum().obs;
|
||||
|
||||
RxBool firstEnd = true.obs;
|
||||
RxBool secondEnd = true.obs;
|
||||
|
||||
late int page = 2;
|
||||
late int page1 = 2;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
Future onRefresh() {
|
||||
page = 2;
|
||||
page1 = 2;
|
||||
return super.onRefresh();
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
try {
|
||||
List<Datum> res = response.response;
|
||||
first.value = res.first;
|
||||
second.value = res[1];
|
||||
|
||||
firstEnd.value = (res.first.mediaListResponse?.count ?? -1) <=
|
||||
(res.first.mediaListResponse?.list?.length ?? -1);
|
||||
secondEnd.value = (res[1].mediaListResponse?.count ?? -1) <=
|
||||
(res[1].mediaListResponse?.list?.length ?? -1);
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
loadingState.value = response;
|
||||
return true;
|
||||
}
|
||||
|
||||
Future userfavFolder() async {
|
||||
var res = await Request().get(Api.userFavFolder, queryParameters: {
|
||||
'pn': page,
|
||||
'ps': 20,
|
||||
'up_mid': mid,
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
page++;
|
||||
firstEnd.value = res.data['data']['has_more'] == false;
|
||||
if (res.data['data'] != null) {
|
||||
List<FavList> list = (res.data['data']['list'] as List<dynamic>?)
|
||||
?.map((item) => FavList.fromJson(item))
|
||||
.toList() ??
|
||||
<FavList>[];
|
||||
first.value.mediaListResponse?.list?.addAll(list);
|
||||
first.refresh();
|
||||
} else {
|
||||
firstEnd.value = true;
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(res.data['message'] ?? '账号未登录');
|
||||
}
|
||||
}
|
||||
|
||||
Future userSubFolder() async {
|
||||
var res = await Request().get(Api.userSubFolder, queryParameters: {
|
||||
'up_mid': mid,
|
||||
'ps': 20,
|
||||
'pn': page1,
|
||||
'platform': 'web',
|
||||
});
|
||||
if (res.data['code'] == 0) {
|
||||
page++;
|
||||
secondEnd.value = res.data['data']['has_more'] == false;
|
||||
if (res.data['data'] != null) {
|
||||
List<FavList> list = (res.data['data']['list'] as List<dynamic>?)
|
||||
?.map((item) => FavList.fromJson(item))
|
||||
.toList() ??
|
||||
<FavList>[];
|
||||
second.value.mediaListResponse?.list?.addAll(list);
|
||||
second.refresh();
|
||||
} else {
|
||||
secondEnd.value = true;
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(res.data['message'] ?? '账号未登录');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.spaceFav(mid: mid);
|
||||
}
|
||||
@@ -1,151 +0,0 @@
|
||||
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/models/space_fav/list.dart';
|
||||
import 'package:PiliPlus/models/user/sub_folder.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavItem extends StatelessWidget {
|
||||
const MemberFavItem({super.key, required this.item, this.callback});
|
||||
|
||||
final FavList item;
|
||||
final ValueChanged<bool?>? callback;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
if (item.state == 1) {
|
||||
// invalid
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.type == 0) {
|
||||
dynamic res = await Get.toNamed(
|
||||
'/favDetail',
|
||||
parameters: {
|
||||
'mediaId': item.id.toString(),
|
||||
'heroTag': Utils.makeHeroTag(item.id),
|
||||
},
|
||||
);
|
||||
callback?.call(res);
|
||||
} else {
|
||||
Get.toNamed(
|
||||
'/subDetail',
|
||||
arguments: SubFolderItemData(
|
||||
type: item.type,
|
||||
title: item.title,
|
||||
cover: item.cover,
|
||||
upper: Upper(
|
||||
mid: item.upper?.mid,
|
||||
name: item.upper?.name,
|
||||
face: item.upper?.face,
|
||||
),
|
||||
mediaCount: item.mediaCount,
|
||||
viewCount: item.viewCount,
|
||||
),
|
||||
parameters: {
|
||||
'id': item.id.toString(),
|
||||
'heroTag': Utils.makeHeroTag(item.id),
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
imageSaveDialog(
|
||||
title: item.title,
|
||||
cover: item.cover,
|
||||
);
|
||||
},
|
||||
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: (context, boxConstraints) {
|
||||
return Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: item.cover,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
),
|
||||
if (item.type == 21)
|
||||
PBadge(
|
||||
right: 3,
|
||||
bottom: 3,
|
||||
text: '合集',
|
||||
bold: false,
|
||||
size: 'small',
|
||||
)
|
||||
else if (item.type == 0 || item.type == 11)
|
||||
Positioned(
|
||||
right: 3,
|
||||
bottom: 3,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(5),
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
child: Icon(
|
||||
Icons.video_library_outlined,
|
||||
size: 12,
|
||||
color: theme.colorScheme.onPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item.title ?? '',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
item.type == 0
|
||||
? '${item.mediaCount}个内容 · ${Utils.isPublicFavText(item.attr ?? 0)}'
|
||||
: item.type == 11
|
||||
? '${item.mediaCount}个内容 · ${item.upper?.name}'
|
||||
: item.type == 21
|
||||
? '创建者: ${item.upper?.name}\n${item.mediaCount}个视频 · ${Utils.numFormat(item.viewCount)}播放'
|
||||
: '${item.mediaCount}个内容',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
|
||||
class SeasonSeriesController extends CommonListController {
|
||||
SeasonSeriesController(this.mid);
|
||||
final int mid;
|
||||
int? count;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
List? getDataList(response) {
|
||||
return ((response['seasons_list'] as List?) ?? []) +
|
||||
((response['series_list'] as List?) ?? []);
|
||||
}
|
||||
|
||||
@override
|
||||
void checkIsEnd(int length) {
|
||||
if (count != null && length >= count!) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success response) {
|
||||
count = response.response['page']?['total'];
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() => MemberHttp.seasonSeriesList(
|
||||
mid: mid,
|
||||
pn: currentPage,
|
||||
);
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
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/content/member_contribute/content/season_series/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/season_series/widget/season_series_card.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video.dart';
|
||||
import 'package:PiliPlus/pages/member/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>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late final _controller = Get.put(
|
||||
SeasonSeriesController(widget.mid),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<dynamic>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? CustomScrollView(
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
dynamic item = loadingState.response![index];
|
||||
return SeasonSeriesCard(
|
||||
item: item,
|
||||
onTap: () {
|
||||
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: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: 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(
|
||||
onReload: () {
|
||||
_controller.onReload();
|
||||
},
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: () {
|
||||
_controller.onReload();
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
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/utils/utils.dart';
|
||||
import 'package:flutter/material.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(
|
||||
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(
|
||||
clipBehavior: Clip.none,
|
||||
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,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
videoContent(context),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget videoContent(context) {
|
||||
final theme = Theme.of(context);
|
||||
return Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['meta']['name'],
|
||||
textAlign: TextAlign.start,
|
||||
style: TextStyle(
|
||||
fontSize: theme.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: 12,
|
||||
height: 1,
|
||||
color: theme.colorScheme.outline,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/custom_sliver_persistent_header_delegate.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_card_h_member_video.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video_ctr.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberVideo extends StatefulWidget {
|
||||
const MemberVideo({
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.heroTag,
|
||||
required this.mid,
|
||||
this.seasonId,
|
||||
this.seriesId,
|
||||
this.title,
|
||||
});
|
||||
|
||||
final ContributeType type;
|
||||
final String? heroTag;
|
||||
final int mid;
|
||||
final int? seasonId;
|
||||
final int? seriesId;
|
||||
final String? title;
|
||||
|
||||
@override
|
||||
State<MemberVideo> createState() => _MemberVideoState();
|
||||
}
|
||||
|
||||
class _MemberVideoState extends State<MemberVideo>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(
|
||||
MemberVideoCtr(
|
||||
type: widget.type,
|
||||
mid: widget.mid,
|
||||
seasonId: widget.seasonId,
|
||||
seriesId: widget.seriesId,
|
||||
username: Get.find<MemberControllerNew>(tag: widget.heroTag).username,
|
||||
title: widget.title,
|
||||
),
|
||||
tag:
|
||||
'${widget.heroTag}${widget.type.name}${widget.seasonId}${widget.seriesId}',
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Obx(() => _buildBody(_controller.loadingState.value));
|
||||
}
|
||||
|
||||
_buildBody(LoadingState<List<Item>?> loadingState) {
|
||||
final theme = Theme.of(context);
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => loadingState.response?.isNotEmpty == true
|
||||
? Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
refreshIndicator(
|
||||
onRefresh: () async {
|
||||
await _controller.onRefresh();
|
||||
},
|
||||
child: CustomScrollView(
|
||||
physics: PositionRetainedScrollPhysics(
|
||||
shouldRetain: _controller.isLocating == true,
|
||||
parent: ClampingScrollPhysics(),
|
||||
),
|
||||
slivers: [
|
||||
SliverPersistentHeader(
|
||||
pinned: false,
|
||||
floating: true,
|
||||
delegate: CustomSliverPersistentHeaderDelegate(
|
||||
extent: 40,
|
||||
bgColor: theme.colorScheme.surface,
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
children: [
|
||||
const SizedBox(width: 8),
|
||||
Obx(
|
||||
() => Padding(
|
||||
padding: const EdgeInsets.only(left: 6),
|
||||
child: Text(
|
||||
_controller.count.value != -1
|
||||
? '共${_controller.count.value}视频'
|
||||
: '',
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(
|
||||
() => _controller.episodicButton.value.uri !=
|
||||
null
|
||||
? Container(
|
||||
height: 35,
|
||||
padding: EdgeInsets.only(
|
||||
left:
|
||||
_controller.count.value != -1
|
||||
? 6
|
||||
: 0),
|
||||
child: TextButton.icon(
|
||||
onPressed:
|
||||
_controller.toViewPlayAll,
|
||||
icon: Icon(
|
||||
Icons.play_circle_outline_rounded,
|
||||
size: 16,
|
||||
color:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
label: Text(
|
||||
_controller.episodicButton.value
|
||||
.text ??
|
||||
'播放全部',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color:
|
||||
theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: TextButton.icon(
|
||||
onPressed: _controller.queryBySort,
|
||||
icon: Icon(
|
||||
Icons.sort,
|
||||
size: 16,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
label: Obx(
|
||||
() => Text(
|
||||
widget.type == ContributeType.video
|
||||
? _controller.order.value ==
|
||||
'pubdate'
|
||||
? '最新发布'
|
||||
: '最多播放'
|
||||
: _controller.sort.value == 'desc'
|
||||
? '默认'
|
||||
: '倒序',
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: theme.colorScheme.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: StyleString.safeSpace - 5,
|
||||
bottom: MediaQuery.of(context).padding.bottom + 80,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (widget.type != ContributeType.season &&
|
||||
index == loadingState.response!.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
final Item item = loadingState.response![index];
|
||||
return VideoCardHMemberVideo(
|
||||
key: ValueKey('${item.param}'),
|
||||
videoItem: item,
|
||||
fromViewAid: _controller.fromViewAid,
|
||||
);
|
||||
},
|
||||
childCount: loadingState.response!.length,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (widget.type == ContributeType.video &&
|
||||
_controller.fromViewAid?.isNotEmpty == true &&
|
||||
_controller.isLocating != true)
|
||||
Positioned(
|
||||
right: 15,
|
||||
bottom: 15,
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
left: false,
|
||||
child: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
_controller
|
||||
..isLocating = true
|
||||
..lastAid = _controller.fromViewAid
|
||||
..currentPage = 0
|
||||
..loadingState.value = LoadingState.loading()
|
||||
..queryData();
|
||||
},
|
||||
label: const Text('定位至上次观看'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: scrollErrorWidget(
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
Error() => scrollErrorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/http/search.dart';
|
||||
import 'package:PiliPlus/models/space_archive/data.dart';
|
||||
import 'package:PiliPlus/models/space_archive/episodic_button.dart';
|
||||
import 'package:PiliPlus/models/space_archive/item.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart'
|
||||
show ContributeType;
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/id_utils.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberVideoCtr extends CommonListController<Data, Item> {
|
||||
MemberVideoCtr({
|
||||
required this.type,
|
||||
required this.mid,
|
||||
required this.seasonId,
|
||||
required this.seriesId,
|
||||
this.username,
|
||||
this.title,
|
||||
});
|
||||
|
||||
ContributeType type;
|
||||
int? seasonId;
|
||||
int? seriesId;
|
||||
final int mid;
|
||||
late RxString order = 'pubdate'.obs;
|
||||
late RxString sort = 'desc'.obs;
|
||||
RxInt count = (-1).obs;
|
||||
int? next;
|
||||
Rx<EpisodicButton> episodicButton = EpisodicButton().obs;
|
||||
final String? username;
|
||||
final String? title;
|
||||
|
||||
String? firstAid;
|
||||
String? lastAid;
|
||||
String? fromViewAid;
|
||||
bool? isLocating;
|
||||
bool? isLoadPrevious;
|
||||
bool? hasPrev;
|
||||
|
||||
@override
|
||||
Future onRefresh() async {
|
||||
if (isLocating == true) {
|
||||
if (hasPrev == true) {
|
||||
isLoadPrevious = true;
|
||||
await queryData();
|
||||
}
|
||||
} else {
|
||||
firstAid = null;
|
||||
lastAid = null;
|
||||
next = null;
|
||||
isEnd = false;
|
||||
currentPage = 0;
|
||||
await queryData();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
if (type == ContributeType.video) {
|
||||
fromViewAid = Get.parameters['from_view_aid'];
|
||||
}
|
||||
currentPage = 0;
|
||||
queryData();
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
Data data = response.response;
|
||||
episodicButton.value = data.episodicButton ?? EpisodicButton();
|
||||
episodicButton.refresh();
|
||||
next = data.next;
|
||||
if (currentPage == 0 || isLoadPrevious == true) {
|
||||
hasPrev = data.hasPrev;
|
||||
}
|
||||
if (currentPage == 0 || isLoadPrevious != true) {
|
||||
if ((type == ContributeType.video
|
||||
? data.hasNext == false
|
||||
: data.next == 0) ||
|
||||
data.item.isNullOrEmpty) {
|
||||
isEnd = true;
|
||||
}
|
||||
}
|
||||
count.value = type == ContributeType.season
|
||||
? (data.item?.length ?? -1)
|
||||
: (data.count ?? -1);
|
||||
if (currentPage != 0 && loadingState.value is Success) {
|
||||
data.item ??= <Item>[];
|
||||
if (isLoadPrevious == true) {
|
||||
data.item!.addAll((loadingState.value as Success).response);
|
||||
} else {
|
||||
data.item!.insertAll(0, (loadingState.value as Success).response);
|
||||
}
|
||||
}
|
||||
firstAid = data.item?.firstOrNull?.param;
|
||||
lastAid = data.item?.lastOrNull?.param;
|
||||
isLoadPrevious = null;
|
||||
loadingState.value = LoadingState.success(data.item);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.spaceArchive(
|
||||
type: type,
|
||||
mid: mid,
|
||||
aid: type == ContributeType.video
|
||||
? isLoadPrevious == true
|
||||
? firstAid
|
||||
: lastAid
|
||||
: null,
|
||||
order: type == ContributeType.video ? order.value : null,
|
||||
sort: type == ContributeType.video
|
||||
? isLoadPrevious == true
|
||||
? 'asc'
|
||||
: null
|
||||
: sort.value,
|
||||
pn: type == ContributeType.charging ? currentPage : null,
|
||||
next: next,
|
||||
seasonId: seasonId,
|
||||
seriesId: seriesId,
|
||||
includeCursor: isLocating == true && currentPage == 0 ? true : null,
|
||||
);
|
||||
|
||||
queryBySort() {
|
||||
if (type == ContributeType.video) {
|
||||
isLocating = null;
|
||||
order.value = order.value == 'pubdate' ? 'click' : 'pubdate';
|
||||
} else {
|
||||
sort.value = sort.value == 'desc' ? 'asc' : 'desc';
|
||||
}
|
||||
onReload();
|
||||
}
|
||||
|
||||
void toViewPlayAll() async {
|
||||
if (loadingState.value is Success) {
|
||||
List<Item>? list = (loadingState.value as Success).response;
|
||||
|
||||
if (list.isNullOrEmpty) return;
|
||||
|
||||
if (episodicButton.value.text == '继续播放') {
|
||||
dynamic oid = RegExp(r'oid=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1);
|
||||
dynamic bvid = IdUtils.av2bv(int.tryParse(oid) ?? 0);
|
||||
dynamic cid = await SearchHttp.ab2c(aid: oid, bvid: bvid);
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=$bvid&cid=$cid',
|
||||
arguments: {
|
||||
'heroTag': Utils.makeHeroTag(oid),
|
||||
'sourceType': 'archive',
|
||||
'mediaId': seasonId ?? seriesId ?? mid,
|
||||
'oid': oid,
|
||||
'favTitle':
|
||||
'$username: ${title ?? episodicButton.value.text ?? '播放全部'}',
|
||||
if (seriesId == null) 'count': count.value,
|
||||
if (seasonId != null || seriesId != null)
|
||||
'mediaType': RegExp(r'page_type=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'desc': RegExp(r'desc=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1) ==
|
||||
'1',
|
||||
'sortField': RegExp(r'sort_field=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'isContinuePlaying': true,
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (Item element in list!) {
|
||||
if (element.cid == null) {
|
||||
continue;
|
||||
} else {
|
||||
if (element.bvid != list.first.bvid) {
|
||||
SmartDialog.showToast('已跳过不支持播放的视频');
|
||||
}
|
||||
bool desc = seasonId != null ? false : true;
|
||||
desc = (seasonId != null || seriesId != null) &&
|
||||
(type == ContributeType.video
|
||||
? order.value == 'click'
|
||||
: sort.value == 'asc')
|
||||
? desc.not
|
||||
: desc;
|
||||
PageUtils.toVideoPage(
|
||||
'bvid=${element.bvid}&cid=${element.cid}',
|
||||
arguments: {
|
||||
'videoItem': element,
|
||||
'heroTag': Utils.makeHeroTag(element.bvid),
|
||||
'sourceType': 'archive',
|
||||
'mediaId': seasonId ?? seriesId ?? mid,
|
||||
'oid': IdUtils.bv2av(element.bvid!),
|
||||
'favTitle':
|
||||
'$username: ${title ?? episodicButton.value.text ?? '播放全部'}',
|
||||
if (seriesId == null) 'count': count.value,
|
||||
if (seasonId != null || seriesId != null)
|
||||
'mediaType': RegExp(r'page_type=([\d]+)')
|
||||
.firstMatch('${episodicButton.value.uri}')
|
||||
?.group(1),
|
||||
'desc': desc,
|
||||
if (type == ContributeType.video)
|
||||
'sortField': order.value == 'click' ? 2 : 1,
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future onReload() {
|
||||
isLocating = null;
|
||||
return super.onReload();
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/member_article.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/audio/member_audio.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/season_series/season_series_page.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/video/member_video.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute_ctr.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
enum ContributeType { video, charging, season, series, bangumi }
|
||||
|
||||
class MemberContribute extends StatefulWidget {
|
||||
const MemberContribute({
|
||||
super.key,
|
||||
this.heroTag,
|
||||
this.initialIndex,
|
||||
required this.mid,
|
||||
});
|
||||
|
||||
final String? heroTag;
|
||||
final int? initialIndex;
|
||||
final int mid;
|
||||
|
||||
@override
|
||||
State<MemberContribute> createState() => _MemberContributeState();
|
||||
}
|
||||
|
||||
class _MemberContributeState extends State<MemberContribute>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _controller = Get.put(
|
||||
MemberContributeCtr(
|
||||
heroTag: widget.heroTag,
|
||||
initialIndex: widget.initialIndex,
|
||||
),
|
||||
tag: widget.heroTag,
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
final theme = Theme.of(context);
|
||||
return _controller.tabs != null
|
||||
? Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: TabBar(
|
||||
overlayColor: WidgetStateProperty.all(Colors.transparent),
|
||||
splashFactory: NoSplash.splashFactory,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8),
|
||||
isScrollable: true,
|
||||
tabs: _controller.tabs!,
|
||||
tabAlignment: TabAlignment.start,
|
||||
controller: _controller.tabController,
|
||||
dividerHeight: 0,
|
||||
indicatorWeight: 0,
|
||||
indicatorPadding:
|
||||
const EdgeInsets.symmetric(horizontal: 3, vertical: 8),
|
||||
indicator: BoxDecoration(
|
||||
color: theme.colorScheme.secondaryContainer,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
labelStyle: TabBarTheme.of(context)
|
||||
.labelStyle
|
||||
?.copyWith(fontSize: 14) ??
|
||||
const TextStyle(fontSize: 14),
|
||||
labelColor: theme.colorScheme.onSecondaryContainer,
|
||||
unselectedLabelColor: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: TabBarView(
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
controller: _controller.tabController,
|
||||
children: _controller.items!.map(_getPageFromType).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: _controller.items?.isNotEmpty == true
|
||||
? _getPageFromType(_controller.items!.first)
|
||||
: const SizedBox.shrink();
|
||||
}
|
||||
|
||||
Widget _getPageFromType(item) {
|
||||
return switch (item.param) {
|
||||
'video' => MemberVideo(
|
||||
type: ContributeType.video,
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
title: item.title,
|
||||
),
|
||||
'charging_video' => MemberVideo(
|
||||
type: ContributeType.charging,
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
title: item.title,
|
||||
),
|
||||
'article' => MemberArticle(
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
),
|
||||
'audio' => MemberAudio(heroTag: widget.heroTag),
|
||||
'season_video' => MemberVideo(
|
||||
type: ContributeType.season,
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
seasonId: item.seasonId,
|
||||
title: item.title,
|
||||
),
|
||||
'series' => MemberVideo(
|
||||
type: ContributeType.series,
|
||||
heroTag: widget.heroTag,
|
||||
mid: widget.mid,
|
||||
seriesId: item.seriesId,
|
||||
title: item.title,
|
||||
),
|
||||
'ugcSeason' => SeasonSeriesPage(
|
||||
mid: widget.mid,
|
||||
heroTag: widget.heroTag,
|
||||
),
|
||||
_ => Center(child: Text(item.title!))
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,64 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space/tab2.dart';
|
||||
import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../../models/space/item.dart';
|
||||
|
||||
class MemberContributeCtr extends CommonDataController
|
||||
with GetSingleTickerProviderStateMixin {
|
||||
MemberContributeCtr({
|
||||
required this.heroTag,
|
||||
required this.initialIndex,
|
||||
});
|
||||
final String? heroTag;
|
||||
final int? initialIndex;
|
||||
|
||||
TabController? tabController;
|
||||
List<Tab>? tabs;
|
||||
late final _ctr = Get.find<MemberControllerNew>(tag: heroTag);
|
||||
List<Item>? items;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
Tab2 contribute =
|
||||
_ctr.tab2!.firstWhere((item) => item.param == 'contribute');
|
||||
if (contribute.items?.isNullOrEmpty == false) {
|
||||
items = contribute.items;
|
||||
if (contribute.items!.length > 1) {
|
||||
// show if exist
|
||||
if (_ctr.hasSeasonOrSeries == true) {
|
||||
items!.add(
|
||||
Item(
|
||||
param: 'ugcSeason',
|
||||
title: '全部合集/列表',
|
||||
),
|
||||
);
|
||||
}
|
||||
tabs = items!.map((item) => Tab(text: item.title)).toList();
|
||||
tabController = TabController(
|
||||
vsync: this,
|
||||
length: items!.length,
|
||||
initialIndex: max(0, initialIndex ?? 0),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState> customGetData() {
|
||||
throw UnimplementedError();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
tabController?.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
@@ -1,338 +0,0 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/common/widgets/video_card_v_member_home.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart';
|
||||
import 'package:PiliPlus/models/space/item.dart';
|
||||
import 'package:PiliPlus/pages/bangumi/widgets/bangumi_card_v_member_home.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/article/widget/item.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute_ctr.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_home/widget/fav_item.dart';
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:PiliPlus/pages/member_coin/index.dart';
|
||||
import 'package:PiliPlus/pages/member_like/index.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberHome extends StatefulWidget {
|
||||
const MemberHome({super.key, this.heroTag});
|
||||
|
||||
final String? heroTag;
|
||||
|
||||
@override
|
||||
State<MemberHome> createState() => _MemberHomeState();
|
||||
}
|
||||
|
||||
class _MemberHomeState extends State<MemberHome>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
late final _ctr = Get.find<MemberControllerNew>(tag: widget.heroTag);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return _buildBody(_ctr.loadingState.value);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState loadingState) {
|
||||
final isVertical = context.orientation == Orientation.portrait;
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => loadingState.response is Data
|
||||
? CustomScrollView(
|
||||
slivers: [
|
||||
if (loadingState.response?.archive?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '视频',
|
||||
param: 'contribute',
|
||||
param1: 'video',
|
||||
count: loadingState.response.archive.count,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(55),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return VideoCardVMemberHome(
|
||||
videoItem:
|
||||
loadingState.response.archive.item[index],
|
||||
);
|
||||
},
|
||||
childCount: min(isVertical ? 4 : 8,
|
||||
loadingState.response.archive.item.length),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (loadingState.response?.favourite2?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '收藏',
|
||||
param: 'favorite',
|
||||
count: loadingState.response.favourite2.count,
|
||||
),
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 98,
|
||||
child: MemberFavItem(
|
||||
item: loadingState.response.favourite2.item.first,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (loadingState.response?.coinArchive?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '最近投币的视频',
|
||||
param: 'coinArchive',
|
||||
count: loadingState.response.coinArchive.count,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(55),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return VideoCardVMemberHome(
|
||||
videoItem:
|
||||
loadingState.response.coinArchive.item[index],
|
||||
);
|
||||
},
|
||||
childCount: min(isVertical ? 2 : 4,
|
||||
loadingState.response.coinArchive.item.length),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (loadingState.response?.likeArchive?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '最近点赞的视频',
|
||||
param: 'likeArchive',
|
||||
count: loadingState.response.likeArchive.count,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(55),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return VideoCardVMemberHome(
|
||||
videoItem:
|
||||
loadingState.response.likeArchive.item[index],
|
||||
);
|
||||
},
|
||||
childCount: min(isVertical ? 2 : 4,
|
||||
loadingState.response.likeArchive.item.length),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
if (loadingState.response?.article?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '专栏',
|
||||
param: 'contribute',
|
||||
param1: 'article',
|
||||
count: loadingState.response.article.count,
|
||||
),
|
||||
SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return MemberArticleItem(
|
||||
item: loadingState.response.article.item[index],
|
||||
);
|
||||
},
|
||||
childCount: isVertical
|
||||
? 1
|
||||
: loadingState.response.article.item.length,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (loadingState.response?.audios?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '音频',
|
||||
param: 'contribute',
|
||||
param1: 'audio',
|
||||
count: loadingState.response.audios.count,
|
||||
),
|
||||
// TODO
|
||||
],
|
||||
if (loadingState.response?.season?.item?.isNotEmpty ==
|
||||
true) ...[
|
||||
_videoHeader(
|
||||
title: '追番',
|
||||
param: 'bangumi',
|
||||
count: loadingState.response.season.count,
|
||||
),
|
||||
SliverPadding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
),
|
||||
sliver: SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
crossAxisSpacing: StyleString.cardSpace,
|
||||
maxCrossAxisExtent: Grid.smallCardWidth / 3 * 2,
|
||||
childAspectRatio: 0.75,
|
||||
mainAxisExtent:
|
||||
MediaQuery.textScalerOf(context).scale(52),
|
||||
),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return BangumiCardVMemberHome(
|
||||
bangumiItem:
|
||||
loadingState.response.season.item[index],
|
||||
);
|
||||
},
|
||||
childCount: min(isVertical ? 3 : 6,
|
||||
loadingState.response.season.item.length),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 80 + MediaQuery.of(context).padding.bottom,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: errorWidget(),
|
||||
Error() => errorWidget(),
|
||||
};
|
||||
}
|
||||
|
||||
Widget _videoHeader({
|
||||
required String title,
|
||||
required String param,
|
||||
String? param1,
|
||||
required int count,
|
||||
}) {
|
||||
final color = Theme.of(context).colorScheme.outline;
|
||||
return SliverToBoxAdapter(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(text: '$title '),
|
||||
TextSpan(
|
||||
text: count.toString(),
|
||||
style: TextStyle(fontSize: 13, color: color),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
int index =
|
||||
_ctr.tab2!.indexWhere((item) => item.param == param);
|
||||
if (index != -1) {
|
||||
if (['video', 'article', 'audio'].contains(param1)) {
|
||||
List<Item> items = _ctr.tab2!
|
||||
.firstWhere((item) => item.param == param)
|
||||
.items!;
|
||||
int index1 =
|
||||
items.indexWhere((item) => item.param == param1);
|
||||
try {
|
||||
final contributeCtr =
|
||||
Get.find<MemberContributeCtr>(tag: widget.heroTag);
|
||||
// contributeCtr.tabController?.animateTo(index1);
|
||||
if (contributeCtr.tabController?.index != index1) {
|
||||
contributeCtr.tabController?.index = index1;
|
||||
}
|
||||
debugPrint('initialized');
|
||||
} catch (e) {
|
||||
_ctr.contributeInitialIndex.value = index1;
|
||||
debugPrint('not initialized');
|
||||
}
|
||||
}
|
||||
_ctr.tabController?.animateTo(index);
|
||||
} else {
|
||||
if (param == 'coinArchive') {
|
||||
Get.to(MemberCoinPage(
|
||||
mid: _ctr.mid,
|
||||
name: _ctr.username,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (param == 'likeArchive') {
|
||||
Get.to(MemberLikePage(
|
||||
mid: _ctr.mid,
|
||||
name: _ctr.username,
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
// else TODO
|
||||
SmartDialog.showToast('view $param');
|
||||
}
|
||||
},
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: '查看更多',
|
||||
style: TextStyle(color: color),
|
||||
),
|
||||
WidgetSpan(
|
||||
alignment: PlaceholderAlignment.top,
|
||||
child: Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 14,
|
||||
color: color,
|
||||
),
|
||||
style: TextStyle(fontSize: 13, color: color),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class MemberFavItem extends StatelessWidget {
|
||||
const MemberFavItem({super.key, required this.item});
|
||||
|
||||
final dynamic item;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
if (item['type'] == 2) {
|
||||
Get.toNamed(
|
||||
'/favDetail',
|
||||
parameters: {
|
||||
'mediaId': item['media_id'].toString(),
|
||||
'heroTag': Utils.makeHeroTag(item['media_id']),
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
onLongPress: () {
|
||||
imageSaveDialog(
|
||||
title: item['title'],
|
||||
cover: item['cover'],
|
||||
);
|
||||
},
|
||||
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: (context, boxConstraints) {
|
||||
return NetworkImgLayer(
|
||||
src: item['cover'],
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
item['title'] ?? '',
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${item['count']}个内容 · ${item['is_public'] == 1 ? '私密' : '公开'}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,7 @@ extension MemberTabTypeExt on MemberTabType {
|
||||
String get title => ['默认', '首页', '动态', '投稿', '收藏', '番剧'][index];
|
||||
}
|
||||
|
||||
class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||
class MemberControllerNew extends CommonDataController<SpaceData, dynamic>
|
||||
with GetTickerProviderStateMixin {
|
||||
MemberControllerNew({required this.mid});
|
||||
int mid;
|
||||
@@ -61,8 +61,8 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||
}
|
||||
|
||||
@override
|
||||
bool customHandleResponse(bool isRefresh, Success<Data> response) {
|
||||
Data data = response.response;
|
||||
bool customHandleResponse(bool isRefresh, Success<SpaceData> response) {
|
||||
SpaceData data = response.response;
|
||||
username = data.card?.name ?? '';
|
||||
isFollowed = data.card?.relation?.isFollowed;
|
||||
if (data.relation == -1) {
|
||||
@@ -133,7 +133,7 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||
Tab2(
|
||||
title: '投稿',
|
||||
param: 'contribute',
|
||||
items: [Item(title: '视频', param: 'video')],
|
||||
items: [SpaceItem(title: '视频', param: 'video')],
|
||||
),
|
||||
Tab2(title: '收藏', param: 'favorite'),
|
||||
Tab2(title: '追番', param: 'bangumi'),
|
||||
@@ -151,7 +151,7 @@ class MemberControllerNew extends CommonDataController<Data, dynamic>
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<Data>> customGetData() => MemberHttp.space(
|
||||
Future<LoadingState<SpaceData>> customGetData() => MemberHttp.space(
|
||||
mid: mid,
|
||||
fromViewAid: fromViewAid,
|
||||
);
|
||||
|
||||
@@ -4,10 +4,10 @@ import 'package:PiliPlus/common/widgets/radio_widget.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/http/member.dart';
|
||||
import 'package:PiliPlus/models/space/data.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/bangumi/member_bangumi.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/content/favorite/member_favorite.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_contribute/member_contribute.dart';
|
||||
import 'package:PiliPlus/pages/member/content/member_home/member_home.dart';
|
||||
import 'package:PiliPlus/pages/member_pgc/view.dart';
|
||||
import 'package:PiliPlus/pages/member_favorite/view.dart';
|
||||
import 'package:PiliPlus/pages/member_contribute/view.dart';
|
||||
import 'package:PiliPlus/pages/member_home/view.dart';
|
||||
import 'package:PiliPlus/pages/member/controller.dart';
|
||||
import 'package:PiliPlus/pages/member/widget/user_info_card.dart';
|
||||
import 'package:PiliPlus/pages/member_dynamics/view.dart';
|
||||
@@ -19,14 +19,14 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
|
||||
class MemberPageNew extends StatefulWidget {
|
||||
const MemberPageNew({super.key});
|
||||
class MemberPage extends StatefulWidget {
|
||||
const MemberPage({super.key});
|
||||
|
||||
@override
|
||||
State<MemberPageNew> createState() => _MemberPageNewState();
|
||||
State<MemberPage> createState() => _MemberPageState();
|
||||
}
|
||||
|
||||
class _MemberPageNewState extends State<MemberPageNew> {
|
||||
class _MemberPageState extends State<MemberPage> {
|
||||
late final int _mid;
|
||||
late final String _heroTag;
|
||||
late final MemberControllerNew _userController;
|
||||
@@ -271,7 +271,7 @@ class _MemberPageNewState extends State<MemberPageNew> {
|
||||
Widget _buildUserInfo(LoadingState userState, [bool isV = true]) {
|
||||
return switch (userState) {
|
||||
Loading() => const CircularProgressIndicator(),
|
||||
Success() => userState.response is Data
|
||||
Success() => userState.response is SpaceData
|
||||
? Obx(
|
||||
() => UserInfoCard(
|
||||
isV: isV,
|
||||
@@ -1,504 +0,0 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget.dart';
|
||||
import 'package:PiliPlus/http/constants.dart';
|
||||
import 'package:PiliPlus/http/index.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart' hide FormData, MultipartFile;
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
enum ProfileType { uname, sign, sex, birthday }
|
||||
|
||||
class EditProfilePage extends StatefulWidget {
|
||||
const EditProfilePage({super.key});
|
||||
|
||||
@override
|
||||
State<EditProfilePage> createState() => _EditProfilePageState();
|
||||
}
|
||||
|
||||
class _EditProfilePageState extends State<EditProfilePage> {
|
||||
LoadingState _loadingState = LoadingState.loading();
|
||||
late final _textController = TextEditingController();
|
||||
late final _imagePicker = ImagePicker();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_getInfo();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_textController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('账号资料')),
|
||||
body: _buildBody(theme, _loadingState),
|
||||
);
|
||||
}
|
||||
|
||||
_getInfo() async {
|
||||
Map<String, String> data = {
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
'mobi_app': 'android_hd',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statistics,
|
||||
};
|
||||
Request()
|
||||
.get('${HttpString.appBaseUrl}/x/v2/account/myinfo',
|
||||
queryParameters: data)
|
||||
.then((data) {
|
||||
setState(() {
|
||||
if (data.data['code'] == 0) {
|
||||
_loadingState = LoadingState.success(data.data['data']);
|
||||
} else {
|
||||
_loadingState = LoadingState.error(data.data['message']);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
Widget _buildBody(ThemeData theme, LoadingState loadingState) {
|
||||
late final divider = Divider(
|
||||
height: 1,
|
||||
color: theme.dividerColor.withOpacity(0.1),
|
||||
);
|
||||
|
||||
late final divider1 = Divider(
|
||||
thickness: 16,
|
||||
color: theme.dividerColor.withOpacity(0.1),
|
||||
);
|
||||
|
||||
return switch (loadingState) {
|
||||
Loading() => loadingWidget,
|
||||
Success() => SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '头像',
|
||||
widget: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 5),
|
||||
child: ClipOval(
|
||||
child: CachedNetworkImage(
|
||||
imageUrl:
|
||||
Utils.thumbnailImgUrl(loadingState.response['face']),
|
||||
),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
EasyThrottle.throttle(
|
||||
'imagePicker', const Duration(milliseconds: 500),
|
||||
() async {
|
||||
_pickImg(theme);
|
||||
});
|
||||
},
|
||||
),
|
||||
divider,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '昵称',
|
||||
text: loadingState.response['name'],
|
||||
onTap: () {
|
||||
if (loadingState.response['coins'] < 6) {
|
||||
SmartDialog.showToast('硬币不足');
|
||||
} else {
|
||||
_editDialog(
|
||||
type: ProfileType.uname,
|
||||
title: '昵称',
|
||||
text: loadingState.response['name'],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
divider,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '性别',
|
||||
text: _sex(loadingState.response['sex']),
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context_) =>
|
||||
_sexDialog(loadingState.response['sex']),
|
||||
);
|
||||
},
|
||||
),
|
||||
divider,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '出生年月',
|
||||
text: loadingState.response['birthday'],
|
||||
onTap: () {
|
||||
showDatePicker(
|
||||
context: context,
|
||||
initialDate:
|
||||
DateTime.parse(loadingState.response['birthday']),
|
||||
firstDate: DateTime(1900, 1, 1),
|
||||
lastDate: DateTime.now(),
|
||||
).then((date) {
|
||||
if (date != null) {
|
||||
_update(
|
||||
type: ProfileType.birthday,
|
||||
datum: DateFormat('yyyy-MM-dd').format(date),
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
),
|
||||
divider,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '个性签名',
|
||||
text: loadingState.response['sign'].isEmpty
|
||||
? '无'
|
||||
: loadingState.response['sign'],
|
||||
onTap: () {
|
||||
_editDialog(
|
||||
type: ProfileType.sign,
|
||||
title: '个性签名',
|
||||
text: loadingState.response['sign'],
|
||||
);
|
||||
},
|
||||
),
|
||||
divider1,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '头像挂件',
|
||||
onTap: () => PageUtils.launchURL(
|
||||
'https://www.bilibili.com/h5/mall/pendant/home'),
|
||||
),
|
||||
divider1,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: 'UID',
|
||||
needIcon: false,
|
||||
text: loadingState.response['mid'].toString(),
|
||||
onTap: () =>
|
||||
Utils.copyText(loadingState.response['mid'].toString()),
|
||||
),
|
||||
divider1,
|
||||
_item(
|
||||
theme: theme,
|
||||
title: '哔哩哔哩认证',
|
||||
onTap: () => PageUtils.launchURL(
|
||||
'https://account.bilibili.com/official/mobile/home'),
|
||||
),
|
||||
divider,
|
||||
SizedBox(height: 25 + MediaQuery.paddingOf(context).bottom),
|
||||
],
|
||||
),
|
||||
),
|
||||
Error() => errorWidget(
|
||||
errMsg: loadingState.errMsg,
|
||||
onReload: _getInfo,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
Widget _sexDialog(int current) {
|
||||
return AlertDialog(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 12),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_sexDialogItem(1, current, '男'),
|
||||
_sexDialogItem(0, current, '保密'),
|
||||
_sexDialogItem(2, current, '女'),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _sexDialogItem(
|
||||
int sex,
|
||||
int current,
|
||||
String text,
|
||||
) {
|
||||
return ListTile(
|
||||
dense: true,
|
||||
enabled: current != sex,
|
||||
title: Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(
|
||||
text,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
),
|
||||
trailing: current == sex ? const Icon(size: 22, Icons.check) : null,
|
||||
onTap: () {
|
||||
Get.back();
|
||||
_update(type: ProfileType.sex, datum: sex);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _editDialog({
|
||||
required ProfileType type,
|
||||
required String title,
|
||||
required String text,
|
||||
}) {
|
||||
_textController.text = text;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return AlertDialog(
|
||||
title: Text('修改$title'),
|
||||
content: TextField(
|
||||
controller: _textController,
|
||||
minLines: type == ProfileType.uname ? 1 : 4,
|
||||
maxLines: type == ProfileType.uname ? 1 : 4,
|
||||
autofocus: true,
|
||||
style: TextStyle(fontSize: 14),
|
||||
textInputAction:
|
||||
type == ProfileType.sign ? TextInputAction.newline : null,
|
||||
inputFormatters: [
|
||||
LengthLimitingTextInputFormatter(
|
||||
type == ProfileType.uname ? 16 : 70),
|
||||
],
|
||||
decoration: InputDecoration(
|
||||
hintText: text,
|
||||
hintStyle: TextStyle(
|
||||
fontSize: 14,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: Get.back,
|
||||
child: Text(
|
||||
'取消',
|
||||
style: TextStyle(color: theme.colorScheme.outline),
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
if (_textController.text == text) {
|
||||
SmartDialog.showToast('与原$title相同');
|
||||
} else {
|
||||
_update(type: type);
|
||||
}
|
||||
},
|
||||
child: const Text('确定'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
).then((_) {
|
||||
_textController.clear();
|
||||
});
|
||||
}
|
||||
|
||||
_update({
|
||||
required ProfileType type,
|
||||
dynamic datum,
|
||||
}) async {
|
||||
final accessKey = Accounts.main.accessKey;
|
||||
if (accessKey.isNullOrEmpty) {
|
||||
SmartDialog.showToast('请退出账号后重新登录');
|
||||
return;
|
||||
}
|
||||
Map<String, String> data = {
|
||||
'access_key': accessKey!,
|
||||
'build': '1462100',
|
||||
'c_locale': 'zh_CN',
|
||||
'channel': 'yingyongbao',
|
||||
'mobi_app': 'android_hd',
|
||||
'platform': 'android',
|
||||
's_locale': 'zh_CN',
|
||||
'statistics': Constants.statistics,
|
||||
'ts': (DateTime.now().millisecondsSinceEpoch ~/ 1000).toString(),
|
||||
if (type == ProfileType.uname)
|
||||
'uname': _textController.text
|
||||
else if (type == ProfileType.sign)
|
||||
'user_sign': _textController.text
|
||||
else if (type == ProfileType.birthday)
|
||||
'birthday': datum
|
||||
else if (type == ProfileType.sex)
|
||||
'sex': datum.toString(),
|
||||
};
|
||||
Utils.appSign(data);
|
||||
Request()
|
||||
.post(
|
||||
'/x/member/app/${type.name}/update',
|
||||
data: data,
|
||||
options: Options(
|
||||
contentType: Headers.formUrlEncodedContentType,
|
||||
),
|
||||
)
|
||||
.then((data) {
|
||||
if (data.data['code'] == 0) {
|
||||
if (type == ProfileType.uname) {
|
||||
(_loadingState as Success).response['name'] = _textController.text;
|
||||
(_loadingState as Success).response['coins'] -= 6;
|
||||
} else if (type == ProfileType.sign) {
|
||||
(_loadingState as Success).response['sign'] = _textController.text;
|
||||
} else if (type == ProfileType.birthday) {
|
||||
(_loadingState as Success).response['birthday'] = datum;
|
||||
} else if (type == ProfileType.sex) {
|
||||
(_loadingState as Success).response['sex'] = datum;
|
||||
}
|
||||
SmartDialog.showToast('修改成功');
|
||||
setState(() {});
|
||||
if (type == ProfileType.uname || type == ProfileType.sign) {
|
||||
Get.back();
|
||||
}
|
||||
} else {
|
||||
SmartDialog.showToast(data.data['message']);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
String _sex(int sex) {
|
||||
return switch (sex) {
|
||||
0 => '保密',
|
||||
1 => '男',
|
||||
2 => '女',
|
||||
_ => '未知',
|
||||
};
|
||||
}
|
||||
|
||||
Widget _item({
|
||||
required ThemeData theme,
|
||||
required String title,
|
||||
Widget? widget,
|
||||
String? text,
|
||||
GestureTapCallback? onTap,
|
||||
bool needIcon = true,
|
||||
}) {
|
||||
return ListTile(
|
||||
onTap: onTap,
|
||||
dense: title != '头像',
|
||||
leading: Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (text != null)
|
||||
Text(
|
||||
text,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.normal,
|
||||
color: theme.colorScheme.outline,
|
||||
),
|
||||
)
|
||||
else if (widget != null)
|
||||
widget,
|
||||
if (needIcon)
|
||||
Icon(
|
||||
Icons.keyboard_arrow_right,
|
||||
color: theme.colorScheme.outline,
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _pickImg(ThemeData theme) async {
|
||||
try {
|
||||
XFile? pickedFile = await _imagePicker.pickImage(
|
||||
source: ImageSource.gallery,
|
||||
imageQuality: 100,
|
||||
);
|
||||
if (pickedFile != null && mounted) {
|
||||
String? mimeType =
|
||||
lookupMimeType(pickedFile.path)?.split('/').getOrNull(1);
|
||||
if (mimeType == 'gif') {
|
||||
SmartDialog.showToast('不能选GIF');
|
||||
return;
|
||||
}
|
||||
CroppedFile? croppedFile = await ImageCropper().cropImage(
|
||||
sourcePath: pickedFile.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: '裁剪',
|
||||
toolbarColor: theme.colorScheme.secondaryContainer,
|
||||
toolbarWidgetColor: theme.colorScheme.onSecondaryContainer,
|
||||
aspectRatioPresets: [
|
||||
CropAspectRatioPresetCustom(),
|
||||
],
|
||||
lockAspectRatio: true,
|
||||
hideBottomControls: true,
|
||||
cropStyle: CropStyle.circle,
|
||||
initAspectRatio: CropAspectRatioPresetCustom(),
|
||||
),
|
||||
IOSUiSettings(
|
||||
title: '裁剪',
|
||||
aspectRatioPresets: [
|
||||
CropAspectRatioPresetCustom(),
|
||||
],
|
||||
cropStyle: CropStyle.circle,
|
||||
aspectRatioLockEnabled: true,
|
||||
resetAspectRatioEnabled: false,
|
||||
aspectRatioPickerButtonHidden: true,
|
||||
),
|
||||
],
|
||||
);
|
||||
if (croppedFile != null) {
|
||||
Request()
|
||||
.post(
|
||||
'/x/member/web/face/update',
|
||||
queryParameters: {
|
||||
'csrf': Accounts.main.csrf,
|
||||
},
|
||||
data: FormData.fromMap({
|
||||
'dopost': 'save',
|
||||
'DisplayRank': 10000,
|
||||
'face': await MultipartFile.fromFile(croppedFile.path),
|
||||
}),
|
||||
)
|
||||
.then((data) {
|
||||
if (data.data['code'] == 0) {
|
||||
(_loadingState as Success).response['face'] = data.data['data'];
|
||||
SmartDialog.showToast('修改成功');
|
||||
setState(() {});
|
||||
} else {
|
||||
SmartDialog.showToast(data.data['message']);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
SmartDialog.showToast(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CropAspectRatioPresetCustom implements CropAspectRatioPresetData {
|
||||
@override
|
||||
(int, int)? get data => (1, 1);
|
||||
|
||||
@override
|
||||
String get name => '1x1 (customized)';
|
||||
}
|
||||
@@ -2,8 +2,8 @@ import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/avatar.dart';
|
||||
import 'package:PiliPlus/common/widgets/interactiveviewer_gallery/interactiveviewer_gallery.dart'
|
||||
show SourceModel;
|
||||
import 'package:PiliPlus/models/space/card.dart' as space;
|
||||
import 'package:PiliPlus/models/space/images.dart' as space;
|
||||
import 'package:PiliPlus/models/space/card.dart';
|
||||
import 'package:PiliPlus/models/space/images.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/page_utils.dart';
|
||||
import 'package:PiliPlus/utils/storage.dart';
|
||||
@@ -29,8 +29,8 @@ class UserInfoCard extends StatelessWidget {
|
||||
final bool isV;
|
||||
final bool isOwner;
|
||||
final int relation;
|
||||
final space.Card card;
|
||||
final space.Images images;
|
||||
final SpaceCard card;
|
||||
final SpaceImages images;
|
||||
final VoidCallback onFollow;
|
||||
final dynamic live;
|
||||
final int? silence;
|
||||
|
||||
Reference in New Issue
Block a user