✨ Reactions
This commit is contained in:
@ -9,16 +9,16 @@ import 'package:solaragent/models/pagination.dart';
|
||||
import 'package:solaragent/router.dart';
|
||||
import 'package:solaragent/widgets/feed.dart';
|
||||
|
||||
class CommentListWidget extends StatefulWidget {
|
||||
class CommentList extends StatefulWidget {
|
||||
final Feed parent;
|
||||
|
||||
const CommentListWidget({super.key, required this.parent});
|
||||
const CommentList({super.key, required this.parent});
|
||||
|
||||
@override
|
||||
State<CommentListWidget> createState() => _CommentListWidgetState();
|
||||
State<CommentList> createState() => _CommentListState();
|
||||
}
|
||||
|
||||
class _CommentListWidgetState extends State<CommentListWidget> {
|
||||
class _CommentListState extends State<CommentList> {
|
||||
static const pageSize = 5;
|
||||
|
||||
final client = Client();
|
||||
|
169
lib/widgets/posts/reactions.dart
Normal file
169
lib/widgets/posts/reactions.dart
Normal file
@ -0,0 +1,169 @@
|
||||
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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user