From 8e0eeb5f5db5f173679c314c52049e3720070df1 Mon Sep 17 00:00:00 2001 From: Hamlet Jiang Su Date: Tue, 6 Jan 2026 18:20:10 -0800 Subject: [PATCH] fix: fix grey screen when navigating to comment --- lib/src/app/utils/navigation.dart | 4 +++ .../widgets/profile_modal_body.dart | 8 +++-- .../post/presentation/pages/post_page.dart | 34 +++++++++++++------ .../widgets/post_body/post_body.dart | 6 ++-- .../widgets/post_body/post_body_title.dart | 20 +++++++---- 5 files changed, 49 insertions(+), 23 deletions(-) diff --git a/lib/src/app/utils/navigation.dart b/lib/src/app/utils/navigation.dart index 0ccfed8c5..b14c2a0dc 100644 --- a/lib/src/app/utils/navigation.dart +++ b/lib/src/app/utils/navigation.dart @@ -281,11 +281,15 @@ Future navigateToComment(BuildContext context, ThunderComment comment) asy canSwipe: !kIsWeb && Platform.isIOS || gestureCubit.state.enableFullScreenSwipeNavigationGesture, canOnlySwipeFromEdge: disableFullPageSwipe(isUserLoggedIn: profileBloc.state.isLoggedIn, state: gestureCubit.state, isPostPage: true) || !gestureCubit.state.enableFullScreenSwipeNavigationGesture, builder: (context) { + final postNavigationCubit = PostNavigationCubit(); + postNavigationCubit.setHighlightedCommentId(comment.id); + return MultiBlocProvider( providers: [ BlocProvider.value(value: profileBloc), BlocProvider.value(value: thunderBloc), BlocProvider(create: (context) => PostBloc(account: account)), + BlocProvider.value(value: postNavigationCubit), ], child: FutureBuilder( future: getPostFromComment(comment, account), diff --git a/lib/src/features/account/presentation/widgets/profile_modal_body.dart b/lib/src/features/account/presentation/widgets/profile_modal_body.dart index 1cf1ebe80..e03858aec 100644 --- a/lib/src/features/account/presentation/widgets/profile_modal_body.dart +++ b/lib/src/features/account/presentation/widgets/profile_modal_body.dart @@ -767,7 +767,9 @@ class _ProfileSelectState extends State { count: 1, timeout: 5, ).stream.first; - setState(() => account.latency = pingData.response?.time); + if (mounted) { + setState(() => account.latency = pingData.response?.time); + } } } } @@ -820,7 +822,9 @@ class _ProfileSelectState extends State { count: 1, timeout: 5, ).stream.first; - setState(() => anonymousInstanceExtended.latency = pingData.response?.time); + if (mounted) { + setState(() => anonymousInstanceExtended.latency = pingData.response?.time); + } } } diff --git a/lib/src/features/post/presentation/pages/post_page.dart b/lib/src/features/post/presentation/pages/post_page.dart index 47e9ff4b5..1f9245628 100644 --- a/lib/src/features/post/presentation/pages/post_page.dart +++ b/lib/src/features/post/presentation/pages/post_page.dart @@ -191,19 +191,26 @@ class _PostPageState extends State { // If it is not visible, scroll to it. final navigationState = context.read().state; final highlightedCommentId = navigationState.highlightedCommentId; - final highlightedCommentIndex = state.comments.indexWhere((element) => element.comment!.id == highlightedCommentId); + final highlightedCommentIndex = state.comments.indexWhere((element) => element.comment?.id == highlightedCommentId); if (widget.highlightedCommentId != null && listController.isAttached && highlightedCommentIndex != -1) { final visibleRange = listController.visibleRange; - - if (visibleRange != null && (highlightedCommentIndex < (visibleRange.$1 + 3) || highlightedCommentIndex > (visibleRange.$2 - 3))) { - listController.animateToItem( - index: highlightedCommentIndex, - scrollController: scrollController, - alignment: 0, - duration: (estimatedDistance) => const Duration(milliseconds: 250), - curve: (estimatedDistance) => Curves.easeInOutCubicEmphasized, - ); + // Add 1 to account for the placeholder widget at index 0 + final adjustedIndex = highlightedCommentIndex + 1; + + if (visibleRange != null && (adjustedIndex < (visibleRange.$1 + 3) || adjustedIndex > (visibleRange.$2 - 3))) { + // Defer animation to after the frame is rendered so that list extents are available + WidgetsBinding.instance.addPostFrameCallback((_) { + if (listController.isAttached) { + listController.animateToItem( + index: adjustedIndex, + scrollController: scrollController, + alignment: 0, + duration: (estimatedDistance) => const Duration(milliseconds: 250), + curve: (estimatedDistance) => Curves.easeInOutCubicEmphasized, + ); + } + }); } } @@ -318,7 +325,12 @@ class _PostPageState extends State { } final commentNode = state.comments[index - 1]; - final comment = commentNode.comment!; + final comment = commentNode.comment; + + // Skip rendering if comment is null + if (comment == null) { + return const SizedBox.shrink(); + } final collapsedComments = context.read().state.collapsedComments; diff --git a/lib/src/features/post/presentation/widgets/post_body/post_body.dart b/lib/src/features/post/presentation/widgets/post_body/post_body.dart index d1d511cca..9d8ded335 100644 --- a/lib/src/features/post/presentation/widgets/post_body/post_body.dart +++ b/lib/src/features/post/presentation/widgets/post_body/post_body.dart @@ -132,7 +132,7 @@ class _PostBodyState extends State with SingleTickerProviderStateMixin final contentFontSizeScale = context.select((cubit) => cubit.state.contentFontSizeScale); final post = widget.post; - final media = post.media.first; + final media = post.media.firstOrNull; List children = [ PostBodyTitle( @@ -146,7 +146,7 @@ class _PostBodyState extends State with SingleTickerProviderStateMixin ), ]; - if (postBodyViewType != PostBodyViewType.condensed && media.mediaType != MediaType.text) { + if (postBodyViewType != PostBodyViewType.condensed && media != null && media.mediaType != MediaType.text) { children.add( Expandable( controller: expandableController, @@ -218,7 +218,7 @@ class _PostBodyState extends State with SingleTickerProviderStateMixin unreadCommentCount: post.unreadComments, dateTime: post.updated?.toIso8601String() ?? post.published.toIso8601String(), hasBeenEdited: post.updated != null, - url: media.mediaType != MediaType.image ? post.url : null, + url: media?.mediaType != MediaType.image ? post.url : null, ), ); diff --git a/lib/src/features/post/presentation/widgets/post_body/post_body_title.dart b/lib/src/features/post/presentation/widgets/post_body/post_body_title.dart index c7d5a96cf..ca85e5de8 100644 --- a/lib/src/features/post/presentation/widgets/post_body/post_body_title.dart +++ b/lib/src/features/post/presentation/widgets/post_body/post_body_title.dart @@ -55,14 +55,14 @@ class PostBodyTitle extends StatelessWidget { Widget _buildCondensedTitle(BuildContext context) { final showThumbnailPreviewOnRight = context.select((cubit) => cubit.state.showThumbnailPreviewOnRight); - final media = post.media.first; + final media = post.media.firstOrNull; return Padding( - padding: EdgeInsets.symmetric(horizontal: media.mediaType == MediaType.text || showThumbnailPreviewOnRight ? 12.0 : 0.0).copyWith(top: 8.0, right: 8.0), + padding: EdgeInsets.symmetric(horizontal: media?.mediaType == MediaType.text || media == null || showThumbnailPreviewOnRight ? 12.0 : 0.0).copyWith(top: 8.0, right: 8.0), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (media.mediaType != MediaType.text && !showThumbnailPreviewOnRight) CompactThumbnailPreview(media: media, postId: post.id), + if (media != null && media.mediaType != MediaType.text && !showThumbnailPreviewOnRight) CompactThumbnailPreview(media: media, postId: post.id), Expanded( child: Column( mainAxisSize: MainAxisSize.min, @@ -76,7 +76,7 @@ class PostBodyTitle extends StatelessWidget { ], ), ), - if (media.mediaType != MediaType.text && showThumbnailPreviewOnRight) CompactThumbnailPreview(media: media, postId: post.id), + if (media != null && media.mediaType != MediaType.text && showThumbnailPreviewOnRight) CompactThumbnailPreview(media: media, postId: post.id), _buildExpandButton(context, postBodyViewType), ], ), @@ -125,10 +125,11 @@ class PostBodyTitle extends StatelessWidget { Widget _buildExpandButton(BuildContext context, PostBodyViewType postBodyViewType) { final l10n = GlobalContext.l10n; - final mediaType = post.media.first.mediaType; + final mediaType = post.media.firstOrNull?.mediaType; final hasPostBody = post.body?.isNotEmpty == true; if (mediaType == MediaType.text && !hasPostBody) return const SizedBox.shrink(); + if (mediaType == null && !hasPostBody) return const SizedBox.shrink(); if (postBodyViewType == PostBodyViewType.condensed && !hasPostBody) return const SizedBox.shrink(); return IconButton( @@ -189,6 +190,11 @@ class _PostBodyAuthorCommunityMetadataState extends State((cubit) => cubit.state.postBodyShowUserInstance); final postBodyShowCommunityInstance = context.select((cubit) => cubit.state.postBodyShowCommunityInstance); @@ -197,7 +203,7 @@ class _PostBodyAuthorCommunityMetadataState extends State