LCOV - code coverage report
Current view: top level - lib/src/utils - event_localizations.dart (source / functions) Hit Total Coverage
Test: merged.info Lines: 109 170 64.1 %
Date: 2024-11-12 07:37:08 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,
      32             :     MatrixLocalizations i18n,
      33             :     String body,
      34             :   ) {
      35           2 :     switch (event.messageType) {
      36           2 :       case MessageTypes.Image:
      37           2 :         return i18n.sentAPicture(
      38           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      39             :         );
      40           2 :       case MessageTypes.File:
      41           2 :         return i18n.sentAFile(
      42           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      43             :         );
      44           2 :       case MessageTypes.Audio:
      45           2 :         return i18n.sentAnAudio(
      46           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      47             :         );
      48           2 :       case MessageTypes.Video:
      49           2 :         return i18n.sentAVideo(
      50           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      51             :         );
      52           2 :       case MessageTypes.Location:
      53           2 :         return i18n.sharedTheLocation(
      54           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      55             :         );
      56           2 :       case MessageTypes.Sticker:
      57           0 :         return i18n.sentASticker(
      58           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      59             :         );
      60           2 :       case MessageTypes.Emote:
      61           2 :         return '* $body';
      62           2 :       case EventTypes.KeyVerificationRequest:
      63           2 :         return i18n.requestedKeyVerification(
      64           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      65             :         );
      66           2 :       case EventTypes.KeyVerificationCancel:
      67           2 :         return i18n.canceledKeyVerification(
      68           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      69             :         );
      70           2 :       case EventTypes.KeyVerificationDone:
      71           2 :         return i18n.completedKeyVerification(
      72           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      73             :         );
      74           2 :       case EventTypes.KeyVerificationReady:
      75           2 :         return i18n.isReadyForKeyVerification(
      76           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      77             :         );
      78           2 :       case EventTypes.KeyVerificationAccept:
      79           2 :         return i18n.acceptedKeyVerification(
      80           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      81             :         );
      82           2 :       case EventTypes.KeyVerificationStart:
      83           2 :         return i18n.startedKeyVerification(
      84           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
      85             :         );
      86           2 :       case MessageTypes.BadEncrypted:
      87             :         String errorText;
      88           0 :         switch (event.body) {
      89           0 :           case DecryptException.channelCorrupted:
      90           0 :             errorText = '${i18n.channelCorruptedDecryptError}.';
      91             :             break;
      92           0 :           case DecryptException.notEnabled:
      93           0 :             errorText = '${i18n.encryptionNotEnabled}.';
      94             :             break;
      95           0 :           case DecryptException.unknownAlgorithm:
      96           0 :             errorText = '${i18n.unknownEncryptionAlgorithm}.';
      97             :             break;
      98           0 :           case DecryptException.unknownSession:
      99           0 :             errorText = '${i18n.noPermission}.';
     100             :             break;
     101             :           default:
     102             :             errorText = body;
     103             :             break;
     104             :         }
     105           0 :         return i18n.couldNotDecryptMessage(errorText);
     106             :       case MessageTypes.Text:
     107             :       case MessageTypes.Notice:
     108             :       case MessageTypes.None:
     109             :       default:
     110             :         return body;
     111             :     }
     112             :   }
     113             : 
     114             :   // This map holds how to localize event types, and thus which event types exist.
     115             :   // If an event exists but it does not have a localized body, set its callback to null
     116             :   static final Map<String,
     117             :           String Function(Event event, MatrixLocalizations i18n, String body)?>
     118           6 :       localizationsMap = {
     119           4 :     EventTypes.Sticker: (event, i18n, body) => i18n.sentASticker(
     120           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     121             :         ),
     122           4 :     EventTypes.Redaction: (event, i18n, body) => i18n.redactedAnEvent(event),
     123           4 :     EventTypes.RoomAliases: (event, i18n, body) => i18n.changedTheRoomAliases(
     124           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     125             :         ),
     126           2 :     EventTypes.RoomCanonicalAlias: (event, i18n, body) =>
     127           2 :         i18n.changedTheRoomInvitationLink(
     128           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     129             :         ),
     130           4 :     EventTypes.RoomCreate: (event, i18n, body) => i18n.createdTheChat(
     131           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     132             :         ),
     133           4 :     EventTypes.RoomTombstone: (event, i18n, body) => i18n.roomHasBeenUpgraded,
     134           2 :     EventTypes.RoomJoinRules: (event, i18n, body) {
     135           2 :       final joinRules = JoinRules.values.firstWhereOrNull(
     136           2 :         (r) =>
     137           6 :             r.toString().replaceAll('JoinRules.', '') ==
     138           4 :             event.content['join_rule'],
     139             :       );
     140             :       if (joinRules == null) {
     141           0 :         return i18n.changedTheJoinRules(
     142           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     143             :         );
     144             :       } else {
     145           2 :         return i18n.changedTheJoinRulesTo(
     146           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     147           2 :           joinRules.getLocalizedString(i18n),
     148             :         );
     149             :       }
     150             :     },
     151           2 :     EventTypes.RoomMember: (event, i18n, body) {
     152           4 :       final targetName = event.stateKeyUser?.calcDisplayname(i18n: i18n) ?? '';
     153             :       final senderName =
     154           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n);
     155          10 :       final userIsTarget = event.stateKey == event.room.client.userID;
     156          10 :       final userIsSender = event.senderId == event.room.client.userID;
     157             : 
     158           2 :       switch (event.roomMemberChangeType) {
     159           2 :         case RoomMemberChangeType.avatar:
     160           0 :           return i18n.changedTheProfileAvatar(targetName);
     161           2 :         case RoomMemberChangeType.displayname:
     162             :           final newDisplayname =
     163           0 :               event.content.tryGet<String>('displayname') ?? '';
     164             :           final oldDisplayname =
     165           0 :               event.prevContent?.tryGet<String>('displayname') ?? '';
     166           0 :           return i18n.changedTheDisplaynameTo(oldDisplayname, newDisplayname);
     167           2 :         case RoomMemberChangeType.join:
     168             :           return userIsTarget
     169           0 :               ? i18n.youJoinedTheChat
     170           2 :               : i18n.joinedTheChat(targetName);
     171           2 :         case RoomMemberChangeType.acceptInvite:
     172             :           return userIsTarget
     173           0 :               ? i18n.youAcceptedTheInvitation
     174           2 :               : i18n.acceptedTheInvitation(targetName);
     175           2 :         case RoomMemberChangeType.rejectInvite:
     176             :           return userIsTarget
     177           0 :               ? i18n.youRejectedTheInvitation
     178           2 :               : i18n.rejectedTheInvitation(targetName);
     179           2 :         case RoomMemberChangeType.withdrawInvitation:
     180             :           return userIsSender
     181           0 :               ? i18n.youHaveWithdrawnTheInvitationFor(targetName)
     182           2 :               : i18n.hasWithdrawnTheInvitationFor(senderName, targetName);
     183           2 :         case RoomMemberChangeType.leave:
     184           0 :           return i18n.userLeftTheChat(targetName);
     185           2 :         case RoomMemberChangeType.kick:
     186             :           return userIsSender
     187           0 :               ? i18n.youKicked(targetName)
     188           2 :               : i18n.kicked(senderName, targetName);
     189           2 :         case RoomMemberChangeType.invite:
     190             :           return userIsSender
     191           0 :               ? i18n.youInvitedUser(targetName)
     192             :               : userIsTarget
     193           0 :                   ? i18n.youInvitedBy(senderName)
     194           2 :                   : i18n.invitedUser(senderName, targetName);
     195           2 :         case RoomMemberChangeType.ban:
     196             :           return userIsSender
     197           0 :               ? i18n.youBannedUser(targetName)
     198           2 :               : i18n.bannedUser(senderName, targetName);
     199           0 :         case RoomMemberChangeType.unban:
     200             :           return userIsSender
     201           0 :               ? i18n.youUnbannedUser(targetName)
     202           0 :               : i18n.unbannedUser(senderName, targetName);
     203           0 :         case RoomMemberChangeType.knock:
     204           0 :           return i18n.hasKnocked(targetName);
     205             :         case RoomMemberChangeType.other:
     206             :         default:
     207             :           return userIsTarget
     208           0 :               ? i18n.youJoinedTheChat
     209           0 :               : i18n.joinedTheChat(targetName);
     210             :       }
     211             :     },
     212           2 :     EventTypes.RoomPowerLevels: (event, i18n, body) =>
     213           2 :         i18n.changedTheChatPermissions(
     214           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     215             :         ),
     216           4 :     EventTypes.RoomName: (event, i18n, body) => i18n.changedTheChatNameTo(
     217           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     218           4 :           event.content.tryGet<String>('name') ?? '',
     219             :         ),
     220           2 :     EventTypes.RoomTopic: (event, i18n, body) =>
     221           2 :         i18n.changedTheChatDescriptionTo(
     222           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     223           4 :           event.content.tryGet<String>('topic') ?? '',
     224             :         ),
     225           4 :     EventTypes.RoomAvatar: (event, i18n, body) => i18n.changedTheChatAvatar(
     226           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     227             :         ),
     228           0 :     EventTypes.GuestAccess: (event, i18n, body) {
     229           0 :       final guestAccess = GuestAccess.values.firstWhereOrNull(
     230           0 :         (r) =>
     231           0 :             r.toString().replaceAll('GuestAccess.', '') ==
     232           0 :             event.content['guest_access'],
     233             :       );
     234             :       if (guestAccess == null) {
     235           0 :         return i18n.changedTheGuestAccessRules(
     236           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     237             :         );
     238             :       } else {
     239           0 :         return i18n.changedTheGuestAccessRulesTo(
     240           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     241           0 :           guestAccess.getLocalizedString(i18n),
     242             :         );
     243             :       }
     244             :     },
     245           2 :     EventTypes.HistoryVisibility: (event, i18n, body) {
     246           2 :       final historyVisibility = HistoryVisibility.values.firstWhereOrNull(
     247           2 :         (r) =>
     248           6 :             r.toString().replaceAll('HistoryVisibility.', '') ==
     249           4 :             event.content['history_visibility'],
     250             :       );
     251             :       if (historyVisibility == null) {
     252           0 :         return i18n.changedTheHistoryVisibility(
     253           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     254             :         );
     255             :       } else {
     256           2 :         return i18n.changedTheHistoryVisibilityTo(
     257           4 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     258           2 :           historyVisibility.getLocalizedString(i18n),
     259             :         );
     260             :       }
     261             :     },
     262           2 :     EventTypes.Encryption: (event, i18n, body) {
     263           2 :       var localizedBody = i18n.activatedEndToEndEncryption(
     264           4 :         event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     265             :       );
     266           8 :       if (event.room.client.encryptionEnabled == false) {
     267           6 :         localizedBody += '. ${i18n.needPantalaimonWarning}';
     268             :       }
     269             :       return localizedBody;
     270             :     },
     271           0 :     EventTypes.CallAnswer: (event, i18n, body) => i18n.answeredTheCall(
     272           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     273             :         ),
     274           0 :     EventTypes.CallHangup: (event, i18n, body) => i18n.endedTheCall(
     275           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     276             :         ),
     277           0 :     EventTypes.CallInvite: (event, i18n, body) => i18n.startedACall(
     278           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     279             :         ),
     280           0 :     EventTypes.CallCandidates: (event, i18n, body) => i18n.sentCallInformations(
     281           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     282             :         ),
     283           0 :     EventTypes.Encrypted: (event, i18n, body) =>
     284           0 :         _localizedBodyNormalMessage(event, i18n, body),
     285           2 :     EventTypes.Message: (event, i18n, body) =>
     286           2 :         _localizedBodyNormalMessage(event, i18n, body),
     287           0 :     EventTypes.Reaction: (event, i18n, body) => i18n.sentReaction(
     288           0 :           event.senderFromMemoryOrFallback.calcDisplayname(i18n: i18n),
     289           0 :           event.content
     290           0 :                   .tryGetMap<String, Object?>('m.relates_to')
     291           0 :                   ?.tryGet<String>('key') ??
     292             :               body,
     293             :         ),
     294             :   };
     295             : }

Generated by: LCOV version 1.14