LCOV - code coverage report
Current view: top level - lib/views - peersettingsview.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 234 0.0 %
Date: 2026-07-15 19:28:30 Functions: 0 0 -

          Line data    Source code
       1             : import 'package:cwtch/config.dart';
       2             : import 'package:cwtch/cwtch_icons_icons.dart';
       3             : import 'package:cwtch/models/appstate.dart';
       4             : import 'package:cwtch/models/contact.dart';
       5             : import 'package:cwtch/models/profile.dart';
       6             : import 'package:cwtch/widgets/profileimage.dart';
       7             : import 'package:flutter/services.dart';
       8             : import 'package:cwtch/widgets/buttontextfield.dart';
       9             : import 'package:cwtch/widgets/cwtchlabel.dart';
      10             : import 'package:flutter/material.dart';
      11             : import 'package:cwtch/settings.dart';
      12             : import 'package:provider/provider.dart';
      13             : import 'package:cwtch/l10n/app_localizations.dart';
      14             : 
      15             : import '../main.dart';
      16             : import '../themes/opaque.dart';
      17             : 
      18             : /// Peer Settings View Provides  way to Configure .
      19             : class PeerSettingsView extends StatefulWidget {
      20           0 :   @override
      21           0 :   _PeerSettingsViewState createState() => _PeerSettingsViewState();
      22             : }
      23             : 
      24             : class _PeerSettingsViewState extends State<PeerSettingsView> {
      25           0 :   @override
      26             :   void dispose() {
      27           0 :     super.dispose();
      28             :   }
      29             : 
      30             :   final ctrlrNick = TextEditingController(text: "");
      31             :   ScrollController peerSettingsScrollController = ScrollController();
      32             : 
      33           0 :   @override
      34             :   void initState() {
      35           0 :     super.initState();
      36           0 :     final nickname = Provider.of<ContactInfoState>(context, listen: false).nickname;
      37           0 :     if (nickname.isNotEmpty) {
      38           0 :       ctrlrNick.text = nickname;
      39             :     }
      40             :   }
      41             : 
      42           0 :   @override
      43             :   Widget build(BuildContext context) {
      44           0 :     var handle = Provider.of<ContactInfoState>(context).nickname;
      45           0 :     if (handle.isEmpty) {
      46           0 :       handle = Provider.of<ContactInfoState>(context).onion;
      47             :     }
      48           0 :     return Scaffold(
      49           0 :       appBar: AppBar(
      50           0 :         title: Container(
      51           0 :           height: Provider.of<Settings>(context).fontScaling * 24.0,
      52             :           clipBehavior: Clip.hardEdge,
      53           0 :           decoration: BoxDecoration(),
      54           0 :           child: Text(handle + " " + AppLocalizations.of(context)!.conversationSettings),
      55             :         ),
      56             :       ),
      57           0 :       body: _buildSettingsList(),
      58             :     );
      59             :   }
      60             : 
      61           0 :   Widget _buildSettingsList() {
      62           0 :     return Consumer<Settings>(
      63           0 :       builder: (context, settings, child) {
      64           0 :         return LayoutBuilder(
      65           0 :           builder: (BuildContext context, BoxConstraints viewportConstraints) {
      66           0 :             String? acnCircuit = Provider.of<ContactInfoState>(context).acnCircuit;
      67             : 
      68           0 :             Widget path = Text(Provider.of<ContactInfoState>(context).status);
      69             : 
      70             :             if (acnCircuit != null) {
      71           0 :               var hops = acnCircuit.split(",");
      72           0 :               if (hops.length == 3) {
      73             :                 List<Widget> paths = hops
      74           0 :                     .map((String countryCodeAndIp) {
      75           0 :                       var parts = countryCodeAndIp.split(":");
      76           0 :                       var country = parts[0];
      77           0 :                       var ip = parts[1];
      78           0 :                       return RichText(
      79             :                         textAlign: TextAlign.left,
      80           0 :                         text: TextSpan(
      81             :                           text: country,
      82           0 :                           style: TextStyle(fontWeight: FontWeight.bold, fontSize: 10, fontFamily: "RobotoMono"),
      83           0 :                           children: [
      84           0 :                             TextSpan(
      85           0 :                               text: " ($ip)",
      86           0 :                               style: TextStyle(fontSize: 8, fontWeight: FontWeight.normal),
      87             :                             ),
      88             :                           ],
      89             :                         ),
      90             :                       );
      91             :                     })
      92           0 :                     .toList(growable: true);
      93             : 
      94           0 :                 paths.add(
      95           0 :                   RichText(
      96           0 :                     text: TextSpan(
      97           0 :                       text: AppLocalizations.of(context)!.labelTorNetwork,
      98           0 :                       style: TextStyle(fontWeight: FontWeight.normal, fontSize: 8, fontFamily: "monospace"),
      99             :                     ),
     100             :                   ),
     101             :                 );
     102             : 
     103           0 :                 path = Column(children: paths);
     104             :               }
     105             :             }
     106             : 
     107           0 :             List<Widget> evWidgets = Provider.of<ContactInfoState>(context, listen: false).contactEvents.map((ev) {
     108           0 :               return ListTile(
     109           0 :                 title: Text(ev.summary, style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)),
     110           0 :                 subtitle: Text(ev.timestamp.toLocal().toIso8601String(), style: Provider.of<Settings>(context).scaleFonts(defaultTextStyle)),
     111             :               );
     112           0 :             }).toList();
     113             : 
     114           0 :             return Scrollbar(
     115             :               trackVisibility: true,
     116           0 :               controller: peerSettingsScrollController,
     117           0 :               child: SingleChildScrollView(
     118             :                 clipBehavior: Clip.antiAlias,
     119           0 :                 controller: peerSettingsScrollController,
     120           0 :                 child: ConstrainedBox(
     121           0 :                   constraints: BoxConstraints(minHeight: viewportConstraints.maxHeight),
     122           0 :                   child: Container(
     123           0 :                     color: settings.theme.backgroundPaneColor,
     124           0 :                     padding: EdgeInsets.all(12),
     125           0 :                     child: Column(
     126             :                       mainAxisAlignment: MainAxisAlignment.start,
     127             :                       crossAxisAlignment: CrossAxisAlignment.center,
     128           0 :                       children: [
     129           0 :                         Column(
     130             :                           crossAxisAlignment: CrossAxisAlignment.center,
     131           0 :                           children: [
     132           0 :                             ProfileImage(
     133           0 :                               imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
     134           0 :                                   ? Provider.of<ContactInfoState>(context).imagePath
     135           0 :                                   : Provider.of<ContactInfoState>(context).defaultImagePath,
     136             :                               diameter: 120,
     137             :                               maskOut: false,
     138           0 :                               border: settings.theme.portraitOnlineBorderColor,
     139           0 :                               badgeTextColor: settings.theme.portraitContactBadgeTextColor,
     140           0 :                               badgeColor: settings.theme.portraitContactBadgeColor,
     141             :                               badgeEdit: false,
     142             :                             ),
     143           0 :                             SizedBox(
     144           0 :                               width: MediaQuery.of(context).size.width / 2,
     145           0 :                               child: Column(
     146           0 :                                 children: [
     147           0 :                                   Padding(
     148           0 :                                     padding: EdgeInsets.all(1),
     149           0 :                                     child: SelectableText(Provider.of<ContactInfoState>(context, listen: false).attributes[0] ?? "", textAlign: TextAlign.center),
     150             :                                   ),
     151           0 :                                   Padding(
     152           0 :                                     padding: EdgeInsets.all(1),
     153           0 :                                     child: SelectableText(Provider.of<ContactInfoState>(context, listen: false).attributes[1] ?? "", textAlign: TextAlign.center),
     154             :                                   ),
     155           0 :                                   Padding(
     156           0 :                                     padding: EdgeInsets.all(1),
     157           0 :                                     child: SelectableText(Provider.of<ContactInfoState>(context, listen: false).attributes[2] ?? "", textAlign: TextAlign.center),
     158             :                                   ),
     159             :                                 ],
     160             :                               ),
     161             :                             ),
     162             :                           ],
     163             :                         ),
     164             : 
     165           0 :                         Column(
     166             :                           mainAxisAlignment: MainAxisAlignment.start,
     167             :                           crossAxisAlignment: CrossAxisAlignment.start,
     168           0 :                           children: [
     169           0 :                             CwtchLabel(label: AppLocalizations.of(context)!.displayNameLabel),
     170           0 :                             SizedBox(height: 20),
     171           0 :                             CwtchButtonTextField(
     172           0 :                               controller: ctrlrNick,
     173             :                               readonly: false,
     174           0 :                               onPressed: () {
     175           0 :                                 var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     176           0 :                                 var conversation = Provider.of<ContactInfoState>(context, listen: false).identifier;
     177           0 :                                 Provider.of<ContactInfoState>(context, listen: false).localNickname = ctrlrNick.text;
     178           0 :                                 Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, conversation, "profile.name", ctrlrNick.text);
     179           0 :                                 final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess));
     180           0 :                                 ScaffoldMessenger.of(context).showSnackBar(snackBar);
     181             :                               },
     182           0 :                               icon: Icon(Icons.save),
     183           0 :                               tooltip: AppLocalizations.of(context)!.saveBtn,
     184             :                             ),
     185             :                           ],
     186             :                         ),
     187             : 
     188             :                         // Address Copy Button
     189           0 :                         Visibility(
     190           0 :                           visible: settings.streamerMode == false,
     191           0 :                           child: Column(
     192             :                             mainAxisAlignment: MainAxisAlignment.start,
     193             :                             crossAxisAlignment: CrossAxisAlignment.start,
     194           0 :                             children: [
     195           0 :                               SizedBox(height: 20),
     196           0 :                               CwtchLabel(label: AppLocalizations.of(context)!.addressLabel),
     197           0 :                               SizedBox(height: 20),
     198           0 :                               CwtchButtonTextField(
     199           0 :                                 controller: TextEditingController(text: Provider.of<ContactInfoState>(context, listen: false).onion),
     200           0 :                                 onPressed: _copyOnion,
     201           0 :                                 icon: Icon(CwtchIcons.address_copy),
     202           0 :                                 tooltip: AppLocalizations.of(context)!.copyBtn,
     203             :                               ),
     204             :                             ],
     205             :                           ),
     206             :                         ),
     207           0 :                         Column(
     208             :                           mainAxisAlignment: MainAxisAlignment.start,
     209             :                           crossAxisAlignment: CrossAxisAlignment.start,
     210           0 :                           children: [
     211           0 :                             SizedBox(height: 20),
     212           0 :                             CwtchLabel(label: AppLocalizations.of(context)!.conversationSettings),
     213           0 :                             SizedBox(height: 20),
     214           0 :                             ListTile(
     215           0 :                               leading: Icon(CwtchIcons.onion_on, color: settings.current().mainTextColor),
     216             :                               isThreeLine: true,
     217           0 :                               title: Text(AppLocalizations.of(context)!.labelACNCircuitInfo),
     218           0 :                               subtitle: Text(AppLocalizations.of(context)!.descriptionACNCircuitInfo),
     219             :                               trailing: path,
     220             :                             ),
     221           0 :                             SizedBox(height: 20),
     222           0 :                             SwitchListTile(
     223           0 :                               title: Text(AppLocalizations.of(context)!.blockBtn, style: TextStyle(color: settings.current().mainTextColor)),
     224           0 :                               value: Provider.of<ContactInfoState>(context).isBlocked,
     225           0 :                               onChanged: (bool blocked) {
     226           0 :                                 Provider.of<ContactInfoState>(context, listen: false).blocked = blocked;
     227             : 
     228           0 :                                 var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     229           0 :                                 var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
     230             : 
     231             :                                 if (blocked) {
     232           0 :                                   Provider.of<FlwtchState>(context, listen: false).cwtch.BlockContact(profileOnion, identifier);
     233             :                                 } else {
     234           0 :                                   Provider.of<FlwtchState>(context, listen: false).cwtch.UnblockContact(profileOnion, identifier);
     235             :                                 }
     236             :                               },
     237           0 :                               activeTrackColor: settings.theme.defaultButtonColor,
     238           0 :                               inactiveTrackColor: settings.theme.defaultButtonDisabledColor,
     239           0 :                               secondary: Icon(CwtchIcons.block_peer, color: settings.current().mainTextColor),
     240             :                             ),
     241           0 :                             ListTile(
     242           0 :                               title: Text(AppLocalizations.of(context)!.savePeerHistory, style: TextStyle(color: settings.current().mainTextColor)),
     243           0 :                               subtitle: Text(AppLocalizations.of(context)!.savePeerHistoryDescription),
     244           0 :                               leading: Icon(CwtchIcons.peer_history, color: settings.current().mainTextColor),
     245           0 :                               trailing: DropdownButton(
     246           0 :                                 value: (Provider.of<ContactInfoState>(context).savePeerHistory == "DefaultDeleteHistory" || Provider.of<ContactInfoState>(context).savePeerHistory == "HistoryDefault")
     247           0 :                                     ? AppLocalizations.of(context)!.conversationNotificationPolicyDefault
     248           0 :                                     : (Provider.of<ContactInfoState>(context).savePeerHistory == "SaveHistory"
     249           0 :                                           ? AppLocalizations.of(context)!.savePeerHistory
     250           0 :                                           : AppLocalizations.of(context)!.dontSavePeerHistory),
     251           0 :                                 onChanged: (newValue) {
     252           0 :                                   setState(() {
     253             :                                     // Set whether or not to dave the Contact History...
     254           0 :                                     var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     255           0 :                                     var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
     256             :                                     const SaveHistoryKey = "profile.SavePeerHistory";
     257             :                                     const SaveHistory = "SaveHistory";
     258             :                                     const DelHistory = "DeleteHistoryConfirmed";
     259             :                                     const HistoryDefault = "HistoryDefault";
     260             : 
     261             :                                     // NOTE: .savePeerHistory is used to show ephemeral warnings so it's state is important to update.
     262           0 :                                     if (newValue == AppLocalizations.of(context)!.conversationNotificationPolicyDefault) {
     263           0 :                                       Provider.of<ContactInfoState>(context, listen: false).savePeerHistory = HistoryDefault;
     264           0 :                                       Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, SaveHistoryKey, HistoryDefault);
     265           0 :                                     } else if (newValue == AppLocalizations.of(context)!.savePeerHistory) {
     266           0 :                                       Provider.of<ContactInfoState>(context, listen: false).savePeerHistory = SaveHistory;
     267           0 :                                       Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, SaveHistoryKey, SaveHistory);
     268             :                                     } else {
     269           0 :                                       Provider.of<ContactInfoState>(context, listen: false).savePeerHistory = DelHistory;
     270           0 :                                       Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, SaveHistoryKey, DelHistory);
     271             :                                     }
     272             :                                   });
     273             :                                 },
     274             :                                 items:
     275           0 :                                     [
     276           0 :                                       AppLocalizations.of(context)!.conversationNotificationPolicyDefault,
     277           0 :                                       AppLocalizations.of(context)!.savePeerHistory,
     278           0 :                                       AppLocalizations.of(context)!.dontSavePeerHistory,
     279           0 :                                     ].map<DropdownMenuItem<String>>((String value) {
     280           0 :                                       return DropdownMenuItem<String>(
     281             :                                         value: value,
     282           0 :                                         child: Text(value, style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
     283             :                                       );
     284           0 :                                     }).toList(),
     285             :                               ),
     286             :                             ),
     287           0 :                             ListTile(
     288           0 :                               title: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingLabel, style: TextStyle(color: settings.current().mainTextColor)),
     289           0 :                               subtitle: Text(AppLocalizations.of(context)!.conversationNotificationPolicySettingDescription),
     290           0 :                               leading: Icon(CwtchIcons.chat_bubble_empty_24px, color: settings.current().mainTextColor),
     291           0 :                               trailing: DropdownButton(
     292           0 :                                 value: Provider.of<ContactInfoState>(context).notificationsPolicy,
     293           0 :                                 items: ConversationNotificationPolicy.values.map<DropdownMenuItem<ConversationNotificationPolicy>>((ConversationNotificationPolicy value) {
     294           0 :                                   return DropdownMenuItem<ConversationNotificationPolicy>(
     295             :                                     value: value,
     296           0 :                                     child: Text(value.toName(context), style: settings.scaleFonts(defaultDropDownMenuItemTextStyle)),
     297             :                                   );
     298           0 :                                 }).toList(),
     299           0 :                                 onChanged: (ConversationNotificationPolicy? newVal) {
     300           0 :                                   Provider.of<ContactInfoState>(context, listen: false).notificationsPolicy = newVal!;
     301           0 :                                   var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     302           0 :                                   var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
     303             :                                   const NotificationPolicyKey = "profile.notification-policy";
     304           0 :                                   Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, identifier, NotificationPolicyKey, newVal.toString());
     305             :                                 },
     306             :                               ),
     307             :                             ),
     308             :                           ],
     309             :                         ),
     310           0 :                         Column(
     311             :                           mainAxisAlignment: MainAxisAlignment.end,
     312             :                           crossAxisAlignment: CrossAxisAlignment.end,
     313           0 :                           children: [
     314           0 :                             SizedBox(height: 20),
     315           0 :                             Row(
     316             :                               crossAxisAlignment: CrossAxisAlignment.center,
     317             :                               mainAxisAlignment: MainAxisAlignment.end,
     318           0 :                               children: [
     319           0 :                                 Tooltip(
     320           0 :                                   message: AppLocalizations.of(context)!.archiveConversation,
     321           0 :                                   child: OutlinedButton.icon(
     322           0 :                                     onPressed: () {
     323           0 :                                       var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     324           0 :                                       var handle = Provider.of<ContactInfoState>(context, listen: false).identifier;
     325             :                                       // locally update cache...
     326           0 :                                       Provider.of<ContactInfoState>(context, listen: false).isArchived = true;
     327           0 :                                       Provider.of<FlwtchState>(context, listen: false).cwtch.ArchiveConversation(profileOnion, handle);
     328           0 :                                       Future.delayed(Duration(milliseconds: 500), () {
     329           0 :                                         Provider.of<AppState>(context, listen: false).selectedConversation = null;
     330           0 :                                         Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog
     331             :                                       });
     332             :                                     },
     333           0 :                                     icon: Icon(Icons.archive),
     334           0 :                                     label: Text(AppLocalizations.of(context)!.archiveConversation),
     335             :                                   ),
     336             :                                 ),
     337             :                               ],
     338             :                             ),
     339           0 :                             SizedBox(height: 20),
     340           0 :                             Row(
     341             :                               crossAxisAlignment: CrossAxisAlignment.center,
     342             :                               mainAxisAlignment: MainAxisAlignment.end,
     343           0 :                               children: [
     344           0 :                                 Tooltip(
     345           0 :                                   message: AppLocalizations.of(context)!.leaveConversation,
     346           0 :                                   child: OutlinedButton.icon(
     347           0 :                                     onPressed: () {
     348           0 :                                       showAlertDialog(context);
     349             :                                     },
     350           0 :                                     icon: Icon(CwtchIcons.leave_group),
     351           0 :                                     label: Text(AppLocalizations.of(context)!.leaveConversation),
     352             :                                   ),
     353             :                                 ),
     354             :                               ],
     355             :                             ),
     356             :                           ],
     357             :                         ),
     358           0 :                         Visibility(
     359           0 :                           visible: EnvironmentConfig.BUILD_VER == dev_version,
     360             :                           maintainSize: false,
     361           0 :                           child: Column(children: evWidgets),
     362             :                         ),
     363             :                       ],
     364             :                     ),
     365             :                   ),
     366             :                 ),
     367             :               ),
     368             :             );
     369             :           },
     370             :         );
     371             :       },
     372             :     );
     373             :   }
     374             : 
     375           0 :   void _copyOnion() {
     376           0 :     Clipboard.setData(new ClipboardData(text: Provider.of<ContactInfoState>(context, listen: false).onion));
     377           0 :     final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification));
     378           0 :     ScaffoldMessenger.of(context).showSnackBar(snackBar);
     379             :   }
     380             : 
     381           0 :   showAlertDialog(BuildContext context) {
     382             :     // set up the buttons
     383           0 :     Widget cancelButton = ElevatedButton(
     384           0 :       child: Text(AppLocalizations.of(context)!.cancel),
     385           0 :       style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
     386           0 :       onPressed: () {
     387           0 :         Navigator.of(context).pop(); // dismiss dialog
     388             :       },
     389             :     );
     390           0 :     Widget continueButton = ElevatedButton(
     391           0 :       style: ButtonStyle(padding: MaterialStateProperty.all(EdgeInsets.all(20))),
     392           0 :       child: Text(AppLocalizations.of(context)!.yesLeave),
     393           0 :       onPressed: () {
     394           0 :         var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     395           0 :         var identifier = Provider.of<ContactInfoState>(context, listen: false).identifier;
     396             :         // locally update cache...
     397           0 :         Provider.of<ProfileInfoState>(context, listen: false).contactList.removeContact(identifier);
     398           0 :         Provider.of<FlwtchState>(context, listen: false).cwtch.DeleteContact(profileOnion, identifier);
     399           0 :         Future.delayed(Duration(milliseconds: 500), () {
     400           0 :           Provider.of<AppState>(context, listen: false).selectedConversation = null;
     401           0 :           Navigator.of(context).popUntil((route) => route.settings.name == "conversations"); // dismiss dialog
     402             :         });
     403             :       },
     404             :     );
     405             : 
     406             :     // set up the AlertDialog
     407           0 :     AlertDialog alert = AlertDialog(title: Text(AppLocalizations.of(context)!.reallyLeaveThisGroupPrompt), actions: [cancelButton, continueButton]);
     408             : 
     409             :     // show the dialog
     410           0 :     showDialog(
     411             :       context: context,
     412           0 :       builder: (BuildContext context) {
     413             :         return alert;
     414             :       },
     415             :     );
     416             :   }
     417             : }

Generated by: LCOV version 1.14