LCOV - code coverage report
Current view: top level - lib/widgets - invitationbubble.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 93 0.0 %
Date: 2024-09-24 21:50:54 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:cwtch/cwtch_icons_icons.dart';
       2             : import 'package:cwtch/models/contact.dart';
       3             : import 'package:cwtch/models/message.dart';
       4             : import 'package:cwtch/models/profile.dart';
       5             : import 'package:cwtch/themes/opaque.dart';
       6             : import 'package:cwtch/widgets/malformedbubble.dart';
       7             : import 'package:flutter/material.dart';
       8             : import 'package:provider/provider.dart';
       9             : import '../main.dart';
      10             : import 'package:flutter_gen/gen_l10n/app_localizations.dart';
      11             : 
      12             : import '../models/redaction.dart';
      13             : import '../settings.dart';
      14             : import 'messageBubbleWidgetHelpers.dart';
      15             : import 'messagebubbledecorations.dart';
      16             : 
      17             : // Like MessageBubble but for displaying chat overlay 100/101 invitations
      18             : // Offers the user an accept/reject button if they don't have a matching contact already
      19             : class InvitationBubble extends StatefulWidget {
      20             :   final int overlay;
      21             :   final String inviteTarget;
      22             :   final String inviteNick;
      23             :   final String invite;
      24             : 
      25           0 :   InvitationBubble(this.overlay, this.inviteTarget, this.inviteNick, this.invite);
      26             : 
      27           0 :   @override
      28           0 :   InvitationBubbleState createState() => InvitationBubbleState();
      29             : }
      30             : 
      31             : class InvitationBubbleState extends State<InvitationBubble> {
      32             :   bool rejected = false;
      33             :   bool isAccepted = false;
      34             : 
      35           0 :   @override
      36             :   Widget build(BuildContext context) {
      37           0 :     var fromMe = Provider.of<MessageMetadata>(context).senderHandle == Provider.of<ProfileInfoState>(context).onion;
      38           0 :     var isGroup = widget.overlay == InviteGroupOverlay;
      39           0 :     isAccepted = Provider.of<ProfileInfoState>(context).contactList.findContact(widget.inviteTarget) != null;
      40             :     var borderRadiousEh = 15.0;
      41           0 :     var showGroupInvite = Provider.of<Settings>(context).isExperimentEnabled(TapirGroupsExperiment);
      42           0 :     rejected = Provider.of<MessageMetadata>(context).attributes["rejected-invite"] == "true";
      43           0 :     DateTime messageDate = Provider.of<MessageMetadata>(context).timestamp;
      44             : 
      45             :     // If the sender is not us, then we want to give them a nickname...
      46             :     var senderDisplayStr = "";
      47             :     if (!fromMe) {
      48           0 :       ContactInfoState? contact = Provider.of<ProfileInfoState>(context).contactList.findContact(Provider.of<MessageMetadata>(context).senderHandle);
      49             :       if (contact != null) {
      50           0 :         senderDisplayStr = redactedNick(context, contact.onion, contact.nickname);
      51             :       } else {
      52           0 :         senderDisplayStr = Provider.of<MessageMetadata>(context).senderHandle;
      53             :       }
      54             :     }
      55           0 :     return LayoutBuilder(builder: (context, constraints) {
      56           0 :       var wdgSender = compileSenderWidget(context, constraints, fromMe, senderDisplayStr);
      57             :       // If we receive an invite for ourselves, treat it as a bug. The UI no longer allows this so it could have only come from
      58             :       // some kind of malfeasance.
      59           0 :       var selfInvite = widget.inviteNick == Provider.of<ProfileInfoState>(context).onion;
      60             :       if (selfInvite) {
      61           0 :         return MalformedBubble();
      62             :       }
      63             : 
      64             :       var wdgMessage = isGroup && !showGroupInvite
      65           0 :           ? Text(AppLocalizations.of(context)!.groupInviteSettingsWarning, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle))
      66             :           : fromMe
      67           0 :               ? senderInviteChrome(
      68           0 :                   AppLocalizations.of(context)!.sendAnInvitation, isGroup ? Provider.of<ProfileInfoState>(context).contactList.findContact(widget.inviteTarget)!.nickname : widget.inviteTarget)
      69           0 :               : (inviteChrome(isGroup ? AppLocalizations.of(context)!.inviteToGroup : AppLocalizations.of(context)!.contactSuggestion, widget.inviteNick, widget.inviteTarget));
      70             : 
      71             :       Widget wdgDecorations;
      72             :       if (isGroup && !showGroupInvite) {
      73           0 :         wdgDecorations = Text('\u202F');
      74             :       } else if (fromMe) {
      75           0 :         wdgDecorations = MessageBubbleDecoration(ackd: Provider.of<MessageMetadata>(context).ackd, errored: Provider.of<MessageMetadata>(context).error, fromMe: fromMe, messageDate: messageDate);
      76           0 :       } else if (isAccepted) {
      77           0 :         wdgDecorations = Text(AppLocalizations.of(context)!.accepted + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
      78           0 :       } else if (this.rejected) {
      79           0 :         wdgDecorations = Text(AppLocalizations.of(context)!.rejected + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle));
      80             :       } else {
      81           0 :         wdgDecorations = Center(
      82             :             widthFactor: 1,
      83           0 :             child: Wrap(children: [
      84           0 :               Padding(
      85           0 :                   padding: EdgeInsets.all(5),
      86           0 :                   child: ElevatedButton(
      87           0 :                       child: Text(AppLocalizations.of(context)!.rejectGroupBtn + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnReject)),
      88           0 :               Padding(
      89           0 :                   padding: EdgeInsets.all(5),
      90           0 :                   child: ElevatedButton(
      91           0 :                       child: Text(AppLocalizations.of(context)!.acceptGroupBtn + '\u202F', style: Provider.of<Settings>(context).scaleFonts(defaultTextButtonStyle)), onPressed: _btnAccept)),
      92             :             ]));
      93             :       }
      94             : 
      95             :       //print(constraints.toString()+", "+constraints.maxWidth.toString());
      96           0 :       return Center(
      97             :           widthFactor: 1.0,
      98           0 :           child: Theme(
      99           0 :               data: Theme.of(context).copyWith(
     100           0 :                 textSelectionTheme: TextSelectionThemeData(
     101           0 :                     cursorColor: Provider.of<Settings>(context).theme.messageSelectionColor,
     102           0 :                     selectionColor: Provider.of<Settings>(context).theme.messageSelectionColor,
     103           0 :                     selectionHandleColor: Provider.of<Settings>(context).theme.messageSelectionColor),
     104             : 
     105             :                 // Horrifying Hack: Flutter doesn't give us direct control over system menus but instead picks BG color from TextButtonThemeData ¯\_(ツ)_/¯
     106           0 :                 textButtonTheme: TextButtonThemeData(
     107           0 :                   style: ButtonStyle(backgroundColor: MaterialStateProperty.all(Provider.of<Settings>(context).theme.menuBackgroundColor)),
     108             :                 ),
     109             :               ),
     110           0 :               child: Container(
     111           0 :                   decoration: BoxDecoration(
     112           0 :                     color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor,
     113             :                     border:
     114           0 :                         Border.all(color: fromMe ? Provider.of<Settings>(context).theme.messageFromMeBackgroundColor : Provider.of<Settings>(context).theme.messageFromOtherBackgroundColor, width: 1),
     115           0 :                     borderRadius: BorderRadius.only(
     116           0 :                       topLeft: Radius.circular(borderRadiousEh),
     117           0 :                       topRight: Radius.circular(borderRadiousEh),
     118           0 :                       bottomLeft: fromMe ? Radius.circular(borderRadiousEh) : Radius.zero,
     119           0 :                       bottomRight: fromMe ? Radius.zero : Radius.circular(borderRadiousEh),
     120             :                     ),
     121             :                   ),
     122           0 :                   child: Center(
     123             :                       widthFactor: 1.0,
     124           0 :                       child: Padding(
     125           0 :                           padding: EdgeInsets.all(9.0),
     126           0 :                           child: Wrap(runAlignment: WrapAlignment.spaceEvenly, alignment: WrapAlignment.spaceEvenly, runSpacing: 1.0, crossAxisAlignment: WrapCrossAlignment.center, children: [
     127           0 :                             Center(
     128             :                                 widthFactor: 1,
     129           0 :                                 child: Padding(padding: EdgeInsets.all(10.0), child: Icon(isGroup && !showGroupInvite ? CwtchIcons.enable_experiments : CwtchIcons.send_invite, size: 32))),
     130           0 :                             Center(
     131             :                               widthFactor: 1.0,
     132           0 :                               child: Column(
     133             :                                   crossAxisAlignment: fromMe ? CrossAxisAlignment.end : CrossAxisAlignment.start,
     134             :                                   mainAxisAlignment: fromMe ? MainAxisAlignment.end : MainAxisAlignment.start,
     135             :                                   mainAxisSize: MainAxisSize.min,
     136           0 :                                   children: fromMe ? [wdgMessage, wdgDecorations] : [wdgSender, wdgMessage, wdgDecorations]),
     137             :                             )
     138             :                           ]))))));
     139             :     });
     140             :   }
     141             : 
     142           0 :   void _btnReject() {
     143           0 :     setState(() {
     144           0 :       var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
     145           0 :       var conversation = Provider.of<ContactInfoState>(context, listen: false).identifier;
     146           0 :       var idx = Provider.of<MessageMetadata>(context, listen: false).messageID;
     147           0 :       Provider.of<FlwtchState>(context, listen: false).cwtch.SetMessageAttribute(profileOnion, conversation, 0, idx, "rejected-invite", "true");
     148             :       //Provider.of<MessageMetadata>(context, listen: false).flags |= 0x01;
     149             :     });
     150             :   }
     151             : 
     152           0 :   void _btnAccept() {
     153           0 :     setState(() {
     154           0 :       var profileOnion = Provider.of<ProfileInfoState>(context, listen: false).onion;
     155           0 :       Provider.of<FlwtchState>(context, listen: false).cwtch.ImportBundle(profileOnion, widget.invite);
     156           0 :       isAccepted = true;
     157             :     });
     158             :   }
     159             : 
     160             :   // Construct an invite chrome for the sender
     161           0 :   Widget senderInviteChrome(String chrome, String targetName) {
     162           0 :     var settings = Provider.of<Settings>(context);
     163             : 
     164           0 :     return Wrap(children: [
     165           0 :       SelectableText(
     166           0 :         chrome + '\u202F',
     167           0 :         style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
     168             :         textAlign: TextAlign.left,
     169             :         maxLines: 2,
     170             :         textWidthBasis: TextWidthBasis.longestLine,
     171             :       ),
     172           0 :       SelectableText(
     173           0 :         targetName + '\u202F',
     174           0 :         style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromMeTextColor)),
     175             :         textAlign: TextAlign.left,
     176             :         maxLines: 2,
     177             :         textWidthBasis: TextWidthBasis.longestLine,
     178             :       )
     179             :     ]);
     180             :   }
     181             : 
     182             :   // Construct an invite chrome
     183           0 :   Widget inviteChrome(String chrome, String targetName, String targetId) {
     184           0 :     var settings = Provider.of<Settings>(context);
     185             : 
     186           0 :     return Wrap(children: [
     187           0 :       SelectableText(
     188           0 :         chrome + '\u202F',
     189           0 :         style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
     190             :         textAlign: TextAlign.left,
     191             :         textWidthBasis: TextWidthBasis.longestLine,
     192             :         maxLines: 2,
     193             :       ),
     194           0 :       SelectableText(
     195           0 :         targetName + '\u202F',
     196           0 :         style: settings.scaleFonts(defaultMessageTextStyle.copyWith(color: Provider.of<Settings>(context).theme.messageFromOtherTextColor)),
     197             :         textAlign: TextAlign.left,
     198             :         maxLines: 2,
     199             :         textWidthBasis: TextWidthBasis.longestLine,
     200             :       )
     201             :     ]);
     202             :   }
     203             : }

Generated by: LCOV version 1.14