diff --git a/lib/api/marianumcloud/talk/leaveRoom/leaveRoom.dart b/lib/api/marianumcloud/talk/leaveRoom/leaveRoom.dart new file mode 100644 index 0000000..7f22874 --- /dev/null +++ b/lib/api/marianumcloud/talk/leaveRoom/leaveRoom.dart @@ -0,0 +1,19 @@ + +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; + +import '../talkApi.dart'; +class LeaveRoom extends TalkApi<void> { + String chatToken; + + LeaveRoom(this.chatToken) : super("v4/room/$chatToken/participants/self", null); + + @override + assemble(String raw) { + } + + @override + Future<Response> request(Uri uri, Object? body, Map<String, String>? headers) { + return http.delete(uri, headers: headers); + } +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/setFavorite/setFavorite.dart b/lib/api/marianumcloud/talk/setFavorite/setFavorite.dart new file mode 100644 index 0000000..ce331cf --- /dev/null +++ b/lib/api/marianumcloud/talk/setFavorite/setFavorite.dart @@ -0,0 +1,27 @@ + +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; + +import '../talkApi.dart'; + +class SetFavorite extends TalkApi<void> { + String chatToken; + bool favoriteState; + + SetFavorite(this.chatToken, this.favoriteState) : super("v4/room/$chatToken/favorite", null); + + @override + assemble(String raw) { + + } + + @override + Future<Response> request(Uri uri, Object? body, Map<String, String>? headers) { + if(favoriteState) { + return http.post(uri, headers: headers); + } else { + return http.delete(uri, headers: headers); + } + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart b/lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart new file mode 100644 index 0000000..68593f4 --- /dev/null +++ b/lib/api/marianumcloud/talk/setReadMarker/setReadMarker.dart @@ -0,0 +1,32 @@ + +import 'package:http/http.dart' as http; +import 'package:http/http.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart'; + +import '../talkApi.dart'; + +class SetReadMarker extends TalkApi<void> { + String chatToken; + bool readState; + SetReadMarkerParams? setReadMarkerParams; + + SetReadMarker(this.chatToken, this.readState, {this.setReadMarkerParams}) : super("v1/chat/$chatToken/read", null, getParameters: setReadMarkerParams?.toJson()) { + if(readState) assert(setReadMarkerParams?.lastReadMessage != null); + } + + @override + assemble(String raw) { + + } + + @override + Future<Response> request(Uri uri, Object? body, Map<String, String>? headers) { + if(readState) { + + return http.post(uri, headers: headers); + } else { + return http.delete(uri, headers: headers); + } + } + +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart b/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart new file mode 100644 index 0000000..7bacd17 --- /dev/null +++ b/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart @@ -0,0 +1,16 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:marianum_mobile/api/apiParams.dart'; + +part 'setReadMarkerParams.g.dart'; + +@JsonSerializable() +class SetReadMarkerParams extends ApiParams { + int? lastReadMessage; + + SetReadMarkerParams({ + this.lastReadMessage + }); + + factory SetReadMarkerParams.fromJson(Map<String, dynamic> json) => _$SetReadMarkerParamsFromJson(json); + Map<String, dynamic> toJson() => _$SetReadMarkerParamsToJson(this); +} \ No newline at end of file diff --git a/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart b/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart new file mode 100644 index 0000000..fa5f7fe --- /dev/null +++ b/lib/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'setReadMarkerParams.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +SetReadMarkerParams _$SetReadMarkerParamsFromJson(Map<String, dynamic> json) => + SetReadMarkerParams( + lastReadMessage: json['lastReadMessage'] as int?, + ); + +Map<String, dynamic> _$SetReadMarkerParamsToJson( + SetReadMarkerParams instance) => + <String, dynamic>{ + 'lastReadMessage': instance.lastReadMessage, + }; diff --git a/lib/view/pages/talk/chatList.dart b/lib/view/pages/talk/chatList.dart index bd50424..b674b4a 100644 --- a/lib/view/pages/talk/chatList.dart +++ b/lib/view/pages/talk/chatList.dart @@ -2,13 +2,18 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:jiffy/jiffy.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/leaveRoom/leaveRoom.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/setReadMarker/setReadMarker.dart'; +import 'package:marianum_mobile/api/marianumcloud/talk/setReadMarker/setReadMarkerParams.dart'; import 'package:persistent_bottom_nav_bar/persistent_tab_view.dart'; import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../../api/marianumcloud/talk/chat/richObjectStringProcessor.dart'; import '../../../api/marianumcloud/talk/room/getRoomResponse.dart'; +import '../../../api/marianumcloud/talk/setFavorite/setFavorite.dart'; import '../../../model/chatList/chatListProps.dart'; +import '../../../widget/confirmDialog.dart'; import '../../../widget/unimplementedDialog.dart'; import 'chatView.dart'; @@ -31,10 +36,14 @@ class _ChatListState extends State<ChatList> { }); WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - Provider.of<ChatListProps>(context, listen: false).run(); + _query(); }); } + void _query({bool renew = false}) { + Provider.of<ChatListProps>(context, listen: false).run(renew: renew); + } + @override Widget build(BuildContext context) { @@ -60,6 +69,16 @@ class _ChatListState extends State<ChatList> { List<ListTile> chats = List<ListTile>.empty(growable: true); for (var chatRoom in data.getRoomsResponse.sortByLastActivity()) { + + setCurrentAsRead() { + SetReadMarker( + chatRoom.token, + true, + setReadMarkerParams: SetReadMarkerParams( + lastReadMessage: chatRoom.lastMessage.id + ) + ).run().then((value) => _query(renew: true)); + } CircleAvatar circleAvatar = CircleAvatar( foregroundImage: chatRoom.type == GetRoomResponseObjectConversationType.oneToOne ? Image.network("https://cloud.marianum-fulda.de/avatar/${chatRoom.name}/128").image : null, @@ -69,45 +88,129 @@ class _ChatListState extends State<ChatList> { ); chats.add(ListTile( + leading: Stack( + children: [ + circleAvatar, + Visibility( + visible: chatRoom.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: Text(chatRoom.displayName), subtitle: Text("${Jiffy.parseFromMillisecondsSinceEpoch(chatRoom.lastMessage.timestamp * 1000).fromNow()}: ${RichObjectStringProcessor.parseToString(chatRoom.lastMessage.message.replaceAll("\n", " "), chatRoom.lastMessage.messageParameters)}", overflow: TextOverflow.ellipsis), - trailing: Visibility( - visible: chatRoom.unreadMessages > 0, - child: 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( - "${chatRoom.unreadMessages}", - style: const TextStyle( - color: Colors.white, - fontSize: 15, + trailing: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Visibility( + visible: chatRoom.unreadMessages > 0, + child: 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( + "${chatRoom.unreadMessages}", + style: const TextStyle( + color: Colors.white, + fontSize: 15, + ), + textAlign: TextAlign.center, + ), ), - textAlign: TextAlign.center, ), - ), + ], ), - leading: circleAvatar, onTap: () async { + setCurrentAsRead(); PersistentNavBarNavigator.pushNewScreen( context, screen: ChatView(room: chatRoom, selfId: username, avatar: circleAvatar), withNavBar: false ); }, + onLongPress: () { + showDialog(context: context, builder: (context) => SimpleDialog( + children: [ + Visibility( + visible: chatRoom.unreadMessages > 0, + replacement: ListTile( + leading: const Icon(Icons.mark_chat_unread_outlined), + title: const Text("Als ungelesen markieren"), + onTap: () { + SetReadMarker(chatRoom.token, false).run().then((value) => _query(renew: true)); + Navigator.of(context).pop(); + }, + ), + child: ListTile( + leading: const Icon(Icons.mark_chat_read_outlined), + title: const Text("Als gelesen markieren"), + onTap: () { + setCurrentAsRead(); + Navigator.of(context).pop(); + }, + ), + ), + Visibility( + visible: chatRoom.isFavorite, + replacement: ListTile( + leading: const Icon(Icons.star_outline), + title: const Text("Zu favoriten hinzufügen"), + onTap: () { + SetFavorite(chatRoom.token, true).run().then((value) => _query(renew: true)); + Navigator.of(context).pop(); + }, + ), + child: ListTile( + leading: const Icon(Icons.stars_outlined), + title: const Text("Von favoriten entfernen"), + onTap: () { + SetFavorite(chatRoom.token, false).run().then((value) => _query(renew: true)); + Navigator.of(context).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(chatRoom.token).run().then((value) => _query(renew: true)); + Navigator.of(context).pop(); + }, + ).asDialog(context); + }, + ), + ], + )); + }, )); } return RefreshIndicator( color: Theme.of(context).primaryColor, onRefresh: () { - Provider.of<ChatListProps>(context, listen: false).run(renew: true); + _query(renew: true); return Future.delayed(const Duration(seconds: 3)); }, child: ListView(children: chats), diff --git a/lib/widget/confirmDialog.dart b/lib/widget/confirmDialog.dart index b6705a4..36d8e0c 100644 --- a/lib/widget/confirmDialog.dart +++ b/lib/widget/confirmDialog.dart @@ -25,8 +25,8 @@ class ConfirmDialog extends StatelessWidget { Navigator.of(context).pop(); }, child: Text(cancelButton)), TextButton(onPressed: () { - onConfirm(); Navigator.of(context).pop(); + onConfirm(); }, child: Text(confirmButton)), ], ); @@ -37,7 +37,7 @@ class ConfirmDialog extends StatelessWidget { context: context, builder: (context) => ConfirmDialog( title: "Link öffnen", - content: "Möchtest du den folgenden Link öffnen?\n${url}", + content: "Möchtest du den folgenden Link öffnen?\n$url", confirmButton: "Öffnen", onConfirm: () => launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication), ),