import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:logging/logging.dart'; import '../../globals.dart'; import '../../models/timeline_entry.dart'; import '../../services/timeline_manager.dart'; import '../../utils/clipboard_utils.dart'; import '../../utils/snackbar_builder.dart'; import '../../utils/url_opening_utils.dart'; class InteractionsBarControl extends StatefulWidget { final TimelineEntry entry; final bool openRemote; final bool showOpenControl; final bool isMine; const InteractionsBarControl({ super.key, required this.entry, required this.isMine, required this.showOpenControl, required this.openRemote, }); @override State createState() => _InteractionsBarControlState(); } class _InteractionsBarControlState extends State { static final _logger = Logger('$InteractionsBarControl'); var isProcessing = false; bool get isPost => widget.entry.parentId.isEmpty; bool get isFavorited => widget.entry.isFavorited; bool get youReshared => widget.entry.youReshared; int get reshares => widget.entry.engagementSummary.rebloggedCount; int get comments => widget.entry.engagementSummary.repliesCount; int get likes => widget.entry.engagementSummary.favoritesCount; Future toggleFavorited() async { setState(() { isProcessing = true; }); final newState = !isFavorited; _logger.finest('Trying to toggle favorite from $isFavorited to $newState'); final result = await getIt() .toggleFavorited(widget.entry.id, newState); result.match(onSuccess: (update) { setState(() { _logger.finest( 'Success toggling! $isFavorited -> ${update.entry.isFavorited}'); }); }, onError: (error) { buildSnackbar(context, 'Error toggling like status: $error'); }); setState(() { isProcessing = false; }); } Future resharePost() async { setState(() { isProcessing = true; }); final id = widget.entry.id; _logger.finest('Trying to reshare $id'); final result = await getIt().resharePost(id); result.match(onSuccess: (update) { setState(() { _logger.finest('Success resharing post by ${widget.entry.author}'); }); }, onError: (error) { buildSnackbar(context, 'Error resharing post by ${widget.entry.author}'); }); setState(() { isProcessing = false; }); } Future addComment() async { context.push('/comment/new?parent_id=${widget.entry.id}'); } Future deleteEntry() async { setState(() { isProcessing = true; }); final confirm = await showYesNoDialog(context, 'Delete ${isPost ? "Post" : "Comment"}'); if (confirm == true) { await getIt().deleteEntryById(widget.entry.id); } setState(() { isProcessing = false; }); } Future unResharePost() async { setState(() { isProcessing = true; }); final id = widget.entry.id; _logger.finest('Trying to un-reshare $id'); final result = await getIt().unResharePost(id); result.match(onSuccess: (update) { setState(() { _logger.finest('Success un-resharing post by ${widget.entry.author}'); }); }, onError: (error) { buildSnackbar( context, 'Error un-resharing post by ${widget.entry.author}'); }); setState(() { isProcessing = false; }); } Future openAction(BuildContext context) async { const goToPost = 'Go to Post'; const copyUrl = 'Copy URL'; const openExternal = 'Open In Browser'; final options = [ if (widget.showOpenControl && !widget.openRemote) goToPost, openExternal, copyUrl, 'Cancel' ]; final result = await showChooseOptions(context, 'Would you like to', options); if (!mounted) { return; } switch (result) { case goToPost: context.push('/post/view/${widget.entry.id}/${widget.entry.id}'); break; case openExternal: await openUrlStringInSystembrowser( context, widget.entry.externalLink, 'Post', ); break; case copyUrl: await copyToClipboard( context: context, text: widget.entry.externalLink, message: 'Post link copied to clipboard', ); break; default: //do nothing } } @override Widget build(BuildContext context) { _logger.finest('Building: ${widget.entry.toShortString()}'); return Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ TextButton( onPressed: likes == 0 ? null : () {}, child: Text('$likes likes'), ), TextButton( onPressed: reshares == 0 ? null : () {}, child: Text('$reshares reshares'), ), Text('$comments comments'), ], ), Text('$likes likes, $reshares reshares, $comments comments'), Row(children: [ IconButton( onPressed: isProcessing ? null : () async => await toggleFavorited(), icon: isFavorited ? const Icon( Icons.thumb_up, semanticLabel: 'Like', ) : Icon(Icons.thumb_up_outlined)), if (isPost) IconButton( onPressed: widget.isMine && !widget.entry.youReshared ? null : isProcessing ? null : () async => youReshared ? await unResharePost() : await resharePost(), icon: Icon( youReshared ? Icons.repeat_on_outlined : Icons.repeat)), IconButton( onPressed: isProcessing ? null : addComment, icon: Icon(Icons.add_comment)), if (widget.isMine && !widget.entry .youReshared) //TODO Figure out why reshares show up as mine sometimes but not others IconButton( onPressed: isProcessing ? null : () async => await deleteEntry(), icon: Icon(Icons.delete)), Expanded( child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ IconButton( onPressed: () async { await copyToClipboard( context: context, text: widget.entry.body, message: 'Post text copied to clipboard', ); }, icon: const Icon(Icons.copy), ), IconButton( onPressed: () async { await openAction(context); }, icon: const Icon(Icons.launch), ), ], ), ), ]), ], ); } }