import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:gap/gap.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:island/models/post.dart'; import 'package:island/pods/network.dart'; import 'package:island/services/time.dart'; import 'package:island/widgets/alert.dart'; import 'package:island/widgets/post/post_item.dart'; import 'package:island/widgets/post/post_shared.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:super_context_menu/super_context_menu.dart'; class PostItemCreator extends HookConsumerWidget { final Color? backgroundColor; final SnPost item; final EdgeInsets? padding; final bool isOpenable; final Function? onRefresh; final Function(SnPost)? onUpdate; const PostItemCreator({ super.key, required this.item, this.backgroundColor, this.padding, this.isOpenable = true, this.onRefresh, this.onUpdate, }); @override Widget build(BuildContext context, WidgetRef ref) { final renderingPadding = padding ?? const EdgeInsets.symmetric(horizontal: 16, vertical: 16); return ContextMenuWidget( menuProvider: (_) { return Menu( children: [ MenuAction( title: 'edit'.tr(), image: MenuImage.icon(Symbols.edit), callback: () { context .pushNamed('postEdit', pathParameters: {'id': item.id}) .then((value) { if (value != null) { onRefresh?.call(); } }); }, ), MenuAction( title: 'delete'.tr(), image: MenuImage.icon(Symbols.delete), callback: () { showConfirmAlert('deletePostHint'.tr(), 'deletePost'.tr()).then( (confirm) { if (confirm) { final client = ref.watch(apiClientProvider); client .delete('/sphere/posts/${item.id}') .catchError((err) { showErrorAlert(err); return err; }) .then((_) { onRefresh?.call(); }); } }, ); }, ), MenuSeparator(), MenuAction( title: 'copyLink'.tr(), image: MenuImage.icon(Symbols.link), callback: () { context.pushNamed( 'postDetail', pathParameters: {'id': item.id}, ); }, ), ], ); }, child: Material( color: backgroundColor ?? Theme.of(context).colorScheme.surface, child: InkWell( borderRadius: BorderRadius.circular(12), onTap: () { if (isOpenable) { context.pushNamed('postDetail', pathParameters: {'id': item.id}); } }, child: Padding( padding: renderingPadding, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ PostHeader(item: item), PostBody(item: item), ReferencedPostWidget(item: item), const Gap(16), _buildAnalyticsSection(context), ], ), ), ), ), ); } Widget _buildAnalyticsSection(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Analytics', style: Theme.of(context).textTheme.titleSmall), const Gap(8), Card( elevation: 1, margin: EdgeInsets.zero, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), side: BorderSide( color: Theme.of(context).colorScheme.outline.withOpacity(0.3), ), ), child: Padding( padding: const EdgeInsets.all(16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _buildMetricItem( context, Symbols.visibility, 'Views', '${item.viewsUnique} / ${item.viewsTotal}', 'Unique / Total', ), _buildMetricItem( context, Symbols.thumb_up, 'Upvotes', '${item.upvotes}', null, ), _buildMetricItem( context, Symbols.thumb_down, 'Downvotes', '${item.downvotes}', null, ), ], ), ), ), const Gap(16), if (item.reactionsCount.isNotEmpty) _buildReactionsSection(context), if (item.meta != null && item.meta!.isNotEmpty) _buildMetadataSection(context), const Gap(16), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Created: ${item.createdAt?.formatSystem() ?? ''}', style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.secondary, ), ), if (item.editedAt != null) Text( 'Edited: ${item.editedAt!.formatSystem()}', style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.secondary, ), ), ], ), ], ); } Widget _buildMetricItem( BuildContext context, IconData icon, String label, String value, String? subtitle, ) { return Column( children: [ Icon(icon, size: 20, color: Theme.of(context).colorScheme.primary), const Gap(4), Text( label, style: TextStyle( fontSize: 12, color: Theme.of(context).colorScheme.secondary, ), ), Text( value, style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold), ), if (subtitle != null) Text( subtitle, style: TextStyle( fontSize: 10, color: Theme.of(context).colorScheme.secondary, ), ), ], ); } Widget _buildReactionsSection(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'reactions'.plural( item.reactionsCount.isNotEmpty ? item.reactionsCount.values.reduce((a, b) => a + b) : 0, ), style: TextStyle( fontSize: 14, fontWeight: FontWeight.w500, color: Theme.of(context).colorScheme.secondary, ), ), const Gap(8), PostReactionList( parentId: item.id, reactions: item.reactionsCount, reactionsMade: item.reactionsMade, padding: EdgeInsets.zero, ), const Gap(16), ], ); } Widget _buildMetadataSection(BuildContext context) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Gap(16), Text('Metadata', style: Theme.of(context).textTheme.titleSmall), const Gap(8), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Theme.of( context, ).colorScheme.surfaceVariant.withOpacity(0.5), borderRadius: BorderRadius.circular(8), border: Border.all( color: Theme.of(context).colorScheme.outline.withOpacity(0.3), ), ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ for (final entry in item.meta!.entries) Padding( padding: const EdgeInsets.only(bottom: 8), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( '${entry.key}: ', style: const TextStyle( fontWeight: FontWeight.bold, fontSize: 12, ), ), Expanded( child: Text( '${entry.value}', style: const TextStyle(fontSize: 12), ), ), ], ), ), ], ), ), ], ); } }