170 lines
5.3 KiB
Dart
170 lines
5.3 KiB
Dart
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<String, Map<String, dynamic>> 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<ReactionList> createState() => _ReactionListState();
|
|
}
|
|
|
|
class _ReactionListState extends State<ReactionList> {
|
|
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<void> 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: <String, String>{
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: jsonEncode(<String, dynamic>{
|
|
'symbol': symbol,
|
|
'attitude': attitude,
|
|
}),
|
|
);
|
|
if (res.statusCode == 201) {
|
|
widget.onReact(symbol, 1);
|
|
} else if (res.statusCode == 204) {
|
|
widget.onReact(symbol, -1);
|
|
} else {
|
|
var message = utf8.decode(res.bodyBytes);
|
|
ScaffoldMessenger.of(context).showSnackBar(
|
|
SnackBar(content: Text("Something went wrong... $message")),
|
|
);
|
|
}
|
|
setState(() => isSubmitting = false);
|
|
}
|
|
|
|
List<MapEntry<String, dynamic>> 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(),
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|