···
+
import 'package:flutter/material.dart';
+
import 'package:provider/provider.dart';
+
import '../../constants/app_colors.dart';
+
import '../../models/comment.dart';
+
import '../../providers/auth_provider.dart';
+
import '../../widgets/comment_card.dart';
+
import '../../widgets/comment_thread.dart';
+
import '../../widgets/status_bar_overlay.dart';
+
import '../compose/reply_screen.dart';
+
/// Focused thread screen for viewing deep comment threads
+
/// Displays a specific comment as the "anchor" with its full reply tree.
+
/// Used when user taps "Read X more replies" on a deeply nested thread.
+
/// - Ancestor comments shown flat at the top (walking up the chain)
+
/// - The anchor comment (highlighted as the focus)
+
/// - All replies threaded below with fresh depth starting at 0
+
/// This screen maintains its own collapsed comment state, intentionally
+
/// providing a "fresh slate" experience. When the user navigates back,
+
/// any collapsed state is reset. This is by design - it allows users to
+
/// explore deep threads without their collapse choices persisting across
+
/// navigation, keeping the focused view clean and predictable.
+
class FocusedThreadScreen extends StatelessWidget {
+
const FocusedThreadScreen({
+
required this.ancestors,
+
/// The comment thread to focus on (becomes the new root)
+
final ThreadViewComment thread;
+
/// Ancestor comments leading to this thread (for context display)
+
final List<ThreadViewComment> ancestors;
+
/// Callback when user replies to a comment
+
final Future<void> Function(String content, ThreadViewComment parent) onReply;
+
Widget build(BuildContext context) {
+
backgroundColor: AppColors.background,
+
body: _FocusedThreadBody(
+
class _FocusedThreadBody extends StatefulWidget {
+
const _FocusedThreadBody({
+
required this.ancestors,
+
final ThreadViewComment thread;
+
final List<ThreadViewComment> ancestors;
+
final Future<void> Function(String content, ThreadViewComment parent) onReply;
+
State<_FocusedThreadBody> createState() => _FocusedThreadBodyState();
+
class _FocusedThreadBodyState extends State<_FocusedThreadBody> {
+
final Set<String> _collapsedComments = {};
+
final ScrollController _scrollController = ScrollController();
+
final GlobalKey _anchorKey = GlobalKey();
+
// Scroll to anchor comment after build
+
WidgetsBinding.instance.addPostFrameCallback((_) {
+
_scrollController.dispose();
+
void _scrollToAnchor() {
+
final context = _anchorKey.currentContext;
+
Scrollable.ensureVisible(
+
duration: const Duration(milliseconds: 300),
+
void _toggleCollapsed(String uri) {
+
if (_collapsedComments.contains(uri)) {
+
_collapsedComments.remove(uri);
+
_collapsedComments.add(uri);
+
void _openReplyScreen(ThreadViewComment comment) {
+
// Check authentication
+
final authProvider = context.read<AuthProvider>();
+
if (!authProvider.isAuthenticated) {
+
ScaffoldMessenger.of(context).showSnackBar(
+
content: Text('Sign in to reply'),
+
behavior: SnackBarBehavior.floating,
+
Navigator.of(context).push(
+
MaterialPageRoute<void>(
+
builder: (context) => ReplyScreen(
+
onSubmit: (content) => widget.onReply(content, comment),
+
/// Navigate deeper into a nested thread
+
void _onContinueThread(
+
ThreadViewComment thread,
+
List<ThreadViewComment> ancestors,
+
Navigator.of(context).push(
+
MaterialPageRoute<void>(
+
builder: (context) => FocusedThreadScreen(
+
onReply: widget.onReply,
+
Widget build(BuildContext context) {
+
// Calculate minimum bottom padding to allow anchor to scroll to top
+
final screenHeight = MediaQuery.of(context).size.height;
+
final minBottomPadding = screenHeight * 0.6;
+
controller: _scrollController,
+
backgroundColor: AppColors.background,
+
surfaceTintColor: Colors.transparent,
+
foregroundColor: AppColors.textPrimary,
+
fontWeight: FontWeight.w600,
+
delegate: SliverChildListDelegate([
+
// Ancestor comments (shown flat, not nested)
+
...widget.ancestors.map(_buildAncestorComment),
+
// Anchor comment (the focused comment) - made prominent
+
child: _buildAnchorComment(),
+
if (widget.thread.replies != null &&
+
widget.thread.replies!.isNotEmpty)
+
...widget.thread.replies!.map((reply) {
+
onCommentTap: _openReplyScreen,
+
collapsedComments: _collapsedComments,
+
onCollapseToggle: _toggleCollapsed,
+
onContinueThread: _onContinueThread,
+
ancestors: [widget.thread],
+
// Empty state if no replies
+
if (widget.thread.replies == null ||
+
widget.thread.replies!.isEmpty)
+
// Bottom padding to allow anchor to scroll to top
+
SizedBox(height: minBottomPadding),
+
// Prevents content showing through transparent status bar
+
const StatusBarOverlay(),
+
/// Build an ancestor comment (shown flat as context above anchor)
+
/// Styled more subtly than the anchor to show it's contextual
+
Widget _buildAncestorComment(ThreadViewComment ancestor) {
+
comment: ancestor.comment,
+
onTap: () => _openReplyScreen(ancestor),
+
/// Build the anchor comment (the focused comment) with prominent styling
+
Widget _buildAnchorComment() {
+
// Note: CommentCard has its own Consumer<VoteProvider> for vote state
+
decoration: BoxDecoration(
+
// Subtle highlight to distinguish anchor from ancestors
+
color: AppColors.primary.withValues(alpha: 0.05),
+
color: AppColors.primary.withValues(alpha: 0.6),
+
comment: widget.thread.comment,
+
onTap: () => _openReplyScreen(widget.thread),
+
onLongPress: () => _toggleCollapsed(widget.thread.comment.uri),
+
isCollapsed: _collapsedComments.contains(widget.thread.comment.uri),
+
collapsedCount: _collapsedComments.contains(widget.thread.comment.uri)
+
? CommentThread.countDescendants(widget.thread)
+
/// Build empty state when there are no replies
+
Widget _buildNoReplies() {
+
padding: const EdgeInsets.all(32),
+
alignment: Alignment.center,
+
Icons.chat_bubble_outline_rounded,
+
color: AppColors.textSecondary.withValues(alpha: 0.5),
+
const SizedBox(height: 16),
+
color: AppColors.textSecondary.withValues(alpha: 0.7),
+
const SizedBox(height: 8),
+
'Be the first to reply to this comment',
+
color: AppColors.textSecondary.withValues(alpha: 0.5),