import 'package:flutter/material.dart'; import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart'; import 'package:logging/logging.dart'; import '../../models/flattened_tree_item.dart'; import '../../models/timeline_entry.dart'; import '../../utils/url_opening_utils.dart'; import '../media_attachment_viewer_control.dart'; import '../padding.dart'; import 'interactions_bar_control.dart'; import 'link_preview_control.dart'; import 'status_header_control.dart'; class FlattenedTreeEntryControl extends StatefulWidget { final FlattenedTreeItem originalItem; final bool openRemote; final bool showStatusOpenButton; const FlattenedTreeEntryControl( {super.key, required this.originalItem, required this.openRemote, required this.showStatusOpenButton}); @override State createState() => _StatusControlState(); } class _StatusControlState extends State { static final _logger = Logger('$FlattenedTreeEntryControl'); var showContent = true; var showComments = false; FlattenedTreeItem get item => widget.originalItem; TimelineEntry get entry => item.timelineEntry; bool get isPublic => entry.isPublic; bool get isPost => entry.parentId.isEmpty; bool get hasComments => entry.engagementSummary.repliesCount > 0; @override void initState() { showContent = entry.spoilerText.isEmpty; showComments = isPost ? false : true; } @override Widget build(BuildContext context) { _logger.finest('Building ${entry.toShortString()}'); const otherPadding = 8.0; final leftPadding = otherPadding + (widget.originalItem.level * 15.0); final color = widget.originalItem.level.isOdd ? Theme.of(context).secondaryHeaderColor : Theme.of(context).dialogBackgroundColor; final body = Container( decoration: BoxDecoration( color: color, border: Border.all(width: 0.5), borderRadius: BorderRadius.circular(5.0), boxShadow: [ BoxShadow( color: Theme.of(context).dividerColor, blurRadius: 2, offset: Offset(4, 4), spreadRadius: 0.1, blurStyle: BlurStyle.normal, ) ], ), child: Padding( padding: const EdgeInsets.all(5.0), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ StatusHeaderControl( entry: entry, ), const VerticalPadding( height: 5, ), if (entry.spoilerText.isNotEmpty) TextButton( onPressed: () { setState(() { showContent = !showContent; }); }, child: Text( 'Content Summary: ${entry.spoilerText} (Click to ${showContent ? "Hide" : "Show"}}')), if (showContent) ...[ buildBody(context), const VerticalPadding( height: 5, ), if (entry.linkPreviewData != null) LinkPreviewControl(preview: entry.linkPreviewData!), buildMediaBar(context), ], const VerticalPadding( height: 5, ), InteractionsBarControl( entry: entry, isMine: item.isMine, openRemote: widget.openRemote, showOpenControl: widget.showStatusOpenButton, ), const VerticalPadding( height: 5, ), ], ), ), ); return Padding( padding: EdgeInsets.only( left: leftPadding, right: otherPadding, top: otherPadding, bottom: otherPadding, ), child: body, ); } Widget buildBody(BuildContext context) { return HtmlWidget( entry.body, onTapUrl: (url) async { return await openUrlStringInSystembrowser(context, url, 'link'); }, ); } Widget buildMediaBar(BuildContext context) { final items = entry.mediaAttachments; if (items.isEmpty) { return const SizedBox(); } return SizedBox( height: 250.0, child: ListView.separated( scrollDirection: Axis.horizontal, itemBuilder: (context, index) { return MediaAttachmentViewerControl( attachments: items, index: index, ); }, separatorBuilder: (context, index) { return HorizontalPadding(); }, itemCount: items.length)); } }