[READ ONLY MIRROR] Open Source TikTok alternative built on AT Protocol github.com/sprksocial/client
flutter atproto video dart
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

better spark comments (#62)

* better loading state for comments page

* fix state thing

* better input + click to go to profile

* replies working again

---------

Co-authored-by: Jean Carlo Polo <vaniapolo@gmail.com>

authored by

Davi Rodrigues
Jean Carlo Polo
and committed by
GitHub
da7bd407 43d57936

+198 -231
+1 -6
lib/src/core/network/atproto/data/repositories/feed_repository_impl.dart
··· 105 105 for (final post in posts) { 106 106 try { 107 107 final postView = PostView.fromJson(post); 108 - // Only add posts that have supported media (video or images) 109 - if (postView.hasSupportedMedia) { 110 - postViews.add(postView); 111 - } else { 112 - _logger.d('Filtered out post with unsupported embed type: ${postView.uri}'); 113 - } 108 + postViews.add(postView); 114 109 } catch (e) { 115 110 _logger.w('Failed to parse post, skipping: $e'); 116 111 // Skip posts that fail to parse instead of crashing
+29 -37
lib/src/features/comments/providers/comments_page_provider.dart
··· 12 12 @riverpod 13 13 class CommentsPage extends _$CommentsPage { 14 14 late final FeedRepository feedRepository; 15 - 15 + 16 16 @override 17 17 Future<CommentsPageState> build({required AtUri postUri}) async { 18 18 // Keep the provider alive to prevent unnecessary rebuilds 19 19 ref.keepAlive(); 20 - 20 + 21 21 feedRepository = GetIt.instance<SprkRepository>().feed; 22 22 // try to get from cache, if not found, fetch from network 23 23 final sqlCache = GetIt.instance<SQLCacheInterface>(); ··· 26 26 final thread = await feedRepository.getThread(postUri, bluesky: !cachedPost.isSprk, depth: 1); 27 27 switch (thread) { 28 28 case ThreadViewPost(): 29 - return CommentsPageState( 30 - thread: thread, 31 - ); 29 + return CommentsPageState(thread: thread); 32 30 case NotFoundPost(): 33 31 throw Exception('Post not found'); 34 32 case BlockedPost(): ··· 46 44 final thread = await feedRepository.getThread(postUri, bluesky: !networkPost.first.isSprk, depth: 1); 47 45 switch (thread) { 48 46 case ThreadViewPost(): 49 - return CommentsPageState( 50 - thread: thread, 51 - ); 47 + return CommentsPageState(thread: thread); 52 48 case NotFoundPost(): 53 49 throw Exception('Post not found'); 54 50 case BlockedPost(): ··· 69 65 }) async { 70 66 final feedRepository = GetIt.instance<SprkRepository>().feed; 71 67 final sqlCache = GetIt.instance<SQLCacheInterface>(); 72 - 73 - // Check if we currently have state data, but don't fail if we don't 74 - // as the state might become loading during execution 68 + 69 + // We need the current state to determine if the post is a sprk or bsky post. 70 + // If the state is not loaded, we cannot proceed. 75 71 final currentState = state.value; 76 - bool wasSprkPost = true; // Default assumption 77 - 78 - if (currentState != null) { 79 - wasSprkPost = currentState.thread.post.isSprk; 80 - } else { 81 - // If state is not available, try to determine from URI 82 - wasSprkPost = parentUri.contains('so.sprk'); 72 + if (currentState == null) { 73 + return; 83 74 } 84 - 75 + 85 76 await feedRepository.postComment( 86 77 text, 87 78 parentCid, ··· 91 82 imageFiles: imageFiles, 92 83 altTexts: altTexts, 93 84 ); 94 - 95 - // Refresh the thread to get the latest comments 96 - final thread = await feedRepository.getThread(AtUri.parse(parentUri), bluesky: !wasSprkPost, depth: 1); 85 + 86 + // Short delay to account for server-side replication lag. 87 + await Future.delayed(const Duration(milliseconds: 700)); 88 + 89 + // Refresh the thread using the provider's own postUri to ensure we're 90 + // refreshing the correct data. 91 + final thread = await feedRepository.getThread(postUri, bluesky: !currentState.thread.post.isSprk, depth: 1); 97 92 switch (thread) { 98 93 case ThreadViewPost(): 99 94 // Simply use the refreshed thread data from the server 100 95 // This ensures we have the most up-to-date comments and counts 101 - // Clear reply state when updating with new thread data 102 - state = AsyncValue.data( 103 - CommentsPageState( 104 - thread: thread, 105 - ), 106 - ); 107 - 96 + state = AsyncValue.data(CommentsPageState(thread: thread)); 97 + 108 98 // Update the cached post with the new reply count so it shows up in feeds 109 99 try { 110 100 await sqlCache.updatePost(thread.post); ··· 122 112 123 113 Future<void> deleteComment(String commentUri) async { 124 114 final sqlCache = GetIt.instance<SQLCacheInterface>(); 125 - 115 + 126 116 // Capture current state before making async calls 127 117 final currentState = state.value; 128 118 if (currentState == null) { 129 119 throw Exception('Cannot delete comment: comments not loaded'); 130 120 } 131 - 121 + 132 122 // Delete the comment first 133 123 await feedRepository.deletePost(AtUri.parse(commentUri)); 134 - 124 + 135 125 // Refresh the thread to get the latest comments and counts 136 - final thread = await feedRepository.getThread(currentState.thread.post.uri, bluesky: !currentState.thread.post.isSprk, depth: 1); 126 + final thread = await feedRepository.getThread( 127 + currentState.thread.post.uri, 128 + bluesky: !currentState.thread.post.isSprk, 129 + depth: 1, 130 + ); 137 131 switch (thread) { 138 132 case ThreadViewPost(): 139 133 // Use the refreshed thread data from the server 140 - state = AsyncValue.data( 141 - currentState.copyWith(thread: thread), 142 - ); 143 - 134 + state = AsyncValue.data(currentState.copyWith(thread: thread)); 135 + 144 136 // Update the cached post with the new reply count so it shows up in feeds 145 137 try { 146 138 await sqlCache.updatePost(thread.post);
+24 -16
lib/src/features/comments/ui/pages/comments_page.dart
··· 13 13 class CommentsPage extends ConsumerStatefulWidget { 14 14 final String postUri; 15 15 final bool isSprk; 16 + final PostView? post; 16 17 17 - const CommentsPage({super.key, required this.postUri, required this.isSprk}); 18 + const CommentsPage({super.key, required this.postUri, required this.isSprk, this.post}); 18 19 19 20 @override 20 21 ConsumerState<CommentsPage> createState() => _CommentsPageState(); ··· 80 81 late final AtUri _postAtUri; 81 82 late final String _postUri; 82 83 late final bool _isSprk; 84 + PostView? _post; 83 85 bool _initialized = false; 84 86 85 87 @override ··· 98 100 final parentArgs = parentRoute.argsAs<CommentsRouteArgs>(); 99 101 _postUri = parentArgs.postUri; 100 102 _isSprk = parentArgs.isSprk; 103 + _post = parentArgs.post; 101 104 _postAtUri = AtUri.parse(_postUri); 102 105 _initialized = true; 103 106 } ··· 136 139 137 140 @override 138 141 Widget build(BuildContext context) { 139 - final state = ref.watch(commentsPageProvider(postUri: _postAtUri)); 142 + final asyncState = ref.watch(commentsPageProvider(postUri: _postAtUri)); 143 + final displayPost = _post ?? asyncState.value?.thread.post; 140 144 final borderColor = Theme.of(context).colorScheme.outline; 141 145 final textColor = Theme.of(context).colorScheme.onSurface; 142 146 147 + if (displayPost == null) { 148 + return const Center(child: CircularProgressIndicator()); 149 + } 150 + 143 151 return Column( 144 152 children: [ 145 153 Container( 146 154 padding: const EdgeInsets.symmetric(vertical: 12), 147 - decoration: BoxDecoration(border: Border(bottom: BorderSide(color: borderColor, width: 0.2))), 155 + decoration: BoxDecoration( 156 + border: Border(bottom: BorderSide(color: borderColor, width: 0.2)), 157 + ), 148 158 child: Column( 149 159 children: [ 150 160 Container( ··· 159 169 mainAxisAlignment: MainAxisAlignment.spaceBetween, 160 170 children: [ 161 171 Text( 162 - '${state.value?.thread.post.replyCount ?? 'Loading'} comments', 172 + '${displayPost.replyCount ?? 0} comments', 163 173 style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold, color: textColor), 164 174 ), 165 175 IconButton( ··· 175 185 ), 176 186 ), 177 187 Expanded( 178 - child: state.when( 188 + child: asyncState.when( 179 189 data: (data) { 190 + if (data.thread.replies == null || data.thread.replies!.isEmpty) { 191 + return const Center(child: Text('No comments yet.')); 192 + } 180 193 return ListView.builder( 181 194 controller: _scrollController, 182 195 padding: const EdgeInsets.only(bottom: 16), ··· 195 208 }, 196 209 ), 197 210 ), 198 - state.when( 199 - data: 200 - (data) => _KeyboardAwareCommentInput( 201 - videoId: _postUri, 202 - postCid: data.thread.post.cid, 203 - postUri: _postUri, 204 - isSprk: _isSprk, 205 - focusNode: _focusNode, 206 - ), 207 - error: (error, stackTrace) => const SizedBox.shrink(), 208 - loading: () => const SizedBox.shrink(), 211 + _KeyboardAwareCommentInput( 212 + videoId: _postUri, 213 + postCid: displayPost.cid, 214 + postUri: _postUri, 215 + isSprk: _isSprk, 216 + focusNode: _focusNode, 209 217 ), 210 218 ], 211 219 );
+56 -93
lib/src/features/comments/ui/widgets/comment_input.dart
··· 8 8 import 'package:sparksocial/src/features/comments/providers/comment_input_state.dart'; 9 9 import 'package:sparksocial/src/features/comments/providers/comment_input_provider.dart'; 10 10 import 'package:sparksocial/src/core/widgets/alt_text_editor_dialog.dart'; 11 + import 'package:sparksocial/src/core/widgets/user_avatar.dart'; 11 12 import 'package:sparksocial/src/features/profile/providers/profile_provider.dart'; 12 13 13 14 import 'emoji_picker.dart'; ··· 68 69 69 70 // Updated input row with centered alignment 70 71 Container( 71 - margin: const EdgeInsets.symmetric(horizontal: 8), 72 72 decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(32)), 73 - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), 73 + padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 4), 74 74 child: Row( 75 75 crossAxisAlignment: CrossAxisAlignment.center, 76 76 children: [ 77 - _UserAvatar(textColor: Theme.of(context).colorScheme.onSurface, did: session?.did ?? ''), 78 - const SizedBox(width: 5), 77 + UserAvatar( 78 + imageUrl: ref 79 + .read(profileNotifierProvider(did: session?.did ?? '')) 80 + .when( 81 + data: (profileData) => profileData.profile?.avatar?.toString(), 82 + error: (error, stackTrace) => null, 83 + loading: () => null, 84 + ), 85 + username: session?.did ?? '', 86 + size: 28, 87 + borderWidth: 0, 88 + ), 89 + const SizedBox(width: 8), 79 90 _AttachmentButton( 80 91 state: state, 81 92 notifier: notifier, ··· 100 111 101 112 // Selected Images Preview (only show if images are selected) 102 113 if (state.selectedImages.isNotEmpty) 103 - Padding(padding: const EdgeInsets.only(top: 8.0), child: _SelectedImagesPreview(state: state, notifier: notifier)), 114 + Padding( 115 + padding: const EdgeInsets.only(top: 8.0), 116 + child: _SelectedImagesPreview(state: state, notifier: notifier), 117 + ), 104 118 ], 105 119 ), 106 120 ); 107 121 } 108 122 } 109 123 110 - class _UserAvatar extends ConsumerWidget { 111 - const _UserAvatar({required this.textColor, required this.did}); 112 - 113 - final Color textColor; 114 - final String did; 115 - 116 - @override 117 - Widget build(BuildContext context, WidgetRef ref) { 118 - final profileState = ref.watch(profileNotifierProvider(did: did)); 119 - profileState.when( 120 - loading: 121 - () => Container( 122 - width: 28, 123 - height: 28, 124 - decoration: const BoxDecoration(color: Color(0xFF330072), shape: BoxShape.circle), 125 - child: const Center( 126 - child: Text('Y', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500, fontSize: 14)), 127 - ), 128 - ), 129 - error: 130 - (error, stackTrace) => Container( 131 - width: 28, 132 - height: 28, 133 - decoration: const BoxDecoration(color: Color(0xFF330072), shape: BoxShape.circle), 134 - child: const Center( 135 - child: Text('Y', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500, fontSize: 14)), 136 - ), 137 - ), 138 - data: (profile) { 139 - if (profile.profile?.avatar == null) { 140 - return Container( 141 - width: 28, 142 - height: 28, 143 - decoration: const BoxDecoration(color: Color(0xFF330072), shape: BoxShape.circle), 144 - child: const Center( 145 - child: Text('Y', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w500, fontSize: 14)), 146 - ), 147 - ); 148 - } 149 - return Container( 150 - width: 28, 151 - height: 28, 152 - decoration: BoxDecoration( 153 - shape: BoxShape.circle, 154 - image: DecorationImage(image: NetworkImage(profile.profile!.avatar.toString())), 155 - ), 156 - ); 157 - }, 158 - ); 159 - return const SizedBox(); // in theory this should never be reached, but it's here to satisfy the compiler 160 - } 161 - } 162 - 163 124 class _TextField extends StatelessWidget { 164 125 const _TextField({ 165 126 required this.widget, ··· 193 154 border: InputBorder.none, 194 155 enabledBorder: InputBorder.none, 195 156 focusedBorder: InputBorder.none, 196 - suffixIcon: 197 - state.isPosting 198 - ? Container( 199 - margin: const EdgeInsets.all(8), 200 - width: 20, 201 - height: 20, 202 - child: CircularProgressIndicator( 203 - strokeWidth: 2, 204 - valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).colorScheme.primary), 205 - ), 206 - ) 207 - : IconButton( 208 - icon: Icon( 209 - FluentIcons.send_24_filled, 210 - size: 20, 211 - color: state.canSubmit ? Theme.of(context).colorScheme.primary : placeholderColor, 212 - ), 213 - onPressed: () { 214 - if (state.canSubmit) { 215 - // Use reply info if available, otherwise use main post info 216 - final parentCid = widget.postCid; 217 - final parentUri = widget.postUri; 218 - final rootCid = widget.rootCid; 219 - final rootUri = widget.rootUri; 157 + suffixIcon: state.isPosting 158 + ? Container( 159 + margin: const EdgeInsets.all(8), 160 + width: 20, 161 + height: 20, 162 + child: CircularProgressIndicator( 163 + strokeWidth: 2, 164 + valueColor: AlwaysStoppedAnimation<Color>(Theme.of(context).colorScheme.primary), 165 + ), 166 + ) 167 + : IconButton( 168 + icon: Icon( 169 + FluentIcons.send_24_filled, 170 + size: 20, 171 + color: state.canSubmit ? Theme.of(context).colorScheme.primary : placeholderColor, 172 + ), 173 + onPressed: () { 174 + if (state.canSubmit) { 175 + // Use reply info if available, otherwise use main post info 176 + final parentCid = widget.postCid; 177 + final parentUri = widget.postUri; 178 + final rootCid = widget.rootCid; 179 + final rootUri = widget.rootUri; 220 180 221 - notifier.submitComment( 222 - parentCid: parentCid, 223 - parentUri: parentUri, 224 - isSprk: widget.isSprk, 225 - rootCid: rootCid, 226 - rootUri: rootUri, 227 - ); 228 - } 229 - }, 230 - ), 181 + notifier.submitComment( 182 + parentCid: parentCid, 183 + parentUri: parentUri, 184 + isSprk: widget.isSprk, 185 + rootCid: rootCid, 186 + rootUri: rootUri, 187 + ); 188 + } 189 + }, 190 + ), 231 191 ), 232 192 style: TextStyle(color: textColor, fontSize: 14), 233 193 maxLines: 5, ··· 326 286 children: [ 327 287 Icon(FluentIcons.image_alt_text_20_regular, color: Colors.white, size: 14), 328 288 const SizedBox(width: 2), 329 - const Text('ALT', style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold)), 289 + const Text( 290 + 'ALT', 291 + style: TextStyle(color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold), 292 + ), 330 293 ], 331 294 ), 332 295 ),
+87 -78
lib/src/features/comments/ui/widgets/comment_item.dart
··· 35 35 super.initState(); 36 36 } 37 37 38 + void _navigateToProfile() { 39 + context.router.push(ProfileRoute(did: commentState.thread.post.author.did)); 40 + } 41 + 38 42 void _showImageCarousel() { 39 43 if (commentState.thread.post.embed == null) return; 40 44 ··· 72 76 final sprkRepository = GetIt.instance<SprkRepository>(); 73 77 showDialog( 74 78 context: context, 75 - builder: 76 - (context) => ReportDialog( 77 - postUri: commentState.thread.post.uri.toString(), 78 - postCid: commentState.thread.post.cid, 79 - onSubmit: (subject, reasonType, reason, service) async { 80 - try { 81 - final result = await sprkRepository.repo.createReport( 82 - subject: subject, 83 - reasonType: reasonType, 84 - reason: reason, 85 - service: service, 86 - ); 79 + builder: (context) => ReportDialog( 80 + postUri: commentState.thread.post.uri.toString(), 81 + postCid: commentState.thread.post.cid, 82 + onSubmit: (subject, reasonType, reason, service) async { 83 + try { 84 + final result = await sprkRepository.repo.createReport( 85 + subject: subject, 86 + reasonType: reasonType, 87 + reason: reason, 88 + service: service, 89 + ); 87 90 88 - if (result) { 89 - scaffoldMessenger.showSnackBar(const SnackBar(content: Text('Report submitted successfully'))); 90 - } 91 - } catch (e) { 92 - scaffoldMessenger.showSnackBar(SnackBar(content: Text('Error submitting report: $e'))); 93 - } 94 - }, 95 - ), 91 + if (result) { 92 + scaffoldMessenger.showSnackBar(const SnackBar(content: Text('Report submitted successfully'))); 93 + } 94 + } catch (e) { 95 + scaffoldMessenger.showSnackBar(SnackBar(content: Text('Error submitting report: $e'))); 96 + } 97 + }, 98 + ), 96 99 ); 97 100 } 98 101 ··· 102 105 // Confirm deletion 103 106 showDialog( 104 107 context: context, 105 - builder: 106 - (context) => AlertDialog( 107 - title: const Text('Delete Comment'), 108 - content: const Text('Are you sure you want to delete this comment? This action cannot be undone.'), 109 - actions: [ 110 - TextButton(onPressed: () => context.router.maybePop(), child: const Text('Cancel')), 111 - TextButton( 112 - style: TextButton.styleFrom(foregroundColor: Colors.red), 113 - onPressed: () async { 114 - try { 115 - await ref 116 - .read(commentsPageProvider(postUri: widget.mainPostUri).notifier) 117 - .deleteComment(commentState.thread.post.uri.toString()); 118 - if (context.mounted) { 119 - await context.router.maybePop(); // to close the menu below 120 - } 121 - } catch (e) { 122 - if (mounted) { 123 - scaffoldMessenger.showSnackBar(SnackBar(content: Text('Failed to delete comment: $e'))); 124 - } 125 - } 126 - }, 127 - child: const Text('Delete'), 128 - ), 129 - ], 108 + builder: (context) => AlertDialog( 109 + title: const Text('Delete Comment'), 110 + content: const Text('Are you sure you want to delete this comment? This action cannot be undone.'), 111 + actions: [ 112 + TextButton(onPressed: () => context.router.maybePop(), child: const Text('Cancel')), 113 + TextButton( 114 + style: TextButton.styleFrom(foregroundColor: Colors.red), 115 + onPressed: () async { 116 + try { 117 + await ref 118 + .read(commentsPageProvider(postUri: widget.mainPostUri).notifier) 119 + .deleteComment(commentState.thread.post.uri.toString()); 120 + if (context.mounted) { 121 + await context.router.maybePop(); // to close the menu below 122 + } 123 + } catch (e) { 124 + if (mounted) { 125 + scaffoldMessenger.showSnackBar(SnackBar(content: Text('Failed to delete comment: $e'))); 126 + } 127 + } 128 + }, 129 + child: const Text('Delete'), 130 130 ), 131 + ], 132 + ), 131 133 ); 132 134 } 133 135 ··· 149 151 child: Row( 150 152 crossAxisAlignment: CrossAxisAlignment.start, 151 153 children: [ 152 - _Avatar(widget: widget), 154 + GestureDetector( 155 + onTap: _navigateToProfile, 156 + child: _Avatar(widget: widget), 157 + ), 153 158 const SizedBox(width: 12), 154 159 Expanded( 155 160 child: Column( ··· 159 164 mainAxisAlignment: MainAxisAlignment.spaceBetween, 160 165 children: [ 161 166 Expanded( 162 - child: Row( 163 - children: [ 164 - Text( 165 - commentState.thread.post.author.handle, 166 - style: TextStyle( 167 - fontWeight: FontWeight.bold, 168 - color: Theme.of(context).textTheme.bodyLarge?.color, 167 + child: GestureDetector( 168 + onTap: _navigateToProfile, 169 + child: Row( 170 + crossAxisAlignment: CrossAxisAlignment.center, 171 + children: [ 172 + Text( 173 + commentState.thread.post.author.handle, 174 + style: TextStyle( 175 + fontWeight: FontWeight.bold, 176 + color: Theme.of(context).textTheme.bodyLarge?.color, 177 + ), 169 178 ), 170 - ), 171 - const SizedBox(width: 8), 172 - Text( 173 - _formatDate(commentState.thread.post.indexedAt.toLocal().toString()), 174 - style: TextStyle(fontSize: 12, color: Theme.of(context).textTheme.bodyMedium?.color), 175 - ), 176 - ], 179 + const SizedBox(width: 8), 180 + Text( 181 + _formatDate(commentState.thread.post.indexedAt.toLocal().toString()), 182 + style: TextStyle(fontSize: 12, color: Theme.of(context).textTheme.bodyMedium?.color), 183 + ), 184 + ], 185 + ), 177 186 ), 178 187 ), 179 188 MenuActionButton( ··· 214 223 CachedNetworkImage( 215 224 imageUrl: commentState.thread.post.imageUrls.first, 216 225 fit: BoxFit.cover, 217 - placeholder: 218 - (context, url) => Container( 219 - color: Colors.grey[850]?.withValues(alpha: 128), 220 - child: const Center( 221 - child: SizedBox( 222 - width: 20, 223 - height: 20, 224 - child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white54), 225 - ), 226 - ), 227 - ), 228 - errorWidget: 229 - (context, url, error) => Container( 230 - color: AppColors.darkPurple.withValues(alpha: 26), 231 - child: const Center( 232 - child: Icon(FluentIcons.image_off_24_regular, size: 24, color: Colors.white70), 233 - ), 226 + placeholder: (context, url) => Container( 227 + color: Colors.grey[850]?.withValues(alpha: 128), 228 + child: const Center( 229 + child: SizedBox( 230 + width: 20, 231 + height: 20, 232 + child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white54), 234 233 ), 234 + ), 235 + ), 236 + errorWidget: (context, url, error) => Container( 237 + color: AppColors.darkPurple.withValues(alpha: 26), 238 + child: const Center( 239 + child: Icon(FluentIcons.image_off_24_regular, size: 24, color: Colors.white70), 240 + ), 241 + ), 235 242 ), 236 243 237 244 if (imageCount > 1) ··· 312 319 child: Container( 313 320 margin: const EdgeInsets.only(left: 64), 314 321 padding: const EdgeInsets.only(top: 4, bottom: 8), 315 - decoration: BoxDecoration(border: Border(left: BorderSide(color: Theme.of(context).colorScheme.surface, width: 1))), 322 + decoration: BoxDecoration( 323 + border: Border(left: BorderSide(color: Theme.of(context).colorScheme.surface, width: 1)), 324 + ), 316 325 child: Text('Show ${commentState.thread.post.replyCount} replies'), 317 326 ), 318 327 );
+1 -1
lib/src/features/feed/ui/widgets/action_buttons/side_action_bar.dart
··· 250 250 251 251 void _handleCommentPressed() { 252 252 final currentPost = _currentPost ?? widget.post; 253 - context.router.push(CommentsRoute(postUri: currentPost.uri.toString(), isSprk: currentPost.isSprk)); 253 + context.router.push(CommentsRoute(postUri: currentPost.uri.toString(), isSprk: currentPost.isSprk, post: currentPost)); 254 254 } 255 255 256 256 @override