relatica/lib/controls/timeline/status_control.dart
2022-12-30 11:44:39 -05:00

214 lines
6.5 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import '../../models/attachment_media_type_enum.dart';
import '../../models/entry_tree_item.dart';
import '../../models/timeline_entry.dart';
import '../../screens/image_viewer_screen.dart';
import '../../services/timeline_manager.dart';
import '../../utils/snackbar_builder.dart';
import '../../utils/url_opening_utils.dart';
import '../image_control.dart';
import '../padding.dart';
import 'interactions_bar_control.dart';
import 'status_header_control.dart';
class StatusControl extends StatefulWidget {
final EntryTreeItem originalItem;
final bool openRemote;
final bool showStatusOpenButton;
const StatusControl(
{super.key,
required this.originalItem,
required this.openRemote,
required this.showStatusOpenButton});
@override
State<StatusControl> createState() => _StatusControlState();
}
class _StatusControlState extends State<StatusControl> {
static final _logger = Logger('$StatusControl');
var showContent = true;
var showComments = false;
EntryTreeItem get item => widget.originalItem;
TimelineEntry get entry => item.entry;
bool get isPublic => item.entry.isPublic;
bool get isPost => item.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) {
final manager = context.watch<TimelineManager>();
_logger.finest('Building ${item.entry.toShortString()}');
final padding = isPost ? 8.0 : 8.0;
final body = Padding(
padding: EdgeInsets.all(padding),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
StatusHeaderControl(
entry: entry,
openRemote: widget.openRemote,
showOpenControl: widget.showStatusOpenButton,
),
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,
),
buildMediaBar(context),
],
const VerticalPadding(
height: 5,
),
InteractionsBarControl(
entry: entry,
isMine: item.isMine,
),
const VerticalPadding(
height: 5,
),
if (isPost && hasComments)
TextButton(
onPressed: () async {
setState(() {
showComments = !showComments;
});
if (showComments) {
await manager.refreshStatusChain(item.id);
}
},
child:
Text(showComments ? 'Hide Comments' : 'Load & Show Comments'),
),
if (item.totalChildren > 0 && showComments)
buildChildComments(context),
],
),
);
return isPost
? body
: Card(color: Theme.of(context).splashColor, child: body);
}
Widget buildBody(BuildContext context) {
return HtmlWidget(
entry.body,
onTapUrl: (url) async {
return await openUrlStringInSystembrowser(context, url, 'video');
},
);
}
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) {
final item = items[index];
if (item.explicitType == AttachmentMediaType.video) {
return ElevatedButton(
onPressed: () async {
if (await canLaunchUrl(item.uri)) {
buildSnackbar(
context,
'Attempting to launch video: ${item.uri}',
);
await launchUrl(item.uri);
} else {
buildSnackbar(
context, 'Unable to launch video: ${item.uri}');
}
},
child: Text(item.description.isNotEmpty
? item.description
: 'Video'));
}
if (item.explicitType != AttachmentMediaType.image) {
return Text('${item.explicitType}: ${item.uri}');
}
return ImageControl(
width: 250.0,
height: 250.0,
imageUrl: item.thumbnailUri.toString(),
altText: item.description,
onTap: () async {
Navigator.push(context, MaterialPageRoute(builder: (context) {
return ImageViewerScreen(attachment: item);
}));
},
);
// return Text(item.toString());
},
separatorBuilder: (context, index) {
return HorizontalPadding();
},
itemCount: items.length));
}
Widget buildChildComments(BuildContext context) {
final comments = widget.originalItem.children;
if (comments.isEmpty) {
return Text('No comments');
}
return Padding(
padding: EdgeInsets.only(left: 5.0, top: 5.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
children: comments
.map((c) => StatusControl(
originalItem: c,
openRemote: false,
showStatusOpenButton: false,
))
.toList(),
),
),
],
));
}
}