Surface/lib/widgets/post/post_reaction.dart

154 lines
5.1 KiB
Dart
Raw Normal View History

2024-11-12 12:47:40 +00:00
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:gap/gap.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:provider/provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:surface/providers/sn_network.dart';
import 'package:surface/types/post.dart';
import 'package:surface/types/reaction.dart';
import 'package:surface/widgets/dialog.dart';
class PostReactionPopup extends StatefulWidget {
final SnPost data;
2024-11-23 10:04:30 +00:00
final Function(Map<String, int> value, int attr, int delta)? onChanged;
2024-11-12 12:47:40 +00:00
const PostReactionPopup({super.key, required this.data, this.onChanged});
@override
State<PostReactionPopup> createState() => _PostReactionPopupState();
}
class _PostReactionPopupState extends State<PostReactionPopup> {
bool _isSubmitting = false;
late Map<String, int> _reactions;
Future<void> _reactPost(String symbol, int attitude) async {
if (_isSubmitting) return;
final sn = context.read<SnNetworkProvider>();
try {
setState(() => _isSubmitting = true);
final resp = await sn.client.post(
'/cgi/co/posts/${widget.data.id}/react',
data: {
'symbol': symbol,
'attitude': attitude,
},
);
if (resp.statusCode == 201) {
_reactions[symbol] = (_reactions[symbol] ?? 0) + 1;
// ignore: use_build_context_synchronously
if (context.mounted) context.showSnackbar('postReactCompleted'.tr());
if (widget.onChanged != null) {
widget.onChanged!(
_reactions,
2024-11-23 10:04:30 +00:00
kTemplateReactions[symbol]!.attitude,
2024-11-12 12:47:40 +00:00
1,
);
}
} else if (resp.statusCode == 204) {
_reactions[symbol] = (_reactions[symbol] ?? 0) - 1;
// ignore: use_build_context_synchronously
if (context.mounted) context.showSnackbar('postReactUncompleted'.tr());
if (widget.onChanged != null) {
widget.onChanged!(
_reactions,
2024-11-23 10:04:30 +00:00
kTemplateReactions[symbol]!.attitude,
2024-11-12 12:47:40 +00:00
-1,
);
}
}
} catch (err) {
// ignore: use_build_context_synchronously
if (context.mounted) context.showErrorDialog(err);
} finally {
setState(() => _isSubmitting = false);
}
}
@override
void initState() {
super.initState();
_reactions = Map.from(widget.data.metric.reactionList);
}
@override
Widget build(BuildContext context) {
return SizedBox(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Icon(Symbols.mood, size: 24),
const Gap(16),
Text('postReactions')
.tr()
.textStyle(Theme.of(context).textTheme.titleLarge!),
],
).padding(horizontal: 20, top: 16, bottom: 12),
2024-11-23 10:04:30 +00:00
Container(
color: Theme.of(context).colorScheme.surfaceContainer,
child: Row(
children: [
const Icon(Symbols.thumb_up, size: 16),
const Gap(8),
Text('postReactionUpvote').plural(widget.data.totalUpvote),
const Gap(24),
const Icon(Symbols.thumb_down, size: 16),
const Gap(8),
Text('postReactionDownvote').plural(widget.data.totalDownvote),
const Gap(24),
Icon(
widget.data.totalUpvote >= widget.data.totalDownvote
? Symbols.trending_up
: Symbols.trending_down,
size: 16,
),
const Gap(8),
Text('postReactionSocialPoint').plural(
widget.data.totalUpvote - widget.data.totalDownvote,
),
],
).padding(vertical: 8, horizontal: 24),
),
2024-11-12 12:47:40 +00:00
Expanded(
2024-11-23 10:04:30 +00:00
child: GridView.extent(
2024-11-12 12:47:40 +00:00
crossAxisSpacing: 4,
mainAxisSpacing: 4,
2024-11-23 10:04:30 +00:00
maxCrossAxisExtent: 120,
2024-11-12 12:47:40 +00:00
children: kTemplateReactions.entries.map((e) {
return InkWell(
onTap: () {
if (widget.onChanged == null) return;
_reactPost(e.key, e.value.attitude).then((_) {
if (context.mounted) Navigator.pop(context);
});
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(e.value.icon).fontSize(40),
Text(
e.key,
style: const TextStyle(fontFamily: 'monospace'),
),
const Gap(6),
Text(
'x${_reactions[e.key]?.toString() ?? '0'}',
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
);
}).toList(),
),
),
],
),
);
}
}