diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart index 1cdc004b..4ab9be17 100644 --- a/lib/pages/live/controller.dart +++ b/lib/pages/live/controller.dart @@ -15,6 +15,8 @@ class LiveController extends CommonListController { queryData(); } + int? count; + // area int? areaId; String? sortType; @@ -29,6 +31,13 @@ class LiveController extends CommonListController { Pair(first: null, second: null).obs; final RxBool isLogin = Accounts.main.isLogin.obs; + @override + void checkIsEnd(int length) { + if (count != null && length >= count!) { + isEnd = true; + } + } + @override List? getDataList(response) { return response.cardList; @@ -38,11 +47,15 @@ class LiveController extends CommonListController { bool customHandleResponse(bool isRefresh, Success response) { if (isRefresh) { if (areaIndex.value == 0) { + if (response.response.hasMore == 0) { + isEnd = true; + } topState.value = Pair( first: response.response.followItem, second: response.response.areaItem, ); } else { + count = response.response.count; newTags = response.response.newTags; if (sortType != null) { tagIndex.value = @@ -69,6 +82,7 @@ class LiveController extends CommonListController { @override Future onRefresh() { + count = null; currentPage = 1; isEnd = false; if (areaIndex.value != 0) { @@ -109,6 +123,7 @@ class LiveController extends CommonListController { areaId = cardLiveItem?.areaV2Id; parentAreaId = cardLiveItem?.areaV2ParentId; + count = null; currentPage = 1; isEnd = false; queryData(); @@ -118,6 +133,7 @@ class LiveController extends CommonListController { tagIndex.value = index; this.sortType = sortType; + count = null; currentPage = 1; isEnd = false; queryData(); diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index 95139635..645360c0 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -147,69 +147,70 @@ class _LivePageState extends CommonPageState childCount: 10, ), ), - Success() => loadingState.response?.isNotEmpty == true - ? SliverMainAxisGroup( - slivers: [ - if (controller.newTags?.isNotEmpty == true) - SliverToBoxAdapter( - child: SelfSizedHorizontalList( - gapSize: 12, - padding: const EdgeInsets.only(bottom: 8), - childBuilder: (index) { - late final item = controller.newTags![index]; - return Obx( - () => SearchText( - fontSize: 13, - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 3, - ), - text: '${item.name}', - bgColor: index == controller.tagIndex.value - ? theme.colorScheme.secondaryContainer - : Colors.transparent, - textColor: index == controller.tagIndex.value - ? theme.colorScheme.onSecondaryContainer - : null, - onTap: (value) { - controller.onSelectTag( - index, - item.sortType, - ); - }, - ), - ); - }, - itemCount: controller.newTags!.length, - ), - ), - SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - mainAxisSpacing: StyleString.cardSpace, - crossAxisSpacing: StyleString.cardSpace, - maxCrossAxisExtent: Grid.smallCardWidth, - childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: MediaQuery.textScalerOf(context).scale(90), - ), - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == loadingState.response!.length - 1) { - controller.onLoadMore(); - } - final item = loadingState.response![index]; - if (item is LiveCardList) { - return LiveCardVApp( - item: item.cardData!.smallCardV1!, - ); - } - return LiveCardVApp(item: item); - }, - childCount: loadingState.response!.length, - ), + Success() => SliverMainAxisGroup( + slivers: [ + if (controller.newTags?.isNotEmpty == true) + SliverToBoxAdapter( + child: SelfSizedHorizontalList( + gapSize: 12, + padding: const EdgeInsets.only(bottom: 8), + childBuilder: (index) { + late final item = controller.newTags![index]; + return Obx( + () => SearchText( + fontSize: 13, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + text: '${item.name}', + bgColor: index == controller.tagIndex.value + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + textColor: index == controller.tagIndex.value + ? theme.colorScheme.onSecondaryContainer + : null, + onTap: (value) { + controller.onSelectTag( + index, + item.sortType, + ); + }, + ), + ); + }, + itemCount: controller.newTags!.length, ), - ], - ) - : HttpError(onReload: controller.onReload), + ), + loadingState.response?.isNotEmpty == true + ? SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + mainAxisSpacing: StyleString.cardSpace, + crossAxisSpacing: StyleString.cardSpace, + maxCrossAxisExtent: Grid.smallCardWidth, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: + MediaQuery.textScalerOf(context).scale(90), + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == loadingState.response!.length - 1) { + controller.onLoadMore(); + } + final item = loadingState.response![index]; + if (item is LiveCardList) { + return LiveCardVApp( + item: item.cardData!.smallCardV1!, + ); + } + return LiveCardVApp(item: item); + }, + childCount: loadingState.response!.length, + ), + ) + : HttpError(onReload: controller.onReload), + ], + ), Error() => HttpError( errMsg: loadingState.errMsg, onReload: controller.onReload, diff --git a/lib/pages/live_area_detail/child/controller.dart b/lib/pages/live_area_detail/child/controller.dart index c24eec20..82adc2d2 100644 --- a/lib/pages/live_area_detail/child/controller.dart +++ b/lib/pages/live_area_detail/child/controller.dart @@ -1,9 +1,13 @@ +import 'dart:math'; + import 'package:PiliPlus/http/live.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart'; import 'package:PiliPlus/models/live/live_second_list/data.dart'; +import 'package:PiliPlus/models/live/live_second_list/tag.dart'; import 'package:PiliPlus/pages/common/common_list_controller.dart'; import 'package:PiliPlus/utils/storage.dart'; +import 'package:get/get.dart'; class LiveAreaChildController extends CommonListController { @@ -11,8 +15,14 @@ class LiveAreaChildController final dynamic areaId; final dynamic parentAreaId; + int? count; + String? sortType; + // tag + final RxInt tagIndex = 0.obs; + List? newTags; + final isLogin = Accounts.main.isLogin; @override @@ -21,8 +31,19 @@ class LiveAreaChildController queryData(); } + @override + void checkIsEnd(int length) { + if (count != null && length >= count!) { + isEnd = true; + } + } + @override List? getDataList(LiveSecondData response) { + count = response.count; + newTags = response.newTags; + tagIndex.value = + max(0, newTags?.indexWhere((e) => e.sortType == sortType) ?? 0); return response.cardList; } @@ -35,4 +56,11 @@ class LiveAreaChildController parentAreaId: parentAreaId, sortType: sortType, ); + + void onSelectTag(int index, String? sortType) { + tagIndex.value = index; + this.sortType = sortType; + + onRefresh(); + } } diff --git a/lib/pages/live_area_detail/child/view.dart b/lib/pages/live_area_detail/child/view.dart index 5334d999..2207fb40 100644 --- a/lib/pages/live_area_detail/child/view.dart +++ b/lib/pages/live_area_detail/child/view.dart @@ -2,10 +2,12 @@ import 'package:PiliPlus/common/constants.dart'; import 'package:PiliPlus/common/skeleton/video_card_v.dart'; import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart'; import 'package:PiliPlus/common/widgets/refresh_indicator.dart'; +import 'package:PiliPlus/common/widgets/self_sized_horizontal_list.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/live/live_feed_index/card_data_list_item.dart'; import 'package:PiliPlus/pages/live/widgets/live_item_app.dart'; import 'package:PiliPlus/pages/live_area_detail/child/controller.dart'; +import 'package:PiliPlus/pages/search/widgets/search_text.dart'; import 'package:PiliPlus/utils/grid.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; @@ -34,6 +36,7 @@ class _LiveAreaChildPageState extends State @override Widget build(BuildContext context) { super.build(context); + final ThemeData theme = Theme.of(context); return refreshIndicator( onRefresh: _controller.onRefresh, child: CustomScrollView( @@ -45,14 +48,16 @@ class _LiveAreaChildPageState extends State top: StyleString.safeSpace, bottom: MediaQuery.paddingOf(context).bottom + 80, ), - sliver: Obx(() => _buildBody(_controller.loadingState.value)), + sliver: + Obx(() => _buildBody(theme, _controller.loadingState.value)), ), ], ), ); } - Widget _buildBody(LoadingState?> loadingState) { + Widget _buildBody( + ThemeData theme, LoadingState?> loadingState) { return switch (loadingState) { Loading() => SliverGrid( gridDelegate: SliverGridDelegateWithExtentAndRatio( @@ -69,28 +74,69 @@ class _LiveAreaChildPageState extends State childCount: 10, ), ), - Success() => loadingState.response?.isNotEmpty == true - ? SliverGrid( - gridDelegate: SliverGridDelegateWithExtentAndRatio( - mainAxisSpacing: StyleString.cardSpace, - crossAxisSpacing: StyleString.cardSpace, - maxCrossAxisExtent: Grid.smallCardWidth, - childAspectRatio: StyleString.aspectRatio, - mainAxisExtent: MediaQuery.textScalerOf(context).scale(90), + Success() => SliverMainAxisGroup( + slivers: [ + if (_controller.newTags?.isNotEmpty == true) + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(bottom: 12), + child: SelfSizedHorizontalList( + gapSize: 12, + childBuilder: (index) { + late final item = _controller.newTags![index]; + return Obx( + () => SearchText( + fontSize: 14, + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 3, + ), + text: '${item.name}', + bgColor: index == _controller.tagIndex.value + ? theme.colorScheme.secondaryContainer + : Colors.transparent, + textColor: index == _controller.tagIndex.value + ? theme.colorScheme.onSecondaryContainer + : null, + onTap: (value) { + _controller.onSelectTag( + index, + item.sortType, + ); + }, + ), + ); + }, + itemCount: _controller.newTags!.length, + ), + ), ), - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == loadingState.response!.length - 1) { - _controller.onLoadMore(); - } - return LiveCardVApp(item: loadingState.response![index]); - }, - childCount: loadingState.response!.length, - ), - ) - : HttpError( - onReload: _controller.onReload, - ), + loadingState.response?.isNotEmpty == true + ? SliverGrid( + gridDelegate: SliverGridDelegateWithExtentAndRatio( + mainAxisSpacing: StyleString.cardSpace, + crossAxisSpacing: StyleString.cardSpace, + maxCrossAxisExtent: Grid.smallCardWidth, + childAspectRatio: StyleString.aspectRatio, + mainAxisExtent: + MediaQuery.textScalerOf(context).scale(90), + ), + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == loadingState.response!.length - 1) { + _controller.onLoadMore(); + } + return LiveCardVApp( + item: loadingState.response![index]); + }, + childCount: loadingState.response!.length, + ), + ) + : HttpError( + onReload: _controller.onReload, + ), + ], + ), Error() => HttpError( errMsg: loadingState.errMsg, onReload: _controller.onReload, diff --git a/lib/pages/live_area_detail/view.dart b/lib/pages/live_area_detail/view.dart index 581b33f2..b5f167a0 100644 --- a/lib/pages/live_area_detail/view.dart +++ b/lib/pages/live_area_detail/view.dart @@ -1,3 +1,5 @@ +import 'package:PiliPlus/common/widgets/button/icon_button.dart'; +import 'package:PiliPlus/common/widgets/image/network_img_layer.dart'; import 'package:PiliPlus/common/widgets/scroll_physics.dart'; import 'package:PiliPlus/http/loading_state.dart'; import 'package:PiliPlus/models/live/live_area_list/area_item.dart'; @@ -59,27 +61,48 @@ class _LiveAreaDetailPageState extends State { ? DefaultTabController( initialIndex: _controller.initialIndex, length: loadingState.response!.length, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( - isScrollable: true, - tabAlignment: TabAlignment.start, - tabs: loadingState.response! - .map((e) => Tab(text: e.name ?? '')) - .toList(), - ), - Expanded( - child: tabBarView( - children: loadingState.response! - .map((e) => LiveAreaChildPage( - areaId: e.id, - parentAreaId: e.parentId, - )) - .toList(), - ), - ), - ], + child: Builder( + builder: (context) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: TabBar( + dividerHeight: 0, + dividerColor: Colors.transparent, + isScrollable: true, + tabAlignment: TabAlignment.start, + tabs: loadingState.response! + .map((e) => Tab(text: e.name ?? '')) + .toList(), + ), + ), + iconButton( + context: context, + icon: Icons.menu, + bgColor: Colors.transparent, + onPressed: () { + _showTags(context, theme, loadingState.response!); + }, + ), + ], + ), + const Divider(height: 1), + Expanded( + child: tabBarView( + children: loadingState.response! + .map((e) => LiveAreaChildPage( + areaId: e.id, + parentAreaId: e.parentId, + )) + .toList(), + ), + ), + ], + ); + }, ), ) : LiveAreaChildPage( @@ -92,4 +115,105 @@ class _LiveAreaDetailPageState extends State { ), }; } + + Widget _tagItem({ + required ThemeData theme, + required AreaItem item, + required VoidCallback onTap, + }) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: onTap, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + NetworkImgLayer( + width: 45, + height: 45, + src: item.pic, + type: 'emote', + ), + const SizedBox(height: 4), + Text( + item.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle(fontSize: 12), + ), + ], + ), + ); + } + + void _showTags(BuildContext context, ThemeData theme, List list) { + showModalBottomSheet( + context: context, + useSafeArea: true, + isScrollControlled: true, + builder: (_) { + return DraggableScrollableSheet( + minChildSize: 0, + maxChildSize: 1, + initialChildSize: 1, + snap: true, + expand: false, + snapSizes: const [1], + builder: (_, scrollController) { + return NotificationListener( + onNotification: (notification) { + if (notification.extent <= 1e-5) { + Get.back(); + } + return false; + }, + child: Column( + children: [ + AppBar( + centerTitle: true, + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + title: Text(widget.parentName), + actions: [ + IconButton( + onPressed: Get.back, + icon: const Icon(Icons.clear), + ), + const SizedBox(width: 12), + ], + ), + Expanded( + child: GridView.builder( + controller: scrollController, + padding: EdgeInsets.only( + top: 12, + bottom: MediaQuery.paddingOf(context).bottom + 80, + ), + itemCount: list.length, + gridDelegate: + const SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 100, + mainAxisSpacing: 10, + crossAxisSpacing: 10, + mainAxisExtent: 80, + ), + itemBuilder: (_, index) { + return _tagItem( + theme: theme, + item: list[index], + onTap: () { + Get.back(); + DefaultTabController.of(context).index = index; + }, + ); + }, + ), + ), + ], + ), + ); + }, + ); + }, + ); + } }