2022-11-18 21:50:15 +00:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
2022-11-21 21:21:45 +00:00
|
|
|
import 'package:logging/logging.dart';
|
2022-11-22 14:53:35 +00:00
|
|
|
import 'package:provider/provider.dart';
|
2022-11-18 21:50:15 +00:00
|
|
|
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';
|
2022-11-21 21:21:45 +00:00
|
|
|
import '../../services/timeline_manager.dart';
|
2022-11-18 21:50:15 +00:00
|
|
|
import '../../utils/snackbar_builder.dart';
|
2022-11-23 20:48:09 +00:00
|
|
|
import '../../utils/url_opening_utils.dart';
|
2022-12-30 15:55:05 +00:00
|
|
|
import '../image_control.dart';
|
2022-11-18 21:50:15 +00:00
|
|
|
import '../padding.dart';
|
|
|
|
import 'interactions_bar_control.dart';
|
2022-11-23 02:59:08 +00:00
|
|
|
import 'status_header_control.dart';
|
2022-11-18 21:50:15 +00:00
|
|
|
|
2022-11-18 23:31:28 +00:00
|
|
|
class StatusControl extends StatefulWidget {
|
|
|
|
final EntryTreeItem originalItem;
|
2022-12-13 12:17:35 +00:00
|
|
|
final bool openRemote;
|
|
|
|
final bool showStatusOpenButton;
|
2022-11-18 21:50:15 +00:00
|
|
|
|
2022-11-23 20:48:09 +00:00
|
|
|
const StatusControl(
|
2022-12-13 12:17:35 +00:00
|
|
|
{super.key,
|
|
|
|
required this.originalItem,
|
|
|
|
required this.openRemote,
|
|
|
|
required this.showStatusOpenButton});
|
2022-11-18 23:31:28 +00:00
|
|
|
|
|
|
|
@override
|
|
|
|
State<StatusControl> createState() => _StatusControlState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _StatusControlState extends State<StatusControl> {
|
2022-11-21 21:21:45 +00:00
|
|
|
static final _logger = Logger('$StatusControl');
|
2022-11-18 21:50:15 +00:00
|
|
|
|
2022-11-22 18:36:57 +00:00
|
|
|
var showContent = true;
|
|
|
|
|
2022-12-13 12:25:03 +00:00
|
|
|
var showComments = false;
|
|
|
|
|
2022-11-21 21:21:45 +00:00
|
|
|
EntryTreeItem get item => widget.originalItem;
|
|
|
|
|
|
|
|
TimelineEntry get entry => item.entry;
|
2022-11-18 21:50:15 +00:00
|
|
|
|
2022-11-22 18:36:57 +00:00
|
|
|
bool get isPublic => item.entry.isPublic;
|
|
|
|
|
2022-11-22 16:43:16 +00:00
|
|
|
bool get isPost => item.entry.parentId.isEmpty;
|
|
|
|
|
|
|
|
bool get hasComments => entry.engagementSummary.repliesCount > 0;
|
|
|
|
|
2022-11-22 18:36:57 +00:00
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
showContent = entry.spoilerText.isEmpty;
|
2022-12-19 14:43:06 +00:00
|
|
|
showComments = isPost ? false : true;
|
2022-11-22 18:36:57 +00:00
|
|
|
}
|
|
|
|
|
2022-11-18 21:50:15 +00:00
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
2022-11-22 14:53:35 +00:00
|
|
|
final manager = context.watch<TimelineManager>();
|
2022-11-21 21:21:45 +00:00
|
|
|
_logger.finest('Building ${item.entry.toShortString()}');
|
2022-12-19 15:13:01 +00:00
|
|
|
final padding = isPost ? 8.0 : 8.0;
|
|
|
|
final body = Padding(
|
|
|
|
padding: EdgeInsets.all(padding),
|
2022-11-18 21:50:15 +00:00
|
|
|
child: Column(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
2022-11-23 20:48:09 +00:00
|
|
|
StatusHeaderControl(
|
|
|
|
entry: entry,
|
2022-12-13 12:17:35 +00:00
|
|
|
openRemote: widget.openRemote,
|
|
|
|
showOpenControl: widget.showStatusOpenButton,
|
2022-11-23 20:48:09 +00:00
|
|
|
),
|
2022-11-18 21:50:15 +00:00
|
|
|
const VerticalPadding(
|
|
|
|
height: 5,
|
|
|
|
),
|
2022-12-13 04:04:32 +00:00
|
|
|
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),
|
|
|
|
],
|
2022-11-18 21:50:15 +00:00
|
|
|
const VerticalPadding(
|
|
|
|
height: 5,
|
|
|
|
),
|
2022-11-22 19:42:26 +00:00
|
|
|
InteractionsBarControl(
|
|
|
|
entry: entry,
|
|
|
|
isMine: item.isMine,
|
|
|
|
),
|
2022-11-18 21:50:15 +00:00
|
|
|
const VerticalPadding(
|
|
|
|
height: 5,
|
|
|
|
),
|
2022-11-22 16:43:16 +00:00
|
|
|
if (isPost && hasComments)
|
2022-11-22 14:53:35 +00:00
|
|
|
TextButton(
|
2022-12-13 12:25:03 +00:00
|
|
|
onPressed: () async {
|
|
|
|
setState(() {
|
|
|
|
showComments = !showComments;
|
|
|
|
});
|
|
|
|
if (showComments) {
|
|
|
|
await manager.refreshStatusChain(item.id);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
child:
|
|
|
|
Text(showComments ? 'Hide Comments' : 'Load & Show Comments'),
|
2022-11-22 14:53:35 +00:00
|
|
|
),
|
2022-12-13 12:25:03 +00:00
|
|
|
if (item.totalChildren > 0 && showComments)
|
|
|
|
buildChildComments(context),
|
2022-11-18 21:50:15 +00:00
|
|
|
],
|
|
|
|
),
|
|
|
|
);
|
2022-12-19 15:13:01 +00:00
|
|
|
|
|
|
|
return isPost
|
|
|
|
? body
|
|
|
|
: Card(color: Theme.of(context).splashColor, child: body);
|
2022-11-18 21:50:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Widget buildBody(BuildContext context) {
|
2022-12-13 04:04:32 +00:00
|
|
|
return HtmlWidget(
|
|
|
|
entry.body,
|
|
|
|
onTapUrl: (url) async {
|
|
|
|
return await openUrlStringInSystembrowser(context, url, 'video');
|
|
|
|
},
|
2022-11-18 21:50:15 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
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}');
|
|
|
|
}
|
|
|
|
|
2022-12-30 15:55:05 +00:00
|
|
|
return ImageControl(
|
|
|
|
width: 250.0,
|
|
|
|
height: 250.0,
|
|
|
|
imageUrl: item.thumbnailUri.toString(),
|
2022-11-18 21:50:15 +00:00
|
|
|
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) {
|
2022-11-18 23:31:28 +00:00
|
|
|
final comments = widget.originalItem.children;
|
2022-12-19 15:13:01 +00:00
|
|
|
|
2022-11-18 21:50:15 +00:00
|
|
|
if (comments.isEmpty) {
|
|
|
|
return Text('No comments');
|
|
|
|
}
|
|
|
|
return Padding(
|
2022-12-19 15:13:01 +00:00
|
|
|
padding: EdgeInsets.only(left: 5.0, top: 5.0),
|
2022-11-18 21:50:15 +00:00
|
|
|
child: Row(
|
|
|
|
mainAxisAlignment: MainAxisAlignment.start,
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: Column(
|
2022-11-18 23:31:28 +00:00
|
|
|
children: comments
|
2022-11-23 20:48:09 +00:00
|
|
|
.map((c) => StatusControl(
|
|
|
|
originalItem: c,
|
2022-12-13 12:17:35 +00:00
|
|
|
openRemote: false,
|
|
|
|
showStatusOpenButton: false,
|
2022-11-23 20:48:09 +00:00
|
|
|
))
|
2022-11-18 23:31:28 +00:00
|
|
|
.toList(),
|
2022-11-18 21:50:15 +00:00
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|