LCOV - code coverage report
Current view: top level - lib/src/utils - event_localizations.dart (source / functions) Hit Total Coverage
Test: merged.info Lines: 107 167 64.1 %
Date: 2024-09-28 12:47:43 Functions: 0 0 -

          Line data    Source code
       1             : /*
       2             :  *   Famedly Matrix SDK
       3             :  *   Copyright (C) 2019, 2020, 2021 Famedly GmbH
       4             :  *
       5             :  *   This program is free software: you can redistribute it and/or modify
       6             :  *   it under the terms of the GNU Affero General Public License as
       7             :  *   published by the Free Software Foundation, either version 3 of the
       8             :  *   License, or (at your option) any later version.
       9             :  *
      10             :  *   This program is distributed in the hope that it will be useful,
      11             :  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12             :  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      13             :  *   GNU Affero General Public License for more details.
      14             :  *
      15             :  *   You should have received a copy of the GNU Affero General Public License
      16             :  *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
      17             :  */
      18             : 
      19             : import 'package:collection/collection.dart';
      20             : 
      21             : import 'package:matrix/encryption.dart';
      22             : import 'package:matrix/matrix.dart';
      23             : 
      24             : abstract class EventLocalizations {
      25             :   // As we need to create the localized body off of a different set of parameters, we
      26             :   // might create it with `event.plaintextBody`, maybe with `event.body`, maybe with the
      27             :   // reply fallback stripped, and maybe with the new body in `event.content['m.new_content']`.
      28             :   // Thus, it seems easier to offload that logic into `Event.getLocalizedBody()` and pass the
      29             :   // `body` variable around here.
      30           2 :   static String _localizedBodyNormalMessage(
      31             :       Event event, MatrixLocalizations i18n, String body) {
      32           2 :     switch (event.messageType) {
      33           2 :       case MessageTypes.Image:
      34           2 :         return i18n.sentAPicture(
      35           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      36           2 :       case MessageTypes.File:
      37           2 :         return i18n.sentAFile(
      38           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      39           2 :       case MessageTypes.Audio:
      40           2 :         return i18n.sentAnAudio(
      41           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      42           2 :       case MessageTypes.Video:
      43           2 :         return i18n.sentAVideo(
      44           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      45           2 :       case MessageTypes.Location:
      46           2 :         return i18n.sharedTheLocation(
      47           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      48           2 :       case MessageTypes.Sticker:
      49           0 :         return i18n.sentASticker(
      50           0 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      51           2 :       case MessageTypes.Emote:
      52           2 :         return '* $body';
      53           2 :       case EventTypes.KeyVerificationRequest:
      54           2 :         return i18n.requestedKeyVerification(
      55           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      56           2 :       case EventTypes.KeyVerificationCancel:
      57           2 :         return i18n.canceledKeyVerification(
      58           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      59           2 :       case EventTypes.KeyVerificationDone:
      60           2 :         return i18n.completedKeyVerification(
      61           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      62           2 :       case EventTypes.KeyVerificationReady:
      63           2 :         return i18n.isReadyForKeyVerification(
      64           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      65           2 :       case EventTypes.KeyVerificationAccept:
      66           2 :         return i18n.acceptedKeyVerification(
      67           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      68           2 :       case EventTypes.KeyVerificationStart:
      69           2 :         return i18n.startedKeyVerification(
      70           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
      71           2 :       case MessageTypes.BadEncrypted:
      72             :         String errorText;
      73           0 :         switch (event.body) {
      74           0 :           case DecryptException.channelCorrupted:
      75           0 :             errorText = '${i18n.channelCorruptedDecryptError}.';
      76             :             break;
      77           0 :           case DecryptException.notEnabled:
      78           0 :             errorText = '${i18n.encryptionNotEnabled}.';
      79             :             break;
      80           0 :           case DecryptException.unknownAlgorithm:
      81           0 :             errorText = '${i18n.unknownEncryptionAlgorithm}.';
      82             :             break;
      83           0 :           case DecryptException.unknownSession:
      84           0 :             errorText = '${i18n.noPermission}.';
      85             :             break;
      86             :           default:
      87             :             errorText = body;
      88             :             break;
      89             :         }
      90           0 :         return i18n.couldNotDecryptMessage(errorText);
      91             :       case MessageTypes.Text:
      92             :       case MessageTypes.Notice:
      93             :       case MessageTypes.None:
      94             :       default:
      95             :         return body;
      96             :     }
      97             :   }
      98             : 
      99             :   // This map holds how to localize event types, and thus which event types exist.
     100             :   // If an event exists but it does not have a localized body, set its callback to null
     101             :   static final Map<String,
     102             :           String Function(Event event, MatrixLocalizations i18n, String body)?>
     103           6 :       localizationsMap = {
     104           4 :     EventTypes.Sticker: (event, i18n, body) => i18n.sentASticker(
     105           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     106           4 :     EventTypes.Redaction: (event, i18n, body) => i18n.redactedAnEvent(event),
     107           4 :     EventTypes.RoomAliases: (event, i18n, body) => i18n.changedTheRoomAliases(
     108           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     109           2 :     EventTypes.RoomCanonicalAlias: (event, i18n, body) =>
     110           2 :         i18n.changedTheRoomInvitationLink(
     111           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     112           4 :     EventTypes.RoomCreate: (event, i18n, body) => i18n.createdTheChat(
     113           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     114           4 :     EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
     115           2 :     EventTypes.RoomJoinRules: (event, i18n, body) {
     116           4 :       final joinRules = JoinRules.values.firstWhereOrNull((r) =>
     117           6 :           r.toString().replaceAll('JoinRules.', '') ==
     118           4 :           event.content['join_rule']);
     119             :       if (joinRules == null) {
     120           0 :         return i18n.changedTheJoinRules(
     121           0 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
     122             :       } else {
     123           2 :         return i18n.changedTheJoinRulesTo(
     124           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     125           2 :             joinRules.getLocalizedString(i18n));
     126             :       }
     127             :     },
     128           2 :     EventTypes.RoomMember: (event, i18n, body) {
     129           4 :       final targetName = event.stateKeyUser?.calcDisplayname(i18n: i18n) ?? '';
     130             :       final senderName =
     131           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
     132          10 :       final userIsTarget = event.stateKey == event.room.client.userID;
     133          10 :       final userIsSender = event.senderId == event.room.client.userID;
     134             : 
     135           2 :       switch (event.roomMemberChangeType) {
     136           2 :         case RoomMemberChangeType.avatar:
     137           0 :           return i18n.changedTheProfileAvatar(targetName);
     138           2 :         case RoomMemberChangeType.displayname:
     139             :           final newDisplayname =
     140           0 :               event.content.tryGet<String>('displayname') ?? '';
     141             :           final oldDisplayname =
     142           0 :               event.prevContent?.tryGet<String>('displayname') ?? '';
     143           0 :           return i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname);
     144           2 :         case RoomMemberChangeType.join:
     145             :           return userIsTarget
     146           0 :               ? i18n.youJoinedTheChat
     147           2 :               : i18n.joinedTheChat(targetName);
     148           2 :         case RoomMemberChangeType.acceptInvite:
     149             :           return userIsTarget
     150           0 :               ? i18n.youAcceptedTheInvitation
     151           2 :               : i18n.acceptedTheInvitation(targetName);
     152           2 :         case RoomMemberChangeType.rejectInvite:
     153             :           return userIsTarget
     154           0 :               ? i18n.youRejectedTheInvitation
     155           2 :               : i18n.rejectedTheInvitation(targetName);
     156           2 :         case RoomMemberChangeType.withdrawInvitation:
     157             :           return userIsSender
     158           0 :               ? i18n.youHaveWithdrawnTheInvitationFor(targetName)
     159           2 :               : i18n.hasWithdrawnTheInvitationFor(senderName, targetName);
     160           2 :         case RoomMemberChangeType.leave:
     161           0 :           return i18n.userLeftTheChat(targetName);
     162           2 :         case RoomMemberChangeType.kick:
     163             :           return userIsSender
     164           0 :               ? i18n.youKicked(targetName)
     165           2 :               : i18n.kicked(senderName, targetName);
     166           2 :         case RoomMemberChangeType.invite:
     167             :           return userIsSender
     168           0 :               ? i18n.youInvitedUser(targetName)
     169             :               : userIsTarget
     170           0 :                   ? i18n.youInvitedBy(senderName)
     171           2 :                   : i18n.invitedUser(senderName, targetName);
     172           2 :         case RoomMemberChangeType.ban:
     173             :           return userIsSender
     174           0 :               ? i18n.youBannedUser(targetName)
     175           2 :               : i18n.bannedUser(senderName, targetName);
     176           0 :         case RoomMemberChangeType.unban:
     177             :           return userIsSender
     178           0 :               ? i18n.youUnbannedUser(targetName)
     179           0 :               : i18n.unbannedUser(senderName, targetName);
     180           0 :         case RoomMemberChangeType.knock:
     181           0 :           return i18n.hasKnocked(targetName);
     182             :         case RoomMemberChangeType.other:
     183             :         default:
     184             :           return userIsTarget
     185           0 :               ? i18n.youJoinedTheChat
     186           0 :               : i18n.joinedTheChat(targetName);
     187             :       }
     188             :     },
     189           2 :     EventTypes.RoomPowerLevels: (event, i18n, body) =>
     190           2 :         i18n.changedTheChatPermissions(
     191           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     192           4 :     EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo(
     193           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     194           4 :         event.content.tryGet<String>('name') ?? ''),
     195           2 :     EventTypes.RoomTopic: (event, i18n, body) =>
     196           2 :         i18n.changedTheChatDescriptionTo(
     197           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     198           4 :             event.content.tryGet<String>('topic') ?? ''),
     199           4 :     EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar(
     200           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     201           0 :     EventTypes.GuestAccess: (event, i18n, body) {
     202           0 :       final guestAccess = GuestAccess.values.firstWhereOrNull((r) =>
     203           0 :           r.toString().replaceAll('GuestAccess.', '') ==
     204           0 :           event.content['guest_access']);
     205             :       if (guestAccess == null) {
     206           0 :         return i18n.changedTheGuestAccessRules(
     207           0 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
     208             :       } else {
     209           0 :         return i18n.changedTheGuestAccessRulesTo(
     210           0 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     211           0 :             guestAccess.getLocalizedString(i18n));
     212             :       }
     213             :     },
     214           2 :     EventTypes.HistoryVisibility: (event, i18n, body) {
     215           4 :       final historyVisibility = HistoryVisibility.values.firstWhereOrNull((r) =>
     216           6 :           r.toString().replaceAll('HistoryVisibility.', '') ==
     217           4 :           event.content['history_visibility']);
     218             :       if (historyVisibility == null) {
     219           0 :         return i18n.changedTheHistoryVisibility(
     220           0 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
     221             :       } else {
     222           2 :         return i18n.changedTheHistoryVisibilityTo(
     223           4 :             event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     224           2 :             historyVisibility.getLocalizedString(i18n));
     225             :       }
     226             :     },
     227           2 :     EventTypes.Encryption: (event, i18n, body) {
     228           2 :       var localizedBody = i18n.activatedEndToEndEncryption(
     229           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n));
     230           8 :       if (event.room.client.encryptionEnabled == false) {
     231           6 :         localizedBody += '. ${i18n.needPantalaimonWarning}';
     232             :       }
     233             :       return localizedBody;
     234             :     },
     235           0 :     EventTypes.CallAnswer: (event, i18n, body) => i18n.answeredTheCall(
     236           0 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     237           0 :     EventTypes.CallHangup: (event, i18n, body) => i18n.endedTheCall(
     238           0 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     239           0 :     EventTypes.CallInvite: (event, i18n, body) => i18n.startedACall(
     240           0 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     241           0 :     EventTypes.CallCandidates: (event, i18n, body) => i18n.sentCallInformations(
     242           0 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n)),
     243           0 :     EventTypes.Encrypted: (event, i18n, body) =>
     244           0 :         _localizedBodyNormalMessage(event, i18n, body),
     245           2 :     EventTypes.Message: (event, i18n, body) =>
     246           2 :         _localizedBodyNormalMessage(event, i18n, body),
     247           0 :     EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction(
     248           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     249           0 :           event.content
     250           0 :                   .tryGetMap<String, Object?>('m.relates_to')
     251           0 :                   ?.tryGet<String>('key') ??
     252             :               body,
     253             :         ),
     254             :   };
     255             : }

Generated by: LCOV version 1.14