From bdb01e5f26f2b5246995eedd1d5b929e91f6b9ca Mon Sep 17 00:00:00 2001 From: Hank Grabowski Date: Wed, 25 Jan 2023 11:26:29 -0600 Subject: [PATCH] Add initial DM "mark read" implementation --- lib/friendica_client/friendica_client.dart | 13 +++++++++ lib/models/direct_message.dart | 25 ++++++++++++++++ lib/models/direct_message_thread.dart | 6 ++++ lib/screens/message_thread_screen.dart | 17 +++++++++-- .../message_threads_browser_screen.dart | 16 ++++++++-- .../direct_message_friendica_extensions.dart | 2 +- lib/services/direct_message_service.dart | 29 +++++++++++++++++++ 7 files changed, 101 insertions(+), 7 deletions(-) diff --git a/lib/friendica_client/friendica_client.dart b/lib/friendica_client/friendica_client.dart index a8a95fa..9399c12 100644 --- a/lib/friendica_client/friendica_client.dart +++ b/lib/friendica_client/friendica_client.dart @@ -532,6 +532,19 @@ class FriendicaClient { .execErrorCast(); } + FutureResult markDirectMessageRead( + DirectMessage message) async { + final id = message.id; + final url = Uri.parse( + 'https://$serverName/api/friendica/direct_messages_setseen?id=$id'); + final result = + await _postUrl(url, {}).andThenSuccessAsync((jsonString) async { + return message.copy(seen: true); + }); + + return result.execErrorCast(); + } + FutureResult, ExecError> _getUrl(Uri url) async { _logger.finer('GET: $url'); try { diff --git a/lib/models/direct_message.dart b/lib/models/direct_message.dart index 0975c7a..e104ecd 100644 --- a/lib/models/direct_message.dart +++ b/lib/models/direct_message.dart @@ -32,6 +32,31 @@ class DirectMessage { required this.parentUri, }); + DirectMessage copy({ + String? id, + String? senderId, + String? senderScreenName, + String? recipientId, + String? recipientScreenName, + String? title, + String? text, + int? createdAt, + bool? seen, + String? parentUri, + }) => + DirectMessage( + id: id ?? this.id, + senderId: senderId ?? this.senderId, + senderScreenName: senderScreenName ?? this.senderScreenName, + recipientId: recipientId ?? this.recipientId, + recipientScreenName: recipientScreenName ?? this.recipientScreenName, + title: title ?? this.title, + text: text ?? this.text, + createdAt: createdAt ?? this.createdAt, + seen: seen ?? this.seen, + parentUri: parentUri ?? this.parentUri, + ); + @override bool operator ==(Object other) => identical(this, other) || diff --git a/lib/models/direct_message_thread.dart b/lib/models/direct_message_thread.dart index 5aea525..85c918b 100644 --- a/lib/models/direct_message_thread.dart +++ b/lib/models/direct_message_thread.dart @@ -19,6 +19,12 @@ class DirectMessageThread { required this.parentUri, }); + get allSeen => messages.isEmpty + ? false + : messages + .map((m) => m.seen) + .reduce((allUnseen, thisUnseen) => allUnseen && thisUnseen); + static List createThreads( Iterable messages) { final cm = getIt(); diff --git a/lib/screens/message_thread_screen.dart b/lib/screens/message_thread_screen.dart index 77f5d86..0f0eeb9 100644 --- a/lib/screens/message_thread_screen.dart +++ b/lib/screens/message_thread_screen.dart @@ -27,11 +27,14 @@ class MessageThreadScreen extends StatelessWidget { onError: (_) => 'Thread'); return Scaffold( appBar: StandardAppBar.build(context, title), - body: buildBody(result), + body: buildBody(result, service), ); } - Widget buildBody(Result result) { + Widget buildBody( + Result result, + DirectMessageService service, + ) { return result.fold( onSuccess: (thread) { final yourId = getIt().currentId; @@ -49,13 +52,21 @@ class MessageThreadScreen extends StatelessWidget { ? yourAvatarUrl : participants[m.senderId]?.avatarUrl ?? ''; return ListTile( + onTap: m.seen + ? null + : () => service.markMessageRead(parentThreadId, m), leading: ImageControl( imageUrl: imageUrl, iconOverride: const Icon(Icons.person), width: 32.0, onTap: null, ), - title: Text(text), + title: Text( + text, + style: m.seen + ? null + : TextStyle(fontWeight: FontWeight.bold), + ), trailing: Text( DateTime.fromMillisecondsSinceEpoch(m.createdAt * 1000) .toString()), diff --git a/lib/screens/message_threads_browser_screen.dart b/lib/screens/message_threads_browser_screen.dart index 24dc7cc..ef716fa 100644 --- a/lib/screens/message_threads_browser_screen.dart +++ b/lib/screens/message_threads_browser_screen.dart @@ -31,6 +31,9 @@ class MessagesScreen extends StatelessWidget { itemCount: threads.length, itemBuilder: (context, index) { final thread = threads[index]; + final style = thread.allSeen + ? null + : TextStyle(fontWeight: FontWeight.bold); return ListTile( onTap: () => context.pushNamed( ScreenPaths.thread, @@ -48,10 +51,17 @@ class MessagesScreen extends StatelessWidget { ...thread.participants.map((p) => '${p.name}(${p.handle})') ].join(thread.participants.length == 1 ? ' & ' : ', '), softWrap: true, + style: style, + ), + subtitle: Text( + thread.title, + style: style, + ), + trailing: Text( + ElapsedDateUtils.epochSecondsToString( + thread.messages.first.createdAt), + style: style, ), - subtitle: Text(thread.title), - trailing: Text(ElapsedDateUtils.epochSecondsToString( - thread.messages.first.createdAt)), ); }, separatorBuilder: (_, __) => const Divider(), diff --git a/lib/serializers/friendica/direct_message_friendica_extensions.dart b/lib/serializers/friendica/direct_message_friendica_extensions.dart index 4b2bb61..e40b64f 100644 --- a/lib/serializers/friendica/direct_message_friendica_extensions.dart +++ b/lib/serializers/friendica/direct_message_friendica_extensions.dart @@ -36,7 +36,7 @@ extension DirectMessageFriendicaExtension on DirectMessage { }) : 0; - final bool seen = json['friendica_seen'] == 'true'; + final bool seen = json['friendica_seen']; final String parentUri = json['friendica_parent_uri']; diff --git a/lib/services/direct_message_service.dart b/lib/services/direct_message_service.dart index d4d281f..6fa9dee 100644 --- a/lib/services/direct_message_service.dart +++ b/lib/services/direct_message_service.dart @@ -4,6 +4,7 @@ import 'package:result_monad/result_monad.dart'; import '../friendica_client/paging_data.dart'; import '../globals.dart'; +import '../models/direct_message.dart'; import '../models/direct_message_thread.dart'; import '../models/exec_error.dart'; import 'auth_service.dart'; @@ -52,4 +53,32 @@ class DirectMessageService extends ChangeNotifier { notifyListeners(); } + + Future markMessageRead(String threadId, DirectMessage m) async { + final thread = _threads[threadId]; + if (thread == null) { + _logger.severe('Message is not for this thread: $threadId, $m'); + return; + } + final oldIndex = thread.messages.indexOf(m); + if (oldIndex < 0) { + _logger.severe('Message is not for this thread: $threadId, $m'); + return; + } + + await getIt() + .currentClient + .andThenAsync((client) => client.markDirectMessageRead(m)) + .match( + onSuccess: (update) { + thread.messages.removeAt(oldIndex); + thread.messages.insert(oldIndex, update); + notifyListeners(); + }, + onError: (error) { + _logger.severe('Error getting direct messages: $error'); + }, + ); + notifyListeners(); + } }