mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-06-01 16:48:16 +08:00
@@ -3,7 +3,7 @@ import 'dart:async';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
||||||
import 'package:easy_debounce/easy_throttle.dart';
|
import 'package:easy_debounce/easy_throttle.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/widgets.dart' show ScrollController;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
mixin ScrollOrRefreshMixin {
|
mixin ScrollOrRefreshMixin {
|
||||||
|
|||||||
@@ -32,7 +32,9 @@ class ActionPanel extends StatelessWidget {
|
|||||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextButton.icon(
|
child: Builder(
|
||||||
|
builder: (context) {
|
||||||
|
return TextButton.icon(
|
||||||
onPressed: () => showModalBottomSheet(
|
onPressed: () => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
@@ -56,8 +58,12 @@ class ActionPanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
style: btnStyle,
|
style: btnStyle,
|
||||||
label: Text(
|
label: Text(
|
||||||
forward.count != null ? NumUtils.numFormat(forward.count) : '转发',
|
forward.count != null
|
||||||
|
? NumUtils.numFormat(forward.count)
|
||||||
|
: '转发',
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@@ -76,32 +82,40 @@ class ActionPanel extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextButton.icon(
|
child: Builder(
|
||||||
onPressed: () => RequestUtils.onLikeDynamic(item, () {
|
builder: (context) {
|
||||||
if (context.mounted) {
|
final likeIcon = Icon(
|
||||||
(context as Element?)?.markNeedsBuild();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
icon: Icon(
|
|
||||||
like.status!
|
like.status!
|
||||||
? FontAwesomeIcons.solidThumbsUp
|
? FontAwesomeIcons.solidThumbsUp
|
||||||
: FontAwesomeIcons.thumbsUp,
|
: FontAwesomeIcons.thumbsUp,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: like.status! ? primary : outline,
|
color: like.status! ? primary : outline,
|
||||||
semanticLabel: like.status! ? "已赞" : "点赞",
|
semanticLabel: like.status! ? "已赞" : "点赞",
|
||||||
|
);
|
||||||
|
return TextButton.icon(
|
||||||
|
onPressed: () => RequestUtils.onLikeDynamic(
|
||||||
|
item,
|
||||||
|
likeIcon.color == primary,
|
||||||
|
() {
|
||||||
|
if (context.mounted) {
|
||||||
|
(context as Element?)?.markNeedsBuild();
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
icon: likeIcon,
|
||||||
style: btnStyle,
|
style: btnStyle,
|
||||||
label: AnimatedSwitcher(
|
label: AnimatedSwitcher(
|
||||||
duration: const Duration(milliseconds: 400),
|
duration: const Duration(milliseconds: 400),
|
||||||
transitionBuilder: (Widget child, Animation<double> animation) {
|
transitionBuilder: (child, animation) =>
|
||||||
return ScaleTransition(scale: animation, child: child);
|
ScaleTransition(scale: animation, child: child),
|
||||||
},
|
|
||||||
child: Text(
|
child: Text(
|
||||||
like.count != null ? NumUtils.numFormat(like.count) : '点赞',
|
like.count != null ? NumUtils.numFormat(like.count) : '点赞',
|
||||||
key: ValueKey<int?>(like.count),
|
key: ValueKey<int?>(like.count),
|
||||||
style: TextStyle(color: like.status! ? primary : outline),
|
style: TextStyle(color: like.status! ? primary : outline),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -368,18 +368,19 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
|||||||
required IconData icon,
|
required IconData icon,
|
||||||
required String text,
|
required String text,
|
||||||
required DynamicStat? stat,
|
required DynamicStat? stat,
|
||||||
required VoidCallback onPressed,
|
required ValueChanged<Color> onPressed,
|
||||||
IconData? activatedIcon,
|
IconData? activatedIcon,
|
||||||
}) {
|
}) {
|
||||||
final status = stat?.status == true;
|
final status = stat?.status == true;
|
||||||
final color = status ? primary : outline;
|
final color = status ? primary : outline;
|
||||||
return TextButton.icon(
|
final iconWidget = Icon(
|
||||||
onPressed: onPressed,
|
|
||||||
icon: Icon(
|
|
||||||
status ? activatedIcon : icon,
|
status ? activatedIcon : icon,
|
||||||
size: 16,
|
size: 16,
|
||||||
color: color,
|
color: color,
|
||||||
),
|
);
|
||||||
|
return TextButton.icon(
|
||||||
|
onPressed: () => onPressed(iconWidget.color!),
|
||||||
|
icon: iconWidget,
|
||||||
style: btnStyle,
|
style: btnStyle,
|
||||||
label: Text(
|
label: Text(
|
||||||
stat?.count != null ? NumUtils.numFormat(stat!.count) : text,
|
stat?.count != null ? NumUtils.numFormat(stat!.count) : text,
|
||||||
@@ -422,7 +423,7 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
|||||||
icon: FontAwesomeIcons.shareFromSquare,
|
icon: FontAwesomeIcons.shareFromSquare,
|
||||||
text: '转发',
|
text: '转发',
|
||||||
stat: forward,
|
stat: forward,
|
||||||
onPressed: () => showModalBottomSheet(
|
onPressed: (_) => showModalBottomSheet(
|
||||||
context: context,
|
context: context,
|
||||||
isScrollControlled: true,
|
isScrollControlled: true,
|
||||||
useSafeArea: true,
|
useSafeArea: true,
|
||||||
@@ -449,7 +450,7 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
|||||||
icon: CustomIcons.share_node,
|
icon: CustomIcons.share_node,
|
||||||
text: '分享',
|
text: '分享',
|
||||||
stat: null,
|
stat: null,
|
||||||
onPressed: () => Utils.shareText(
|
onPressed: (_) => Utils.shareText(
|
||||||
'${HttpString.dynamicShareBaseUrl}/${controller.dynItem.idStr}',
|
'${HttpString.dynamicShareBaseUrl}/${controller.dynItem.idStr}',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -462,8 +463,10 @@ class _DynamicDetailPageState extends CommonDynPageState<DynamicDetailPage> {
|
|||||||
activatedIcon: FontAwesomeIcons.solidThumbsUp,
|
activatedIcon: FontAwesomeIcons.solidThumbsUp,
|
||||||
text: '点赞',
|
text: '点赞',
|
||||||
stat: moduleStat?.like,
|
stat: moduleStat?.like,
|
||||||
onPressed: () => RequestUtils.onLikeDynamic(
|
onPressed: (iconColor) =>
|
||||||
|
RequestUtils.onLikeDynamic(
|
||||||
controller.dynItem,
|
controller.dynItem,
|
||||||
|
iconColor == primary,
|
||||||
() {
|
() {
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
(context as Element).markNeedsBuild();
|
(context as Element).markNeedsBuild();
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import 'package:PiliPlus/models_new/live/live_second_list/data.dart';
|
|||||||
import 'package:PiliPlus/models_new/live/live_second_list/tag.dart';
|
import 'package:PiliPlus/models_new/live/live_second_list/tag.dart';
|
||||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||||
import 'package:PiliPlus/services/account_service.dart';
|
import 'package:PiliPlus/services/account_service.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
||||||
|
import 'package:flutter/widgets.dart' show ScrollController;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class LiveController extends CommonListController with AccountMixin {
|
class LiveController extends CommonListController with AccountMixin {
|
||||||
@@ -32,6 +34,8 @@ class LiveController extends CommonListController with AccountMixin {
|
|||||||
final Rx<Pair<LiveCardList?, LiveCardList?>> topState =
|
final Rx<Pair<LiveCardList?, LiveCardList?>> topState =
|
||||||
Pair<LiveCardList?, LiveCardList?>(first: null, second: null).obs;
|
Pair<LiveCardList?, LiveCardList?>(first: null, second: null).obs;
|
||||||
|
|
||||||
|
final followController = ScrollController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void checkIsEnd(int length) {
|
void checkIsEnd(int length) {
|
||||||
if (count != null && length >= count!) {
|
if (count != null && length >= count!) {
|
||||||
@@ -87,10 +91,11 @@ class LiveController extends CommonListController with AccountMixin {
|
|||||||
page = 1;
|
page = 1;
|
||||||
isEnd = false;
|
isEnd = false;
|
||||||
if (areaIndex.value != 0) {
|
if (areaIndex.value != 0) {
|
||||||
queryTop();
|
queryTop().whenComplete(followController.jumpToTop);
|
||||||
}
|
|
||||||
return queryData();
|
return queryData();
|
||||||
}
|
}
|
||||||
|
return queryData().whenComplete(followController.jumpToTop);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> queryTop() async {
|
Future<void> queryTop() async {
|
||||||
final res = await LiveHttp.liveFeedIndex(pn: page, moduleSelect: true);
|
final res = await LiveHttp.liveFeedIndex(pn: page, moduleSelect: true);
|
||||||
@@ -143,4 +148,10 @@ class LiveController extends CommonListController with AccountMixin {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onChangeAccount(bool isLogin) => onReload();
|
void onChangeAccount(bool isLogin) => onReload();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
followController.dispose();
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ class _LivePageState extends State<LivePage>
|
|||||||
height: 68.0 + textScaler.scale(12),
|
height: 68.0 + textScaler.scale(12),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
scrollDirection: Axis.horizontal,
|
scrollDirection: Axis.horizontal,
|
||||||
|
controller: controller.followController,
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
slivers: [
|
slivers: [
|
||||||
SliverFixedExtentList.builder(
|
SliverFixedExtentList.builder(
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import 'package:PiliPlus/pages/common/common_data_controller.dart';
|
|||||||
import 'package:PiliPlus/services/account_service.dart';
|
import 'package:PiliPlus/services/account_service.dart';
|
||||||
import 'package:PiliPlus/utils/accounts.dart';
|
import 'package:PiliPlus/utils/accounts.dart';
|
||||||
import 'package:PiliPlus/utils/accounts/account.dart';
|
import 'package:PiliPlus/utils/accounts/account.dart';
|
||||||
|
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
||||||
import 'package:PiliPlus/utils/login_utils.dart';
|
import 'package:PiliPlus/utils/login_utils.dart';
|
||||||
import 'package:PiliPlus/utils/storage.dart';
|
import 'package:PiliPlus/utils/storage.dart';
|
||||||
import 'package:PiliPlus/utils/storage_key.dart';
|
import 'package:PiliPlus/utils/storage_key.dart';
|
||||||
@@ -135,6 +136,7 @@ class MineController extends CommonDataController<FavFolderData, FavFolderData>
|
|||||||
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
|
bool customHandleResponse(bool isRefresh, Success<FavFolderData> response) {
|
||||||
favFolderCount = response.response.count;
|
favFolderCount = response.response.count;
|
||||||
loadingState.value = response;
|
loadingState.value = response;
|
||||||
|
scrollController.jumpToTop();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ class _MediaPageState extends CommonPageState<MinePage>
|
|||||||
child: onBuild(
|
child: onBuild(
|
||||||
ListView(
|
ListView(
|
||||||
padding: const .only(bottom: 100),
|
padding: const .only(bottom: 100),
|
||||||
controller: controller.scrollController,
|
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
children: [
|
children: [
|
||||||
_buildUserInfo(theme, secondary),
|
_buildUserInfo(theme, secondary),
|
||||||
@@ -505,6 +504,7 @@ class _MediaPageState extends CommonPageState<MinePage>
|
|||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: ListView.separated(
|
child: ListView.separated(
|
||||||
|
controller: controller.scrollController,
|
||||||
padding: const .only(left: 20, top: 10, right: 20),
|
padding: const .only(left: 20, top: 10, right: 20),
|
||||||
itemCount: response.list.length + (flag ? 1 : 0),
|
itemCount: response.list.length + (flag ? 1 : 0),
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|||||||
@@ -9,14 +9,17 @@ import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
|||||||
import 'package:PiliPlus/services/account_service.dart';
|
import 'package:PiliPlus/services/account_service.dart';
|
||||||
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
import 'package:PiliPlus/utils/extension/scroll_controller_ext.dart';
|
||||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/widgets.dart' show ScrollController;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
|
|
||||||
class PgcController
|
class PgcController
|
||||||
extends CommonListController<List<PgcIndexItem>?, PgcIndexItem>
|
extends CommonListController<List<PgcIndexItem>?, PgcIndexItem>
|
||||||
with AccountMixin {
|
with AccountMixin {
|
||||||
PgcController({required this.tabType});
|
PgcController({required this.tabType})
|
||||||
|
: indexType = tabType == HomeTabType.cinema ? 102 : null;
|
||||||
|
|
||||||
final HomeTabType tabType;
|
final HomeTabType tabType;
|
||||||
|
final int? indexType;
|
||||||
|
|
||||||
late final showPgcTimeline =
|
late final showPgcTimeline =
|
||||||
tabType == HomeTabType.bangumi && Pref.showPgcTimeline;
|
tabType == HomeTabType.bangumi && Pref.showPgcTimeline;
|
||||||
@@ -33,9 +36,6 @@ class PgcController
|
|||||||
if (showPgcTimeline) {
|
if (showPgcTimeline) {
|
||||||
queryPgcTimeline();
|
queryPgcTimeline();
|
||||||
}
|
}
|
||||||
if (accountService.isLogin.value) {
|
|
||||||
followController = ScrollController();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -62,7 +62,7 @@ class PgcController
|
|||||||
late bool followEnd = false;
|
late bool followEnd = false;
|
||||||
late Rx<LoadingState<List<FavPgcItemModel>?>> followState =
|
late Rx<LoadingState<List<FavPgcItemModel>?>> followState =
|
||||||
LoadingState<List<FavPgcItemModel>?>.loading().obs;
|
LoadingState<List<FavPgcItemModel>?>.loading().obs;
|
||||||
ScrollController? followController;
|
final followController = ScrollController();
|
||||||
|
|
||||||
// timeline
|
// timeline
|
||||||
late Rx<LoadingState<List<TimelineResult>?>> timelineState =
|
late Rx<LoadingState<List<TimelineResult>?>> timelineState =
|
||||||
@@ -117,7 +117,7 @@ class PgcController
|
|||||||
followEnd = true;
|
followEnd = true;
|
||||||
}
|
}
|
||||||
followState.value = Success(list);
|
followState.value = Success(list);
|
||||||
followController?.animToTop();
|
followController.jumpToTop();
|
||||||
} else if (followState.value case Success(:final response)) {
|
} else if (followState.value case Success(:final response)) {
|
||||||
final currentList = response!..addAll(list);
|
final currentList = response!..addAll(list);
|
||||||
if (currentList.length >= followCount.value) {
|
if (currentList.length >= followCount.value) {
|
||||||
@@ -135,12 +135,12 @@ class PgcController
|
|||||||
@override
|
@override
|
||||||
Future<LoadingState<List<PgcIndexItem>?>> customGetData() => PgcHttp.pgcIndex(
|
Future<LoadingState<List<PgcIndexItem>?>> customGetData() => PgcHttp.pgcIndex(
|
||||||
page: page,
|
page: page,
|
||||||
indexType: tabType == HomeTabType.cinema ? 102 : null,
|
indexType: indexType,
|
||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onClose() {
|
void onClose() {
|
||||||
followController?.dispose();
|
followController.dispose();
|
||||||
super.onClose();
|
super.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -417,9 +417,7 @@ class _PgcPageState extends State<PgcPage> with AutomaticKeepAliveClientMixin {
|
|||||||
? StyleString.safeSpace
|
? StyleString.safeSpace
|
||||||
: 0,
|
: 0,
|
||||||
),
|
),
|
||||||
child: PgcCardV(
|
child: PgcCardV(item: response[index]),
|
||||||
item: response[index],
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -362,27 +362,29 @@ abstract final class RequestUtils {
|
|||||||
// 动态点赞
|
// 动态点赞
|
||||||
static Future<void> onLikeDynamic(
|
static Future<void> onLikeDynamic(
|
||||||
DynamicItemModel item,
|
DynamicItemModel item,
|
||||||
|
bool uiStatus,
|
||||||
VoidCallback onSuccess,
|
VoidCallback onSuccess,
|
||||||
) async {
|
) async {
|
||||||
feedBack();
|
feedBack();
|
||||||
String dynamicId = item.idStr!;
|
|
||||||
// 1 已点赞 2 不喜欢 0 未操作
|
final like = item.modules.moduleStat?.like;
|
||||||
DynamicStat? like = item.modules.moduleStat?.like;
|
final status = like?.status ?? false;
|
||||||
int count = like?.count ?? 0;
|
|
||||||
bool status = like?.status ?? false;
|
if (status ^ uiStatus) {
|
||||||
int up = status ? 2 : 1;
|
SmartDialog.showToast(status ? '点赞成功' : '取消赞');
|
||||||
final res = await DynamicsHttp.thumbDynamic(dynamicId: dynamicId, up: up);
|
onSuccess();
|
||||||
if (res.isSuccess) {
|
return;
|
||||||
SmartDialog.showToast(!status ? '点赞成功' : '取消赞');
|
|
||||||
if (up == 1) {
|
|
||||||
like
|
|
||||||
?..count = count + 1
|
|
||||||
..status = true;
|
|
||||||
} else {
|
|
||||||
like
|
|
||||||
?..count = count - 1
|
|
||||||
..status = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final res = await DynamicsHttp.thumbDynamic(
|
||||||
|
dynamicId: item.idStr!,
|
||||||
|
up: status ? 2 : 1, // 1 已点赞 2 不喜欢 0 未操作
|
||||||
|
);
|
||||||
|
if (res.isSuccess) {
|
||||||
|
SmartDialog.showToast(status ? '取消赞' : '点赞成功');
|
||||||
|
like
|
||||||
|
?..count = (like.count ?? 0) + (status ? -1 : 1)
|
||||||
|
..status = !status;
|
||||||
onSuccess();
|
onSuccess();
|
||||||
} else {
|
} else {
|
||||||
res.toast();
|
res.toast();
|
||||||
|
|||||||
Reference in New Issue
Block a user