From bf50222eb058cac8b1df1e13a691de1b8f892bce Mon Sep 17 00:00:00 2001 From: w568w <1278297578@qq.com> Date: Fri, 3 Jan 2025 21:51:11 +0800 Subject: [PATCH 1/3] Revert "fix: now when user pulls to refresh while in "only this person" mode, it will automatically quit." This reverts commit 8f88a74879631b606de66ddf5445fc63dc5cb379. --- lib/page/forum/hole_detail.dart | 191 ++++++++++++++++---------------- 1 file changed, 93 insertions(+), 98 deletions(-) diff --git a/lib/page/forum/hole_detail.dart b/lib/page/forum/hole_detail.dart index 9a2afdbd..30014a80 100644 --- a/lib/page/forum/hole_detail.dart +++ b/lib/page/forum/hole_detail.dart @@ -68,7 +68,7 @@ String preprocessContentForDisplay(String content) { if (element is UrlElement) { // Only add tag if tag has not yet been added. if (RegExp("\\[.*?\\]\\(${RegExp.escape(element.url)}\\)") - .hasMatch(content) || + .hasMatch(content) || RegExp("\\[.*?${RegExp.escape(element.url)}.*?\\]\\(http.*?\\)") .hasMatch(content)) { result += element.url; @@ -121,7 +121,7 @@ class BBSPostDetailState extends State { OTFloor? locateFloor; final PagedListViewController _listViewController = - PagedListViewController(); + PagedListViewController(); /// Reload/load the (new) content and set the [_content] future. Future?> _loadContent(int page) async { @@ -141,7 +141,7 @@ class BBSPostDetailState extends State { .loadFloors(hole, startFloor: page * Constant.POST_COUNT_PER_PAGE), Search(keyword: var searchKeyword) => await ForumRepository.getInstance() .loadSearchResults(searchKeyword, - startFloor: _listViewController.length()), + startFloor: _listViewController.length()), MyReplies() => await ForumRepository.getInstance() .loadUserFloors(startFloor: _listViewController.length()), PunishmentHistory() => await loadPunishmentHistory(page), @@ -203,10 +203,7 @@ class BBSPostDetailState extends State { } /// Refresh the list view. - Future refreshListView({bool scrollToEnd = false, required selectedPerson, required VoidCallback func}) async { - if (!scrollToEnd && selectedPerson != null) { - func(); - } + Future refreshListView({bool scrollToEnd = false}) async { _allDataLoaded = false; await _listViewController.notifyUpdate(queueDataClear: true); @@ -250,7 +247,7 @@ class BBSPostDetailState extends State { } // scroll to end. _listViewController.scheduleLoadedCallback( - () async => await _listViewController.scrollToEnd(), + () async => await _listViewController.scrollToEnd(), rebuild: true); shouldScrollToEnd = false; } catch (_) { @@ -276,7 +273,7 @@ class BBSPostDetailState extends State { padding: const EdgeInsets.only(bottom: 16), child: switch (_renderModel) { Normal(hole: var hole) when (hole.view ?? -1) >= 0 => - Text(S.of(context).view_count(hole.view.toString())), + Text(S.of(context).view_count(hole.view.toString())), _ => Text(S.of(context).end_reached), }, ), @@ -284,11 +281,11 @@ class BBSPostDetailState extends State { // Only show empty message when searching, for now. emptyBuilder: switch (_renderModel) { Search() => (context) => Center( - child: Padding( - padding: const EdgeInsets.all(16), - child: Text(S.of(context).no_data), - ), - ), + child: Padding( + padding: const EdgeInsets.all(16), + child: Text(S.of(context).no_data), + ), + ), _ => null, }, ); @@ -309,7 +306,7 @@ class BBSPostDetailState extends State { ), trailingActions: [ if (_renderModel - case Normal(hole: var hole, selectedPerson: var selectedPerson)) ...[ + case Normal(hole: var hole, selectedPerson: var selectedPerson)) ...[ _buildSubscribeActionButton(), _buildFavoredActionButton(), PlatformIconButton( @@ -320,7 +317,7 @@ class BBSPostDetailState extends State { onPressed: () async { if (await OTEditor.createNewReply( context, hole.hole_id, null)) { - refreshListView(scrollToEnd: true, selectedPerson: null, func: (){}); + refreshListView(scrollToEnd: true); } }, ), @@ -412,8 +409,8 @@ class BBSPostDetailState extends State { decoration: _backgroundImage == null ? null : BoxDecoration( - image: DecorationImage( - image: _backgroundImage!, fit: BoxFit.cover)), + image: DecorationImage( + image: _backgroundImage!, fit: BoxFit.cover)), child: switch (_renderModel) { Normal() => RefreshIndicator( edgeOffset: MediaQuery.of(context).padding.top, @@ -421,9 +418,7 @@ class BBSPostDetailState extends State { backgroundColor: Theme.of(context).dialogBackgroundColor, onRefresh: () async { HapticFeedback.mediumImpact(); - await refreshListView(selectedPerson: (_renderModel as Normal).selectedPerson, func: () { - (_renderModel as Normal).selectedPerson = null; - }); + await refreshListView(); }, child: pagedListView), _ => pagedListView, @@ -476,8 +471,8 @@ class BBSPostDetailState extends State { bool? isFavored = snapshot.data; return isFavored! ? Icon(PlatformX.isMaterial(context) - ? Icons.star - : CupertinoIcons.star_fill) + ? Icons.star + : CupertinoIcons.star_fill) : notFavoredIcon; }, errorBuilder: () => Icon( @@ -491,10 +486,10 @@ class BBSPostDetailState extends State { setState(() => normalModel.isFavored = !normalModel.isFavored!); await ForumRepository.getInstance() .setFavorite( - normalModel.isFavored! - ? SetStatusMode.ADD - : SetStatusMode.DELETE, - normalModel.hole.hole_id) + normalModel.isFavored! + ? SetStatusMode.ADD + : SetStatusMode.DELETE, + normalModel.hole.hole_id) .onError((dynamic error, stackTrace) { Noticing.showNotice(context, error.toString(), title: S.of(context).operation_failed, useSnackBar: false); @@ -520,8 +515,8 @@ class BBSPostDetailState extends State { bool? isSubscribed = snapshot.data; return isSubscribed! ? Icon(PlatformX.isMaterial(context) - ? Icons.visibility - : CupertinoIcons.eye) + ? Icons.visibility + : CupertinoIcons.eye) : notSubscribedIcon; }, errorBuilder: () => Icon( @@ -535,10 +530,10 @@ class BBSPostDetailState extends State { setState(() => normalModel.isSubscribed = !normalModel.isSubscribed!); await ForumRepository.getInstance() .setSubscription( - normalModel.isSubscribed! - ? SetStatusMode.ADD - : SetStatusMode.DELETE, - normalModel.hole.hole_id) + normalModel.isSubscribed! + ? SetStatusMode.ADD + : SetStatusMode.DELETE, + normalModel.hole.hole_id) .onError((dynamic error, stackTrace) { Noticing.showNotice(context, error.toString(), title: S.of(context).operation_failed, useSnackBar: false); @@ -604,9 +599,9 @@ class BBSPostDetailState extends State { if (hide != null) { int? result = hide ? await ForumRepository.getInstance() - .adminDeleteHole(e.hole_id) + .adminDeleteHole(e.hole_id) : await ForumRepository.getInstance() - .adminUndeleteHole(e.hole_id); + .adminUndeleteHole(e.hole_id); if (result != null && result < 300 && mounted) { Noticing.showMaterialNotice( context, S.of(context).operation_successful); @@ -691,7 +686,7 @@ class BBSPostDetailState extends State { .getDivisions() .firstWhere( (element) => element.division_id == hole.division_id, - orElse: () => OTDivision(hole.division_id, '', '', null)); + orElse: () => OTDivision(hole.division_id, '', '', null)); List buildDivisionOptionsList(BuildContext cxt) { List list = []; @@ -725,21 +720,21 @@ class BBSPostDetailState extends State { .isNotEmpty) { selectedDivision = (await showPlatformModalSheet( - context: context, - builder: (BuildContext context) { - final Widget content = Padding( - padding: const EdgeInsets.all(16.0), - child: ListView( - shrinkWrap: true, - primary: false, - children: - buildDivisionOptionsList( - context))); - return PlatformX.isCupertino(context) - ? SafeArea( - child: Card(child: content)) - : SafeArea(child: content); - })) ?? + context: context, + builder: (BuildContext context) { + final Widget content = Padding( + padding: const EdgeInsets.all(16.0), + child: ListView( + shrinkWrap: true, + primary: false, + children: + buildDivisionOptionsList( + context))); + return PlatformX.isCupertino(context) + ? SafeArea( + child: Card(child: content)) + : SafeArea(child: content); + })) ?? selectedDivision; setState(() {}); } @@ -771,7 +766,7 @@ class BBSPostDetailState extends State { if (confirmChanged ?? false) { int? result = await ForumRepository.getInstance() .adminUpdateTagAndDivision( - newTagsList, hole.hole_id, selectedDivision.division_id); + newTagsList, hole.hole_id, selectedDivision.division_id); if (result != null && result < 300 && mounted) { Noticing.showMaterialNotice( context, S.of(context).operation_successful); @@ -814,9 +809,9 @@ class BBSPostDetailState extends State { menuContext: menuContext, onPressed: () async { if (await Noticing.showConfirmationDialog(context, - S.of(context).about_to_delete_floor(e.floor_id ?? "null"), - title: S.of(context).are_you_sure, - isConfirmDestructive: true) == + S.of(context).about_to_delete_floor(e.floor_id ?? "null"), + title: S.of(context).are_you_sure, + isConfirmDestructive: true) == true) { if (!context.mounted) return; try { @@ -958,14 +953,14 @@ class BBSPostDetailState extends State { PlatformContextMenuItem( onPressed: () async { if (await Noticing.showConfirmationDialog( - context, S.of(context).are_you_sure, - isConfirmDestructive: true) == + context, S.of(context).are_you_sure, + isConfirmDestructive: true) == true) { final reason = await Noticing.showInputDialog( context, S.of(context).input_reason); await multiExecution( _selectedFloors, - (floor) async => await ForumRepository.getInstance() + (floor) async => await ForumRepository.getInstance() .adminDeleteFloor(floor.floor_id, reason)); } }, @@ -982,7 +977,7 @@ class BBSPostDetailState extends State { } await multiExecution( _selectedFloors, - (floor) async => await ForumRepository.getInstance() + (floor) async => await ForumRepository.getInstance() .adminAddSpecialTag(tag, floor.floor_id)); }, menuContext: menuContext, @@ -997,9 +992,9 @@ class BBSPostDetailState extends State { } await multiExecution( _selectedFloors, - (floor) async => await ForumRepository.getInstance() + (floor) async => await ForumRepository.getInstance() .adminFoldFloor( - reason.isEmpty ? [] : [reason], floor.floor_id)); + reason.isEmpty ? [] : [reason], floor.floor_id)); }, menuContext: menuContext, child: Text(S.of(context).fold_floor), @@ -1033,7 +1028,7 @@ class BBSPostDetailState extends State { {bool isNested = false}) { if (_renderModel case Normal(selectedPerson: var selectedPerson, hole: _)) { if (selectedPerson != null && - floor.anonyname != selectedPerson) { + floor.anonyname != selectedPerson) { return nil; } } @@ -1043,16 +1038,16 @@ class BBSPostDetailState extends State { List? result = switch (_renderModel) { Normal(hole: var hole) => await ForumRepository.getInstance() .loadFloors(hole, - startFloor: pageIndex * Constant.POST_COUNT_PER_PAGE), + startFloor: pageIndex * Constant.POST_COUNT_PER_PAGE), Search(keyword: var searchKeyword) => - await ForumRepository.getInstance().loadSearchResults(searchKeyword, - startFloor: pageIndex * Constant.POST_COUNT_PER_PAGE), + await ForumRepository.getInstance().loadSearchResults(searchKeyword, + startFloor: pageIndex * Constant.POST_COUNT_PER_PAGE), MyReplies() => (await ForumRepository.getInstance().loadUserFloors( startFloor: pageIndex * Constant.POST_COUNT_PER_PAGE)), PunishmentHistory() => - (await ForumRepository.getInstance().getPunishmentHistory()) - ?.map((e) => e.floor!) - .toList(), + (await ForumRepository.getInstance().getPunishmentHistory()) + ?.map((e) => e.floor!) + .toList(), }; if (result == null || result.isEmpty) { @@ -1090,40 +1085,40 @@ class BBSPostDetailState extends State { }, onTap: _multiSelectMode ? () { - // If we are in multi-select mode, we should (un)select the floor. - setState(() { - if (_selectedFloors.contains(floor)) { - _selectedFloors.remove(floor); - } else if (floor.floor_id != null) { - _selectedFloors.add(floor); - } - }); - } - : () async { - switch (_renderModel) { - case Normal(hole: var hole): - int? replyId; - // Set the replyId to null when tapping on the first reply. - if (hole.floors?.first_floor?.floor_id != floor.floor_id) { - replyId = floor.floor_id; - ForumRepository.getInstance().cacheFloor(floor); - } - if (await OTEditor.createNewReply( - context, hole.hole_id, replyId)) { - await refreshListView(scrollToEnd: true, selectedPerson: null, func: (){}); + // If we are in multi-select mode, we should (un)select the floor. + setState(() { + if (_selectedFloors.contains(floor)) { + _selectedFloors.remove(floor); + } else if (floor.floor_id != null) { + _selectedFloors.add(floor); + } + }); } - break; - default: - await OTFloorMentionWidget.jumpToFloorInNewPage( - context, floor); - } - }, + : () async { + switch (_renderModel) { + case Normal(hole: var hole): + int? replyId; + // Set the replyId to null when tapping on the first reply. + if (hole.floors?.first_floor?.floor_id != floor.floor_id) { + replyId = floor.floor_id; + ForumRepository.getInstance().cacheFloor(floor); + } + if (await OTEditor.createNewReply( + context, hole.hole_id, replyId)) { + await refreshListView(scrollToEnd: true); + } + break; + default: + await OTFloorMentionWidget.jumpToFloorInNewPage( + context, floor); + } + }, onTapImage: (String? url, Object heroTag) { final int length = _listViewController.length(); smartNavigatorPush(context, '/image/detail', arguments: { 'preview_url': url, 'hd_url': - ForumRepository.getInstance().extractHighDefinitionImageUrl(url!), + ForumRepository.getInstance().extractHighDefinitionImageUrl(url!), 'hero_tag': heroTag, 'image_list': extractAllImages(), 'loader': loadPageImage, @@ -1219,7 +1214,7 @@ class Normal extends RenderModel { Future isHoleFavorite() async { if (isFavored != null) return isFavored!; final List? favorites = - await (ForumRepository.getInstance().getFavoriteHoleId()); + await (ForumRepository.getInstance().getFavoriteHoleId()); isFavored = favorites!.any((elementId) => elementId == hole.hole_id); return isFavored!; } @@ -1227,7 +1222,7 @@ class Normal extends RenderModel { Future isHoleSubscribed() async { if (isSubscribed != null) return isSubscribed!; final List? subscriptions = - await (ForumRepository.getInstance().getSubscribedHoleId()); + await (ForumRepository.getInstance().getSubscribedHoleId()); isSubscribed = subscriptions!.any((elementId) => elementId == hole.hole_id); return isSubscribed!; } From 22e00c4d0d9285d67ca5d1bf0b79cc601ca0cb76 Mon Sep 17 00:00:00 2001 From: "AInfinity_LilacDream." <1977741520@qq.com> Date: Sat, 4 Jan 2025 19:57:20 +0800 Subject: [PATCH 2/3] fix: now when users pull to refresh under 'only this person' mode, it will automatically quit. --- lib/page/forum/hole_detail.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/page/forum/hole_detail.dart b/lib/page/forum/hole_detail.dart index 30014a80..7230abb0 100644 --- a/lib/page/forum/hole_detail.dart +++ b/lib/page/forum/hole_detail.dart @@ -418,6 +418,7 @@ class BBSPostDetailState extends State { backgroundColor: Theme.of(context).dialogBackgroundColor, onRefresh: () async { HapticFeedback.mediumImpact(); + (_renderModel as Normal).selectedPerson = null; await refreshListView(); }, child: pagedListView), From 8ffd00379d0219e3613e896643cae6f9f4b230ff Mon Sep 17 00:00:00 2001 From: "AInfinity_LilacDream." <1977741520@qq.com> Date: Sun, 5 Jan 2025 01:12:59 +0800 Subject: [PATCH 3/3] fix: add some comments to explain the function of code; when showing only DZ, the state will not be reset since the first floor is always on the first page. --- lib/page/forum/hole_detail.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/page/forum/hole_detail.dart b/lib/page/forum/hole_detail.dart index 7230abb0..9c14a758 100644 --- a/lib/page/forum/hole_detail.dart +++ b/lib/page/forum/hole_detail.dart @@ -418,7 +418,14 @@ class BBSPostDetailState extends State { backgroundColor: Theme.of(context).dialogBackgroundColor, onRefresh: () async { HapticFeedback.mediumImpact(); - (_renderModel as Normal).selectedPerson = null; + + // when users pull to refresh under "only this person" mode, + // the mode should be quited since if the floor is deep + // the initial request won't fetch them, and the page will be blank. + if ((_renderModel as Normal).selectedPerson != + (_renderModel as Normal).hole.floors?.first_floor?.anonyname) { + (_renderModel as Normal).selectedPerson = null; + } await refreshListView(); }, child: pagedListView),