Added reaction overview with list of names who reacted

This commit is contained in:
2023-07-10 14:07:25 +02:00
parent 5d62f1b863
commit 575343ff82
8 changed files with 323 additions and 132 deletions

View File

@ -0,0 +1,27 @@
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:http/http.dart';
import '../../../apiParams.dart';
import '../talkApi.dart';
import 'getReactionsResponse.dart';
class GetReactions extends TalkApi<GetReactionsResponse> {
String chatToken;
int messageId;
GetReactions({required this.chatToken, required this.messageId}) : super("v1/reaction/$chatToken/$messageId", null);
@override
assemble(String raw) {
log(raw);
return GetReactionsResponse.fromJson(jsonDecode(raw)['ocs']);
}
@override
Future<Response>? request(Uri uri, ApiParams? body, Map<String, String>? headers) {
return http.get(uri, headers: headers);
}
}

View File

@ -0,0 +1,31 @@
import 'package:json_annotation/json_annotation.dart';
part 'getReactionsResponse.g.dart';
@JsonSerializable(explicitToJson: true)
class GetReactionsResponse {
Map<String, List<GetReactionsResponseObject>> data;
GetReactionsResponse(this.data);
factory GetReactionsResponse.fromJson(Map<String, dynamic> json) => _$GetReactionsResponseFromJson(json);
Map<String, dynamic> toJson() => _$GetReactionsResponseToJson(this);
}
@JsonSerializable()
class GetReactionsResponseObject {
GetReactionsResponseObjectActorType actorType;
String actorId;
String actorDisplayName;
int timestamp;
GetReactionsResponseObject(this.actorType, this.actorId, this.actorDisplayName, this.timestamp);
factory GetReactionsResponseObject.fromJson(Map<String, dynamic> json) => _$GetReactionsResponseObjectFromJson(json);
Map<String, dynamic> toJson() => _$GetReactionsResponseObjectToJson(this);
}
enum GetReactionsResponseObjectActorType {
@JsonValue("guests") guests,
@JsonValue("users") users,
}

View File

@ -0,0 +1,52 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'getReactionsResponse.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
GetReactionsResponse _$GetReactionsResponseFromJson(
Map<String, dynamic> json) =>
GetReactionsResponse(
(json['data'] as Map<String, dynamic>).map(
(k, e) => MapEntry(
k,
(e as List<dynamic>)
.map((e) => GetReactionsResponseObject.fromJson(
e as Map<String, dynamic>))
.toList()),
),
);
Map<String, dynamic> _$GetReactionsResponseToJson(
GetReactionsResponse instance) =>
<String, dynamic>{
'data': instance.data
.map((k, e) => MapEntry(k, e.map((e) => e.toJson()).toList())),
};
GetReactionsResponseObject _$GetReactionsResponseObjectFromJson(
Map<String, dynamic> json) =>
GetReactionsResponseObject(
$enumDecode(
_$GetReactionsResponseObjectActorTypeEnumMap, json['actorType']),
json['actorId'] as String,
json['actorDisplayName'] as String,
json['timestamp'] as int,
);
Map<String, dynamic> _$GetReactionsResponseObjectToJson(
GetReactionsResponseObject instance) =>
<String, dynamic>{
'actorType':
_$GetReactionsResponseObjectActorTypeEnumMap[instance.actorType]!,
'actorId': instance.actorId,
'actorDisplayName': instance.actorDisplayName,
'timestamp': instance.timestamp,
};
const _$GetReactionsResponseObjectActorTypeEnumMap = {
GetReactionsResponseObjectActorType.guests: 'guests',
GetReactionsResponseObjectActorType.users: 'users',
};

View File

@ -53,7 +53,7 @@ GetRoomResponseObject _$GetRoomResponseObjectFromJson(
json['status'] as String?,
json['statusIcon'] as String?,
json['statusMessage'] as String?,
);
)..sort = json['sort'] as String?;
Map<String, dynamic> _$GetRoomResponseObjectToJson(
GetRoomResponseObject instance) =>
@ -90,6 +90,7 @@ Map<String, dynamic> _$GetRoomResponseObjectToJson(
'status': instance.status,
'statusIcon': instance.statusIcon,
'statusMessage': instance.statusMessage,
'sort': instance.sort,
};
const _$GetRoomResponseObjectConversationTypeEnumMap = {

View File

@ -18,6 +18,7 @@ import '../../../theming/appTheme.dart';
import '../../../widget/debug/debugTile.dart';
import '../files/fileElement.dart';
import 'chatMessage.dart';
import 'messageReactions.dart';
class ChatBubble extends StatefulWidget {
final BuildContext context;
@ -199,6 +200,20 @@ class _ChatBubbleState extends State<ChatBubble> {
),
],
),
const Divider(),
Visibility(
visible: true,
child: ListTile(
leading: const Icon(Icons.add_reaction_outlined),
title: const Text("Reaktionen"),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) => MessageReactions(
token: widget.chatData.token,
messageId: widget.bubbleData.id,
)));
},
),
),
Visibility(
visible: !message.containsFile && widget.bubbleData.messageType == GetRoomResponseObjectMessageType.comment,
child: ListTile(

View File

@ -0,0 +1,73 @@
import 'dart:developer';
import 'package:flutter/material.dart';
import '../../../api/marianumcloud/talk/getReactions/getReactions.dart';
import '../../../api/marianumcloud/talk/getReactions/getReactionsResponse.dart';
import '../../../model/accountData.dart';
import '../../../widget/centeredLeading.dart';
import '../../../widget/loadingSpinner.dart';
import '../../../widget/unimplementedDialog.dart';
class MessageReactions extends StatefulWidget {
final String token;
final int messageId;
const MessageReactions({super.key, required this.token, required this.messageId});
@override
State<MessageReactions> createState() => _MessageReactionsState();
}
class _MessageReactionsState extends State<MessageReactions> {
late Future<GetReactionsResponse> data;
@override
void initState() {
super.initState();
data = GetReactions(chatToken: widget.token, messageId: widget.messageId).run();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Reaktionen"),
),
body: FutureBuilder(
future: data,
builder: (context, snapshot) {
if(snapshot.data == null) return const LoadingSpinner();
log(snapshot.data!.toJson().toString());
return ListView(
children: [
...snapshot.data!.data.entries.map<Widget>((entry) {
return ExpansionTile(
textColor: Theme.of(context).colorScheme.onSurface,
collapsedTextColor: Theme.of(context).colorScheme.onSurface,
iconColor: Theme.of(context).colorScheme.onSurface,
collapsedIconColor: Theme.of(context).colorScheme.onSurface,
subtitle: const Text("Tippe für mehr"),
leading: Text(entry.key),
title: Text("${entry.value.length} mal reagiert"),
children: entry.value.map((e) {
bool isSelf = AccountData().getUsername() == e.actorId;
return ListTile(
leading: const CenteredLeading(Icon(Icons.person)),
title: Text(e.actorDisplayName),
subtitle: isSelf ? const Text("Du") : e.actorType == GetReactionsResponseObjectActorType.guests ? const Text("Gast") : null,
trailing: isSelf ? null : IconButton(
onPressed: () => UnimplementedDialog.show(context),
icon: const Icon(Icons.textsms_outlined),
),
);
}).toList(),
);
}).toList()
],
);
},
),
);
}
}