kopia lustrzana https://gitlab.com/mysocialportal/relatica
Refactor post display to be cleaner
rodzic
5ffa37d9be
commit
942f6f0ec5
|
@ -1,9 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html_core/flutter_widget_from_html_core.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
import '../../globals.dart';
|
||||
import '../../models/flattened_tree_item.dart';
|
||||
import '../../models/timeline_entry.dart';
|
||||
import '../../services/timeline_manager.dart';
|
||||
import '../../utils/active_profile_selector.dart';
|
||||
import '../../utils/clipboard_utils.dart';
|
||||
import '../../utils/url_opening_utils.dart';
|
||||
import '../media_attachment_viewer_control.dart';
|
||||
import '../padding.dart';
|
||||
|
@ -43,6 +48,8 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
|
|||
|
||||
bool get hasComments => entry.engagementSummary.repliesCount > 0;
|
||||
|
||||
bool isProcessing = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
showContent = entry.spoilerText.isEmpty;
|
||||
|
@ -78,8 +85,15 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
|
|||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
StatusHeaderControl(
|
||||
entry: entry,
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: StatusHeaderControl(
|
||||
entry: entry,
|
||||
),
|
||||
),
|
||||
buildMenuControl(context),
|
||||
],
|
||||
),
|
||||
const VerticalPadding(
|
||||
height: 5,
|
||||
|
@ -108,7 +122,6 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
|
|||
InteractionsBarControl(
|
||||
entry: entry,
|
||||
isMine: item.isMine,
|
||||
openRemote: widget.openRemote,
|
||||
showOpenControl: widget.showStatusOpenButton,
|
||||
),
|
||||
const VerticalPadding(
|
||||
|
@ -158,4 +171,88 @@ class _StatusControlState extends State<FlattenedTreeEntryControl> {
|
|||
},
|
||||
itemCount: items.length));
|
||||
}
|
||||
|
||||
Widget buildMenuControl(BuildContext context) {
|
||||
const editStatus = 'Edit';
|
||||
const deleteStatus = 'Delete';
|
||||
const goToPost = 'Open Post';
|
||||
const copyText = 'Copy Post Text';
|
||||
const copyUrl = 'Copy URL';
|
||||
const openExternal = 'Open In Browser';
|
||||
final options = [
|
||||
if (widget.showStatusOpenButton && !widget.openRemote) goToPost,
|
||||
if (item.isMine && !item.timelineEntry.youReshared) editStatus,
|
||||
if (item.isMine) deleteStatus,
|
||||
copyText,
|
||||
openExternal,
|
||||
copyUrl,
|
||||
];
|
||||
|
||||
return PopupMenuButton<String>(onSelected: (menuOption) async {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (menuOption) {
|
||||
case goToPost:
|
||||
context.push(
|
||||
'/post/view/${item.timelineEntry.id}/${item.timelineEntry.id}');
|
||||
break;
|
||||
case editStatus:
|
||||
if (item.timelineEntry.parentId.isEmpty) {
|
||||
context.push('/post/edit/${item.timelineEntry.id}');
|
||||
} else {
|
||||
context.push('/comment/edit/${item.timelineEntry.id}');
|
||||
}
|
||||
break;
|
||||
case deleteStatus:
|
||||
deleteEntry();
|
||||
break;
|
||||
case openExternal:
|
||||
await openUrlStringInSystembrowser(
|
||||
context,
|
||||
item.timelineEntry.externalLink,
|
||||
'Post',
|
||||
);
|
||||
break;
|
||||
case copyUrl:
|
||||
await copyToClipboard(
|
||||
context: context,
|
||||
text: item.timelineEntry.externalLink,
|
||||
message: 'Post link copied to clipboard',
|
||||
);
|
||||
break;
|
||||
case copyText:
|
||||
await copyToClipboard(
|
||||
context: context,
|
||||
text: item.timelineEntry.body,
|
||||
message: 'Post link copied to clipboard',
|
||||
);
|
||||
break;
|
||||
default:
|
||||
//do nothing
|
||||
}
|
||||
}, itemBuilder: (context) {
|
||||
return options
|
||||
.map((o) => PopupMenuItem(value: o, child: Text(o)))
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> deleteEntry() async {
|
||||
setState(() {
|
||||
isProcessing = true;
|
||||
});
|
||||
final confirm =
|
||||
await showYesNoDialog(context, 'Delete ${isPost ? "Post" : "Comment"}');
|
||||
if (confirm == true) {
|
||||
await getIt<ActiveProfileSelector<TimelineManager>>()
|
||||
.activeEntry
|
||||
.andThenAsync(
|
||||
(tm) async => await tm.deleteEntryById(item.timelineEntry.id));
|
||||
}
|
||||
setState(() {
|
||||
isProcessing = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,10 @@ import '../../globals.dart';
|
|||
import '../../models/timeline_entry.dart';
|
||||
import '../../services/timeline_manager.dart';
|
||||
import '../../utils/active_profile_selector.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;
|
||||
|
||||
|
@ -21,7 +18,6 @@ class InteractionsBarControl extends StatefulWidget {
|
|||
required this.entry,
|
||||
required this.isMine,
|
||||
required this.showOpenControl,
|
||||
required this.openRemote,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -94,23 +90,6 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
context.push('/comment/new?parent_id=${widget.entry.id}');
|
||||
}
|
||||
|
||||
Future<void> deleteEntry() async {
|
||||
setState(() {
|
||||
isProcessing = true;
|
||||
});
|
||||
final confirm =
|
||||
await showYesNoDialog(context, 'Delete ${isPost ? "Post" : "Comment"}');
|
||||
if (confirm == true) {
|
||||
await getIt<ActiveProfileSelector<TimelineManager>>()
|
||||
.activeEntry
|
||||
.andThenAsync(
|
||||
(tm) async => await tm.deleteEntryById(widget.entry.id));
|
||||
}
|
||||
setState(() {
|
||||
isProcessing = false;
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> unResharePost() async {
|
||||
setState(() {
|
||||
isProcessing = true;
|
||||
|
@ -133,55 +112,6 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
});
|
||||
}
|
||||
|
||||
Future<void> openAction(BuildContext context) async {
|
||||
const editStatus = 'Edit Post';
|
||||
const goToPost = 'Go to Post';
|
||||
const copyUrl = 'Copy URL';
|
||||
const openExternal = 'Open In Browser';
|
||||
final options = [
|
||||
if (widget.showOpenControl && !widget.openRemote) goToPost,
|
||||
if (widget.isMine && !widget.entry.youReshared) editStatus,
|
||||
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 editStatus:
|
||||
if (widget.entry.parentId.isEmpty) {
|
||||
context.push('/post/edit/${widget.entry.id}');
|
||||
} else {
|
||||
context.push('/comment/edit/${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()}');
|
||||
|
@ -197,7 +127,7 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
: () {
|
||||
context.push('/likes/${widget.entry.id}');
|
||||
},
|
||||
child: Text('$likes likes'),
|
||||
child: Text('$likes Likes'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: reshares == 0
|
||||
|
@ -205,7 +135,7 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
: () {
|
||||
context.push('/reshares/${widget.entry.id}');
|
||||
},
|
||||
child: Text('$reshares reshares'),
|
||||
child: Text('$reshares Reshares'),
|
||||
),
|
||||
widget.showOpenControl
|
||||
? TextButton(
|
||||
|
@ -215,9 +145,9 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
context.push(
|
||||
'/post/view/${widget.entry.id}/${widget.entry.id}');
|
||||
},
|
||||
child: Text('$comments comments'),
|
||||
child: Text('$comments Comments'),
|
||||
)
|
||||
: Text('$comments comments'),
|
||||
: Text('$comments Comments'),
|
||||
],
|
||||
),
|
||||
Row(children: [
|
||||
|
@ -244,36 +174,6 @@ class _InteractionsBarControlState extends State<InteractionsBarControl> {
|
|||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -45,66 +45,73 @@ class StatusHeaderControl extends StatelessWidget {
|
|||
},
|
||||
);
|
||||
|
||||
return Wrap(
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
ImageControl(
|
||||
imageUrl: author.avatarUrl.toString(),
|
||||
iconOverride: Icon(Icons.person),
|
||||
width: 32.0,
|
||||
onTap: () => goToProfile(context, author.id),
|
||||
),
|
||||
const HorizontalPadding(),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.end,
|
||||
runSpacing: 5.0,
|
||||
children: [
|
||||
GestureDetector(
|
||||
ImageControl(
|
||||
imageUrl: author.avatarUrl.toString(),
|
||||
iconOverride: Icon(Icons.person),
|
||||
width: 32.0,
|
||||
onTap: () => goToProfile(context, author.id),
|
||||
child: Text(
|
||||
author.name,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
const HorizontalPadding(),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
ElapsedDateUtils.epochSecondsToString(
|
||||
entry.backdatedTimestamp),
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
const HorizontalPadding(),
|
||||
Icon(
|
||||
entry.isPublic ? Icons.public : Icons.lock,
|
||||
color: Theme.of(context).hintColor,
|
||||
size: Theme.of(context).textTheme.caption?.fontSize,
|
||||
GestureDetector(
|
||||
onTap: () => goToProfile(context, author.id),
|
||||
child: Text(
|
||||
author.name,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Text(
|
||||
// entry.id,
|
||||
// ),
|
||||
if (reshareAuthor.isNotEmpty) ...[
|
||||
const HorizontalPadding(width: 3.0),
|
||||
const Text('reshared post by: '),
|
||||
const HorizontalPadding(width: 3.0),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ImageControl(
|
||||
imageUrl: reshareAuthor.avatarUrl.toString(),
|
||||
iconOverride: Icon(Icons.person),
|
||||
width: 32.0,
|
||||
onTap: () => goToProfile(context, reshareAuthor.id),
|
||||
),
|
||||
const HorizontalPadding(width: 3.0),
|
||||
GestureDetector(
|
||||
onTap: () => goToProfile(context, reshareAuthor.id),
|
||||
child: Text(
|
||||
reshareAuthor.name,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (reshareAuthor.isNotEmpty) ...[
|
||||
const HorizontalPadding(width: 3.0),
|
||||
const Icon(Icons.repeat),
|
||||
const HorizontalPadding(width: 3.0),
|
||||
ImageControl(
|
||||
imageUrl: reshareAuthor.avatarUrl.toString(),
|
||||
iconOverride: Icon(Icons.person),
|
||||
width: 32.0,
|
||||
onTap: () => goToProfile(context, reshareAuthor.id),
|
||||
),
|
||||
const HorizontalPadding(width: 3.0),
|
||||
GestureDetector(
|
||||
onTap: () => goToProfile(context, reshareAuthor.id),
|
||||
child: Text(
|
||||
reshareAuthor.name,
|
||||
style: Theme.of(context).textTheme.bodyText1,
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
ElapsedDateUtils.epochSecondsToString(entry.backdatedTimestamp),
|
||||
style: Theme.of(context).textTheme.caption,
|
||||
),
|
||||
),
|
||||
],
|
||||
const HorizontalPadding(),
|
||||
Icon(
|
||||
entry.isPublic ? Icons.public : Icons.lock,
|
||||
color: Theme.of(context).hintColor,
|
||||
size: Theme.of(context).textTheme.caption?.fontSize,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ class _PostScreenState extends State<PostScreen> {
|
|||
originalItem: post,
|
||||
scrollToId: widget.goToId,
|
||||
openRemote: true,
|
||||
showStatusOpenButton: true,
|
||||
showStatusOpenButton: false,
|
||||
isRoot: true,
|
||||
),
|
||||
),
|
||||
|
|
Ładowanie…
Reference in New Issue