import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:solaragent/auth.dart'; import 'package:solaragent/models/feed.dart'; class ReactionList extends StatefulWidget { static const Map> emojis = { 'thumb_up': {'icon': '👍', 'attitude': 1}, 'clap': {'icon': '👏', 'attitude': 1} }; final Feed parent; final void Function(String symbol, int num) onReact; const ReactionList({super.key, required this.parent, required this.onReact}); @override State createState() => _ReactionListState(); } class _ReactionListState extends State { bool isSubmitting = false; void viewReactMenu(BuildContext context) { showModalBottomSheet( context: context, builder: (context) { return ListView.builder( padding: const EdgeInsets.all(8), itemCount: ReactionList.emojis.length, itemBuilder: (BuildContext context, int index) { var element = ReactionList.emojis.entries.toList()[index]; return InkWell( borderRadius: const BorderRadius.all( Radius.circular(64), ), onTap: () async { await doReact(element.key, element.value['attitude']); }, child: ListTile( title: Text(element.value['icon']), subtitle: Text( ":${element.key}:", style: const TextStyle(fontFamily: "monospace"), ), ), ); }); }, ); } Future doReact(String symbol, int attitude) async { if (!await authClient.isAuthorized()) return; var dataset = "${widget.parent.modelType}s"; var alias = widget.parent.id; var uri = Uri.parse( "https://co.solsynth.dev/api/p/$dataset/$alias/react", ); setState(() => isSubmitting = true); var res = await authClient.client!.post( uri, headers: { 'Content-Type': 'application/json', }, body: jsonEncode({ 'symbol': symbol, 'attitude': attitude, }), ); if (res.statusCode == 201) { widget.onReact(symbol, 1); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Your reaction has been added onto this post."), ), ); } else if (res.statusCode == 204) { widget.onReact(symbol, -1); ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text("Your reaction has been removed from this post."), ), ); } else { var message = utf8.decode(res.bodyBytes); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("Something went wrong... $message")), ); } setState(() => isSubmitting = false); } List> getReactionEntries() => widget.parent.reactionList?.entries.toList() ?? List.empty(); @override Widget build(BuildContext context) { return Column( children: [ // Title Container( padding: const EdgeInsets.only(left: 10, right: 10, top: 20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Padding( padding: const EdgeInsets.symmetric( horizontal: 8.0, vertical: 12.0, ), child: Text( 'Reactions', style: Theme.of(context).textTheme.headlineSmall, ), ), FutureBuilder( future: authClient.isAuthorized(), builder: (context, snapshot) { if (snapshot.hasData && snapshot.data == true) { return TextButton.icon( icon: const Icon(Icons.add_reaction), label: const Text("REACT"), onPressed: isSubmitting ? null : () => viewReactMenu(context), ); } else { return Container(); } }, ), ], ), ), // Loading indicator isSubmitting ? const LinearProgressIndicator() : Container(), // Data list Expanded( child: ListView.separated( itemCount: getReactionEntries().length, itemBuilder: (BuildContext context, int index) { var element = getReactionEntries()[index]; return InkWell( onTap: isSubmitting ? null : () { doReact( element.key, ReactionList.emojis[element.key]!['attitude'], ); }, child: Container( padding: const EdgeInsets.symmetric(horizontal: 6.0), child: ListTile( title: Text( "${ReactionList.emojis[element.key]!['icon']} x${element.value.toString()}", ), subtitle: Text( ":${element.key}:", style: const TextStyle(fontFamily: "monospace"), ), ), )); }, separatorBuilder: (context, index) => const Divider(), ), ), ], ); } }