From 8ac86a883d1b41a21e4e56d7ab891e164e20b262 Mon Sep 17 00:00:00 2001 From: Krille Date: Tue, 19 Sep 2023 10:42:14 +0200 Subject: [PATCH] feat: Implement member change type For easier handling and comparing of m.room.member state events to better know what these event actually changes. --- lib/matrix.dart | 1 + lib/src/utils/event_localizations.dart | 114 +++++++----------- .../utils/matrix_default_localizations.dart | 3 + lib/src/utils/matrix_localizations.dart | 2 + lib/src/utils/room_member_change_type.dart | 112 +++++++++++++++++ test/event_test.dart | 9 ++ 6 files changed, 169 insertions(+), 72 deletions(-) create mode 100644 lib/src/utils/room_member_change_type.dart diff --git a/lib/matrix.dart b/lib/matrix.dart index 844b0e69..786894ff 100644 --- a/lib/matrix.dart +++ b/lib/matrix.dart @@ -49,6 +49,7 @@ export 'src/utils/matrix_file.dart'; export 'src/utils/matrix_id_string_extension.dart'; export 'src/utils/matrix_localizations.dart'; export 'src/utils/native_implementations.dart'; +export 'src/utils/room_member_change_type.dart'; export 'src/utils/push_notification.dart'; export 'src/utils/pushrule_evaluator.dart'; export 'src/models/receipts.dart'; diff --git a/lib/src/utils/event_localizations.dart b/lib/src/utils/event_localizations.dart index 61155b2f..5707eac7 100644 --- a/lib/src/utils/event_localizations.dart +++ b/lib/src/utils/event_localizations.dart @@ -114,89 +114,59 @@ abstract class EventLocalizations { final userIsTarget = event.stateKey == event.room.client.userID; final userIsSender = event.senderId == event.room.client.userID; - // Fallback message if just nothing has changed: - var text = i18n.joinedTheChat(targetName); - - // Has the membership changed? - final newMembership = event.content['membership'] ?? ''; - final oldMembership = event.prevContent?['membership'] ?? ''; - - if (newMembership != oldMembership) { - if (oldMembership == 'invite' && newMembership == 'join') { - text = userIsTarget - ? i18n.youAcceptedTheInvitation - : i18n.acceptedTheInvitation(targetName); - } else if (oldMembership == 'invite' && newMembership == 'leave') { - if (event.stateKey == event.senderId) { - text = userIsTarget - ? i18n.youRejectedTheInvitation - : i18n.rejectedTheInvitation(targetName); - } else { - text = userIsSender - ? i18n.youHaveWithdrawnTheInvitationFor(targetName) - : i18n.hasWithdrawnTheInvitationFor(senderName, targetName); - } - } else if (oldMembership == 'leave' && newMembership == 'join') { - text = userIsTarget + switch (event.roomMemberChangeType) { + case RoomMemberChangeType.avatar: + return i18n.changedTheProfileAvatar(targetName); + case RoomMemberChangeType.displayname: + final newDisplayname = + event.content.tryGet('displayname') ?? ''; + final oldDisplayname = + event.prevContent?.tryGet('displayname') ?? ''; + return i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname); + case RoomMemberChangeType.join: + return userIsTarget ? i18n.youJoinedTheChat : i18n.joinedTheChat(targetName); - } else if (oldMembership == 'join' && newMembership == 'ban') { - text = userIsSender - ? i18n.youKickedAndBanned(targetName) - : i18n.kickedAndBanned(senderName, targetName); - } else if (oldMembership == 'join' && - newMembership == 'leave' && - event.stateKey != event.senderId) { - text = userIsSender + case RoomMemberChangeType.acceptInvite: + return userIsTarget + ? i18n.youAcceptedTheInvitation + : i18n.acceptedTheInvitation(targetName); + case RoomMemberChangeType.rejectInvite: + return userIsTarget + ? i18n.youRejectedTheInvitation + : i18n.rejectedTheInvitation(targetName); + case RoomMemberChangeType.withdrawInvitation: + return userIsSender + ? i18n.youHaveWithdrawnTheInvitationFor(targetName) + : i18n.hasWithdrawnTheInvitationFor(senderName, targetName); + case RoomMemberChangeType.leave: + return i18n.userLeftTheChat(targetName); + case RoomMemberChangeType.kick: + return userIsSender ? i18n.youKicked(targetName) : i18n.kicked(senderName, targetName); - } else if (oldMembership == 'join' && - newMembership == 'leave' && - event.stateKey == event.senderId) { - text = i18n.userLeftTheChat(targetName); - } else if (oldMembership == 'invite' && newMembership == 'ban') { - text = userIsSender - ? i18n.youBannedUser(targetName) - : i18n.bannedUser(senderName, targetName); - } else if (oldMembership == 'leave' && newMembership == 'ban') { - text = userIsSender - ? i18n.youBannedUser(targetName) - : i18n.bannedUser(senderName, targetName); - } else if (oldMembership == 'ban' && newMembership == 'leave') { - text = userIsSender - ? i18n.youUnbannedUser(targetName) - : i18n.unbannedUser(senderName, targetName); - } else if (newMembership == 'invite') { - text = userIsSender + case RoomMemberChangeType.invite: + return userIsSender ? i18n.youInvitedUser(targetName) : userIsTarget ? i18n.youInvitedBy(senderName) : i18n.invitedUser(senderName, targetName); - } else if (newMembership == 'join') { - text = userIsTarget + case RoomMemberChangeType.ban: + return userIsSender + ? i18n.youBannedUser(targetName) + : i18n.bannedUser(senderName, targetName); + case RoomMemberChangeType.unban: + return userIsSender + ? i18n.youUnbannedUser(targetName) + : i18n.unbannedUser(senderName, targetName); + case RoomMemberChangeType.knock: + return i18n.hasKnocked(targetName); + case RoomMemberChangeType.other: + default: + return userIsTarget ? i18n.youJoinedTheChat : i18n.joinedTheChat(targetName); - } - } else if (newMembership == 'join') { - final newAvatar = event.content.tryGet('avatar_url') ?? ''; - final oldAvatar = event.prevContent?.tryGet('avatar_url') ?? ''; - - final newDisplayname = - event.content.tryGet('displayname') ?? ''; - final oldDisplayname = - event.prevContent?.tryGet('displayname') ?? ''; - final stateKey = event.stateKey; - - // Has the user avatar changed? - if (newAvatar != oldAvatar) { - text = i18n.changedTheProfileAvatar(targetName); - } - // Has the user displayname changed? - else if (newDisplayname != oldDisplayname && stateKey != null) { - text = i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname); - } } - return text; }, EventTypes.RoomPowerLevels: (event, i18n, body) => i18n.changedTheChatPermissions( diff --git a/lib/src/utils/matrix_default_localizations.dart b/lib/src/utils/matrix_default_localizations.dart index a17958db..bc0c8b4f 100644 --- a/lib/src/utils/matrix_default_localizations.dart +++ b/lib/src/utils/matrix_default_localizations.dart @@ -274,4 +274,7 @@ class MatrixDefaultLocalizations extends MatrixLocalizations { @override String get unknownUser => 'Unknown user'; + + @override + String hasKnocked(String targetName) => '$targetName has knocked'; } diff --git a/lib/src/utils/matrix_localizations.dart b/lib/src/utils/matrix_localizations.dart index af23118c..399bb162 100644 --- a/lib/src/utils/matrix_localizations.dart +++ b/lib/src/utils/matrix_localizations.dart @@ -161,6 +161,8 @@ abstract class MatrixLocalizations { String sentCallInformations(String senderName); String wasDirectChatDisplayName(String oldDisplayName); + + String hasKnocked(String targetName); } extension HistoryVisibilityDisplayString on HistoryVisibility { diff --git a/lib/src/utils/room_member_change_type.dart b/lib/src/utils/room_member_change_type.dart new file mode 100644 index 00000000..4af4cd10 --- /dev/null +++ b/lib/src/utils/room_member_change_type.dart @@ -0,0 +1,112 @@ +import 'package:matrix/matrix.dart'; + +/// The kind of what has changed with this m.room.member event to have a +/// comparable type. +enum RoomMemberChangeType { + /// The user has changed the avatar. + avatar, + + /// The user has changed the displayname. + displayname, + + /// The user has joined the chat from being not a user before. The user + /// also was not invited before. + join, + + /// The user was invited before and has joined. + acceptInvite, + + /// The user was invited before and has left. + rejectInvite, + + /// The user was invited before and the invitation got withdrawn by someone. + withdrawInvitation, + + /// The user was joined before and has now left the room by themself. + leave, + + /// The user was joined before and has now been kicked out of the room by + /// someone. + kick, + + /// The user has been invited by someone. + invite, + + /// The user has been banned by someone. + ban, + + /// The user was banned before and has been unbanned by someone. + unban, + + /// The user was not a member of the room and now knocks. + knock, + + /// Something else which is not handled yet. + other, +} + +extension RoomMemberChangeTypeExtension on Event { + /// Returns the comparable type of this m.room.member event to handle this + /// differently in the UI. If the event is not of the type m.room.member, + /// this throws an exception! + RoomMemberChangeType get roomMemberChangeType { + if (type != EventTypes.RoomMember) { + throw Exception( + 'Tried to call `roomMemberChangeType` but the Event has a type of `$type`', + ); + } + + // Has the membership changed? + final newMembership = content.tryGet('membership') ?? ''; + final oldMembership = prevContent?.tryGet('membership') ?? ''; + + if (newMembership != oldMembership) { + if (oldMembership == 'invite' && newMembership == 'join') { + return RoomMemberChangeType.acceptInvite; + } else if (oldMembership == 'invite' && newMembership == 'leave') { + if (stateKey == senderId) { + return RoomMemberChangeType.rejectInvite; + } else { + return RoomMemberChangeType.withdrawInvitation; + } + } else if ((oldMembership == 'leave' || oldMembership == '') && + newMembership == 'join') { + return RoomMemberChangeType.join; + } else if (oldMembership == 'join' && newMembership == 'ban') { + return RoomMemberChangeType.ban; + } else if (oldMembership == 'join' && + newMembership == 'leave' && + stateKey != senderId) { + return RoomMemberChangeType.kick; + } else if (oldMembership == 'join' && + newMembership == 'leave' && + stateKey == senderId) { + return RoomMemberChangeType.leave; + } else if (oldMembership != newMembership && newMembership == 'ban') { + return RoomMemberChangeType.ban; + } else if (oldMembership == 'ban' && newMembership == 'leave') { + return RoomMemberChangeType.unban; + } else if (newMembership == 'invite') { + return RoomMemberChangeType.invite; + } else if (newMembership == 'knock') { + return RoomMemberChangeType.knock; + } + } else if (newMembership == 'join') { + final newAvatar = content.tryGet('avatar_url') ?? ''; + final oldAvatar = prevContent?.tryGet('avatar_url') ?? ''; + + final newDisplayname = content.tryGet('displayname') ?? ''; + final oldDisplayname = prevContent?.tryGet('displayname') ?? ''; + + // Has the user avatar changed? + if (newAvatar != oldAvatar) { + return RoomMemberChangeType.avatar; + } + // Has the user displayname changed? + else if (newDisplayname != oldDisplayname && stateKey != null) { + return RoomMemberChangeType.displayname; + } + } + return RoomMemberChangeType.other; + } +} diff --git a/test/event_test.dart b/test/event_test.dart index c7bf9204..ecc7b209 100644 --- a/test/event_test.dart +++ b/test/event_test.dart @@ -569,6 +569,7 @@ void main() { 'type': 'm.room.member', 'unsigned': {'age': 1234} }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.join); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Alice joined the chat'); expect(event.isEventTypeKnown, true); @@ -582,6 +583,7 @@ void main() { 'state_key': '@alice:example.org', 'type': 'm.room.member' }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.invite); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example has invited Alice'); expect(event.isEventTypeKnown, true); @@ -598,6 +600,7 @@ void main() { 'prev_content': {'membership': 'join'}, } }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.kick); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example kicked Alice'); expect(event.isEventTypeKnown, true); @@ -614,6 +617,7 @@ void main() { 'prev_content': {'membership': 'join'}, } }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.ban); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example banned Alice'); expect(event.isEventTypeKnown, true); @@ -630,6 +634,7 @@ void main() { 'prev_content': {'membership': 'invite'}, } }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.acceptInvite); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Alice accepted the invitation'); expect(event.isEventTypeKnown, true); @@ -646,6 +651,7 @@ void main() { 'prev_content': {'membership': 'join'}, } }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.invite); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example has invited Alice'); expect(event.isEventTypeKnown, true); @@ -662,6 +668,8 @@ void main() { 'prev_content': {'membership': 'invite'}, } }, room); + expect( + event.roomMemberChangeType, RoomMemberChangeType.withdrawInvitation); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Example has withdrawn the invitation for Alice'); expect(event.isEventTypeKnown, true); @@ -678,6 +686,7 @@ void main() { 'prev_content': {'membership': 'invite'}, } }, room); + expect(event.roomMemberChangeType, RoomMemberChangeType.rejectInvite); expect(await event.calcLocalizedBody(MatrixDefaultLocalizations()), 'Alice rejected the invitation'); expect(event.isEventTypeKnown, true);