Files
Client/lib/view/pages/talk/components/chatTile.dart
T
2026-05-04 13:54:39 +02:00

197 lines
7.3 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:jiffy/jiffy.dart';
import '../../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart';
import '../../../../api/marianumcloud/talk/leaveRoom/leaveRoom.dart';
import '../../../../api/marianumcloud/talk/room/getRoomResponse.dart';
import '../../../../api/marianumcloud/talk/setFavorite/setFavorite.dart';
import '../../../../api/marianumcloud/talk/setReadMarker/setReadMarker.dart';
import '../../../../api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart';
import '../../../../model/accountData.dart';
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
import '../../../../state/app/modules/chatList/bloc/chat_list_bloc.dart';
import '../../../../widget/confirmDialog.dart';
import '../../../../widget/debug/debugTile.dart';
import '../../../../widget/userAvatar.dart';
import '../chatView.dart';
import '../talkNavigator.dart';
class ChatTile extends StatefulWidget {
final GetRoomResponseObject data;
final bool disableContextActions;
final bool hasDraft;
const ChatTile({super.key, required this.data, this.disableContextActions = false, this.hasDraft = false});
@override
State<ChatTile> createState() => _ChatTileState();
}
class _ChatTileState extends State<ChatTile> {
String? selfUsername;
@override
void initState() {
super.initState();
AccountData().waitForPopulation().then((_) {
if (!mounted) return;
setState(() => selfUsername = AccountData().isPopulated() ? AccountData().getUsername() : null);
});
}
void _refreshList() => context.read<ChatListBloc>().refresh();
void setCurrentAsRead() {
SetReadMarker(
widget.data.token,
true,
setReadMarkerParams: SetReadMarkerParams(lastReadMessage: widget.data.lastMessage.id),
).run().then((_) {
if (!mounted) return;
_refreshList();
});
}
@override
Widget build(BuildContext context) {
final chatBloc = context.watch<ChatBloc>();
final isGroup = widget.data.type != GetRoomResponseObjectConversationType.oneToOne;
final circleAvatar = UserAvatar(id: isGroup ? widget.data.token : widget.data.name, isGroup: isGroup);
return ListTile(
style: ListTileStyle.list,
tileColor: chatBloc.state.data?.currentToken == widget.data.token && TalkNavigator.isSecondaryVisible(context)
? Theme.of(context).primaryColor.withAlpha(100)
: null,
leading: Stack(
children: [
circleAvatar,
Visibility(
visible: widget.data.isFavorite,
child: Positioned(
right: 0,
bottom: 0,
child: Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor.withAlpha(200),
borderRadius: BorderRadius.circular(90.0),
),
child: const Icon(Icons.star, color: Colors.amberAccent, size: 15),
),
),
)
],
),
title: Row(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(child: Text(widget.data.displayName, overflow: TextOverflow.ellipsis)),
if (widget.hasDraft) ...[
const SizedBox(width: 5),
const Icon(Icons.edit_outlined, size: 15),
],
],
),
subtitle: Text(
'${Jiffy.parseFromMillisecondsSinceEpoch(widget.data.lastMessage.timestamp * 1000).fromNow()}: '
'${RichObjectStringProcessor.parseToString(widget.data.lastMessage.message.replaceAll("\n", " "), widget.data.lastMessage.messageParameters)}',
overflow: TextOverflow.ellipsis,
),
trailing: widget.data.unreadMessages <= 0
? null
: Container(
padding: const EdgeInsets.all(1),
decoration: BoxDecoration(
color: Theme.of(context).primaryColor,
borderRadius: BorderRadius.circular(30),
),
constraints: const BoxConstraints(minWidth: 20, minHeight: 20),
child: Text(
'${widget.data.unreadMessages}',
style: const TextStyle(color: Colors.white, fontSize: 15),
textAlign: TextAlign.center,
),
),
onTap: () {
if (selfUsername == null) return;
setCurrentAsRead();
final view = ChatView(room: widget.data, selfId: selfUsername!, avatar: circleAvatar);
TalkNavigator.pushSplitView(context, view, overrideToSingleSubScreen: true);
context.read<ChatBloc>().setToken(widget.data.token);
},
onLongPress: () {
if (widget.disableContextActions) return;
showDialog(context: context, builder: (dialogCtx) => SimpleDialog(
children: [
Visibility(
visible: widget.data.unreadMessages > 0,
replacement: ListTile(
leading: const Icon(Icons.mark_chat_unread_outlined),
title: const Text('Als ungelesen markieren'),
onTap: () {
SetReadMarker(widget.data.token, false).run().then((_) {
if (mounted) _refreshList();
});
Navigator.of(dialogCtx).pop();
},
),
child: ListTile(
leading: const Icon(Icons.mark_chat_read_outlined),
title: const Text('Als gelesen markieren'),
onTap: () {
setCurrentAsRead();
Navigator.of(dialogCtx).pop();
},
),
),
Visibility(
visible: widget.data.isFavorite,
replacement: ListTile(
leading: const Icon(Icons.star_outline),
title: const Text('Zu Favoriten hinzufügen'),
onTap: () {
SetFavorite(widget.data.token, true).run().then((_) {
if (mounted) _refreshList();
});
Navigator.of(dialogCtx).pop();
},
),
child: ListTile(
leading: const Icon(Icons.stars_outlined),
title: const Text('Von Favoriten entfernen'),
onTap: () {
SetFavorite(widget.data.token, false).run().then((_) {
if (mounted) _refreshList();
});
Navigator.of(dialogCtx).pop();
},
),
),
ListTile(
leading: const Icon(Icons.delete_outline),
title: const Text('Konversation verlassen'),
onTap: () {
ConfirmDialog(
title: 'Chat verlassen',
content: 'Du benötigst ggf. eine Einladung um erneut beizutreten.',
confirmButton: 'Löschen',
onConfirm: () {
LeaveRoom(widget.data.token).run().then((_) {
if (mounted) _refreshList();
});
Navigator.of(dialogCtx).pop();
},
).asDialog(dialogCtx);
},
),
DebugTile(dialogCtx).jsonData(widget.data.toJson()),
],
));
},
);
}
}