LCOV - code coverage report
Current view: top level - lib/views - editgroupview.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 0 263 0.0 %
Date: 2026-08-30 22:38:45 Functions: 0 0 -

          Line data    Source code
       1             : import 'dart:io';
       2             : 
       3             : import 'package:cwtch/config.dart';
       4             : import 'package:cwtch/cwtch_icons_icons.dart';
       5             : import 'package:cwtch/controllers/filesharing.dart' as filesharing;
       6             : import 'package:cwtch/settings.dart';
       7             : import 'package:cwtch/models/appstate.dart';
       8             : import 'package:cwtch/models/contact.dart';
       9             : import 'package:cwtch/models/contactlist.dart';
      10             : import 'package:cwtch/models/groupmembers.dart';
      11             : import 'package:cwtch/models/profile.dart';
      12             : import 'package:cwtch/widgets/buttontextfield.dart';
      13             : import 'package:cwtch/widgets/cwtchlabel.dart';
      14             : import 'package:cwtch/widgets/DropdownContacts.dart';
      15             : import 'package:cwtch/widgets/profileimage.dart';
      16             : import 'package:flutter/services.dart';
      17             : import 'package:flutter/material.dart';
      18             : import 'package:provider/provider.dart';
      19             : import 'package:cwtch/l10n/app_localizations.dart';
      20             : 
      21             : import '../constants.dart';
      22             : import '../main.dart';
      23             : import '../themes/opaque.dart';
      24             : 
      25             : class EditGroupView extends StatefulWidget {
      26           0 :   @override
      27           0 :   _EditGroupViewState createState() => _EditGroupViewState();
      28             : }
      29             : 
      30             : class _EditGroupViewState extends State<EditGroupView> {
      31             :   bool maskControlsOpen = false;
      32             : 
      33           0 :   @override
      34             :   void dispose() {
      35           0 :     super.dispose();
      36             :   }
      37             : 
      38             :   final ctrlrNick = TextEditingController(text: "");
      39             :   ScrollController metaScrollController = ScrollController();
      40             :   ScrollController modeScrollController = ScrollController();
      41             :   ScrollController maskScrollController = ScrollController();
      42             :   final ctrlrRemoteName = TextEditingController(text: "");
      43             :   final ctrlrAttr1 = TextEditingController(text: "");
      44             :   final ctrlrAttr2 = TextEditingController(text: "");
      45             :   final ctrlrAttr3 = TextEditingController(text: "");
      46             : 
      47           0 :   @override
      48             :   void initState() {
      49           0 :     super.initState();
      50           0 :     final nickname = Provider.of<ContactInfoState>(context, listen: false).nickname;
      51           0 :     if (nickname.isNotEmpty) {
      52           0 :       ctrlrNick.text = nickname;
      53             :     }
      54           0 :     if (Provider.of<ContactInfoState>(context, listen: false).remoteNickname != null) {
      55           0 :       ctrlrRemoteName.text = Provider.of<ContactInfoState>(context, listen: false).remoteNickname!;
      56             :     }
      57           0 :     if (Provider.of<ContactInfoState>(context, listen: false).attributes[0] != null) {
      58           0 :       ctrlrAttr1.text = Provider.of<ContactInfoState>(context, listen: false).attributes[0]!;
      59             :     }
      60           0 :     if (Provider.of<ContactInfoState>(context, listen: false).attributes[1] != null) {
      61           0 :       ctrlrAttr2.text = Provider.of<ContactInfoState>(context, listen: false).attributes[1]!;
      62             :     }
      63           0 :     if (Provider.of<ContactInfoState>(context, listen: false).attributes[2] != null) {
      64           0 :       ctrlrAttr3.text = Provider.of<ContactInfoState>(context, listen: false).attributes[2]!;
      65             :     }
      66             :   }
      67             : 
      68           0 :   @override
      69             :   Widget build(BuildContext context) {
      70           0 :     var handle = Provider.of<ContactInfoState>(context).nickname;
      71           0 :     if (handle.isEmpty) {
      72           0 :       handle = Provider.of<ContactInfoState>(context).onion;
      73             :     }
      74           0 :     return DefaultTabController(
      75             :       length: 3,
      76           0 :       child: Scaffold(
      77           0 :         appBar: AppBar(
      78           0 :           title: Container(
      79           0 :             height: Provider.of<Settings>(context).fontScaling * 24.0,
      80             :             clipBehavior: Clip.hardEdge,
      81           0 :             decoration: BoxDecoration(),
      82           0 :             child: Text(handle + " ยป " + AppLocalizations.of(context)!.editGroupTitle),
      83             :           ),
      84           0 :           bottom: TabBar(
      85             :             isScrollable: true,
      86           0 :             tabs: [
      87           0 :               Tab(key: Key("OpenGroupMetadata"), icon: Icon(CwtchIcons.edit_24px), text: AppLocalizations.of(context)!.editGroupMetadataTitle),
      88           0 :               Tab(key: Key("OpenGroupMode"), icon: Icon(CwtchIcons.anti_spam_1), text: AppLocalizations.of(context)!.editGroupModeTitle),
      89           0 :               Tab(key: Key("OpenGroupMask"), icon: Icon(CwtchIcons.member_mod), text: AppLocalizations.of(context)!.editGroupMaskTitle),
      90             :             ],
      91             :           ),
      92             :         ),
      93           0 :         body: _buildGroupEditor(),
      94             :       ),
      95             :     );
      96             :   }
      97             : 
      98           0 :   Widget _buildGroupEditor() {
      99           0 :     return Consumer<Settings>(
     100           0 :       builder: (context, settings, child) {
     101           0 :         return LayoutBuilder(
     102           0 :           builder: (BuildContext context, BoxConstraints viewportConstraints) {
     103           0 :             return TabBarView(
     104           0 :               children: [
     105           0 :                 _tabHelper(_buildMetaTab(settings), "EditGroupMeta", this.metaScrollController),
     106           0 :                 _tabHelper(_buildModeTab(settings), "EditGroupMode", this.modeScrollController),
     107           0 :                 _tabHelper(_buildMaskTab(settings), "EditGroupMask", this.maskScrollController),
     108             :               ],
     109             :             );
     110             :           },
     111             :         );
     112             :       },
     113             :     );
     114             :   }
     115             : 
     116           0 :   Widget _tabHelper(Widget tab, String scrollKey, ScrollController sc) {
     117           0 :     return Consumer<Settings>(
     118           0 :       builder: (ccontext, settings, child) {
     119           0 :         return LayoutBuilder(
     120           0 :           builder: (BuildContext context, BoxConstraints viewportConstraints) {
     121           0 :             return Scrollbar(
     122           0 :               key: Key(scrollKey),
     123             :               trackVisibility: true,
     124             :               controller: sc,
     125           0 :               child: SingleChildScrollView(
     126             :                 clipBehavior: Clip.antiAlias,
     127             :                 controller: sc,
     128           0 :                 child: ConstrainedBox(
     129           0 :                   constraints: BoxConstraints(minHeight: viewportConstraints.maxHeight, maxWidth: viewportConstraints.maxWidth),
     130           0 :                   child: Material(
     131           0 :                     color: settings.theme.backgroundPaneColor,
     132           0 :                     child: Container(width: viewportConstraints.maxWidth / 2, margin: EdgeInsetsGeometry.all(16.0), child: tab),
     133             :                   ),
     134             :                 ),
     135             :               ),
     136             :             );
     137             :           },
     138             :         );
     139             :       },
     140             :     );
     141             :   }
     142             : 
     143           0 :   Widget _buildMetaTab(Settings settings) {
     144           0 :     bool pending = Provider.of<ContactInfoState>(context).isOpPending;
     145           0 :     bool isOpen = Provider.of<ContactInfoState>(context).modeLine.contains('o');
     146             : 
     147           0 :     return Column(
     148             :       mainAxisAlignment: MainAxisAlignment.start,
     149             :       crossAxisAlignment: CrossAxisAlignment.center,
     150           0 :       children: [
     151           0 :         Column(
     152             :           crossAxisAlignment: CrossAxisAlignment.center,
     153           0 :           children: [
     154           0 :             _buildGroupPictureBox(),
     155           0 :             SizedBox(width: MediaQuery.of(context).size.width / 2, child: !pending && CanUpdateMetadata() ? _buildMetadataEditor() : _buildMetadataViewer()),
     156             :           ],
     157             :         ),
     158             : 
     159           0 :         Column(
     160             :           mainAxisAlignment: MainAxisAlignment.start,
     161             :           crossAxisAlignment: CrossAxisAlignment.start,
     162           0 :           children: [
     163           0 :             CwtchLabel(label: AppLocalizations.of(context)!.displayNameLocalOnly),
     164           0 :             SizedBox(height: 20),
     165           0 :             CwtchButtonTextField(
     166           0 :               controller: ctrlrNick,
     167             :               readonly: false,
     168           0 :               onPressed: () {
     169           0 :                 var profileOnion = Provider.of<ContactInfoState>(context, listen: false).profileOnion;
     170           0 :                 var conversation = Provider.of<ContactInfoState>(context, listen: false).identifier;
     171           0 :                 Provider.of<ContactInfoState>(context, listen: false).localNickname = ctrlrNick.text;
     172           0 :                 Provider.of<FlwtchState>(context, listen: false).cwtch.SetConversationAttribute(profileOnion, conversation, "profile.name", ctrlrNick.text);
     173           0 :                 final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess));
     174           0 :                 ScaffoldMessenger.of(context).showSnackBar(snackBar);
     175             :               },
     176           0 :               icon: Icon(Icons.save),
     177           0 :               tooltip: AppLocalizations.of(context)!.saveBtn,
     178             :             ),
     179             :           ],
     180             :         ),
     181             : 
     182           0 :         SizedBox(height: 20),
     183           0 :         CwtchLabel(label: AppLocalizations.of(context)!.addressLabel),
     184           0 :         SizedBox(height: 20),
     185             : 
     186             :         // Address Copy Button
     187           0 :         Visibility(
     188           0 :           visible: settings.streamerMode == false && isOpen,
     189           0 :           child: Column(
     190             :             mainAxisAlignment: MainAxisAlignment.start,
     191             :             crossAxisAlignment: CrossAxisAlignment.start,
     192           0 :             children: [
     193           0 :               Text(AppLocalizations.of(context)!.anyoneCanJoin),
     194           0 :               CwtchButtonTextField(
     195           0 :                 controller: TextEditingController(text: "grwp-" + Provider.of<ContactInfoState>(context, listen: false).onion),
     196           0 :                 onPressed: isOpen ? _copyOnion : null,
     197           0 :                 icon: Icon(CwtchIcons.copy_address),
     198           0 :                 tooltip: AppLocalizations.of(context)!.copyBtn,
     199             :               ),
     200             :             ],
     201             :           ),
     202             :         ),
     203           0 :         Visibility(visible: !isOpen, child: Text(AppLocalizations.of(context)!.inviteRequired)),
     204             :       ],
     205             :     );
     206             :   }
     207             : 
     208           0 :   Widget _buildModeTab(Settings settings) {
     209           0 :     return Column(
     210             :       mainAxisAlignment: MainAxisAlignment.start,
     211             :       crossAxisAlignment: CrossAxisAlignment.start,
     212           0 :       children: [_buildModeBox(Provider.of<ContactInfoState>(context).modeLine, Provider.of<ContactInfoState>(context).modeMask)],
     213             :     );
     214             :   }
     215             : 
     216           0 :   Widget _buildMaskTab(Settings settings) {
     217           0 :     return Column(mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [_buildModeMaskBox(Provider.of<ContactInfoState>(context).modeMask)]);
     218             :   }
     219             : 
     220           0 :   void _copyOnion() {
     221           0 :     Clipboard.setData(new ClipboardData(text: "grwp-" + Provider.of<ContactInfoState>(context, listen: false).onion));
     222           0 :     final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.copiedToClipboardNotification));
     223           0 :     ScaffoldMessenger.of(context).showSnackBar(snackBar);
     224             :   }
     225             : 
     226           0 :   Widget _buildGroupPictureBox() {
     227           0 :     return Consumer<Settings>(
     228           0 :       builder: (context, theme, child) {
     229             :         var isStatic =
     230           0 :             Provider.of<AppState>(context, listen: false).disableFilePicker ||
     231           0 :             !Provider.of<Settings>(context, listen: false).isExperimentEnabled(FileSharingExperiment) ||
     232           0 :             !Provider.of<Settings>(context, listen: false).isExperimentEnabled(ImagePreviewsExperiment) ||
     233           0 :             !Provider.of<ContactInfoState>(context, listen: false).modeLine.contains('f') ||
     234           0 :             !CanUpdateMetadata() ||
     235           0 :             Provider.of<ContactInfoState>(context, listen: false).isOpPending;
     236             : 
     237           0 :         return MouseRegion(
     238             :           cursor: !isStatic ? SystemMouseCursors.click : SystemMouseCursors.basic,
     239           0 :           child: GestureDetector(
     240             :             // don't allow setting of images if the image previews experiment is disabled.
     241             :             onTap: isStatic
     242             :                 ? null
     243           0 :                 : () {
     244           0 :                     filesharing.showFilePicker(
     245             :                       context,
     246             :                       MaxImageFileSharingSize,
     247           0 :                       (File file) {
     248           0 :                         Provider.of<FlwtchState>(
     249             :                           context,
     250             :                           listen: false,
     251           0 :                         ).cwtch.UpdateGroupPicture(Provider.of<ProfileInfoState>(context, listen: false).onion, Provider.of<ContactInfoState>(context, listen: false).identifier, file.path);
     252             :                         // update the image cache locally
     253             :                         // TODO: should put a spinner here instead, so the user knows something is happening while it syncs
     254             :                         // Provider.of<ProfileInfoState>(context, listen: false).imagePath = file.path;
     255             :                       },
     256           0 :                       () {
     257           0 :                         final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.msgFileTooBig), duration: Duration(seconds: 4));
     258           0 :                         ScaffoldMessenger.of(context).showSnackBar(snackBar);
     259             :                       },
     260           0 :                       () {},
     261             :                     );
     262             :                   },
     263           0 :             child: ProfileImage(
     264           0 :               imagePath: Provider.of<Settings>(context).isExperimentEnabled(ImagePreviewsExperiment)
     265           0 :                   ? Provider.of<ContactInfoState>(context).imagePath
     266           0 :                   : Provider.of<ContactInfoState>(context).defaultImagePath,
     267             :               diameter: 120,
     268           0 :               tooltip: isStatic ? "" : AppLocalizations.of(context)!.changeGroupPicture,
     269             :               maskOut: false,
     270           0 :               border: theme.theme.portraitOnlineBorderColor,
     271           0 :               badgeTextColor: theme.theme.portraitContactBadgeTextColor,
     272           0 :               badgeColor: theme.theme.portraitContactBadgeColor,
     273             :               badgeEdit: !isStatic,
     274             :               managed: true,
     275             :             ),
     276             :           ),
     277             :         );
     278             :       },
     279             :     );
     280             :   }
     281             : 
     282           0 :   Widget _buildMetadataViewer() {
     283           0 :     return Column(
     284           0 :       children: [
     285           0 :         Padding(
     286           0 :           padding: EdgeInsets.all(1),
     287           0 :           child: CwtchLabel(label: Provider.of<ContactInfoState>(context).remoteNickname ?? ""),
     288             :         ),
     289           0 :         Padding(
     290           0 :           padding: EdgeInsets.all(1),
     291           0 :           child: SelectableText(Provider.of<ContactInfoState>(context).attributes[0] ?? "", textAlign: TextAlign.center),
     292             :         ),
     293           0 :         Padding(
     294           0 :           padding: EdgeInsets.all(1),
     295           0 :           child: SelectableText(Provider.of<ContactInfoState>(context).attributes[1] ?? "", textAlign: TextAlign.center),
     296             :         ),
     297           0 :         Padding(
     298           0 :           padding: EdgeInsets.all(1),
     299           0 :           child: SelectableText(Provider.of<ContactInfoState>(context).attributes[2] ?? "", textAlign: TextAlign.center),
     300             :         ),
     301             :       ],
     302             :     );
     303             :   }
     304             : 
     305           0 :   Widget _buildMetadataEditor() {
     306           0 :     return Column(
     307           0 :       children: [
     308           0 :         CwtchLabel(label: AppLocalizations.of(context)!.groupInfoName),
     309           0 :         Padding(
     310           0 :           padding: EdgeInsets.all(5.0),
     311           0 :           child: CwtchButtonTextField(controller: ctrlrRemoteName, readonly: false, onPressed: _saveMetadataPressed, icon: Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn),
     312             :         ),
     313           0 :         CwtchLabel(label: AppLocalizations.of(context)!.groupInfoTopic),
     314           0 :         Padding(
     315           0 :           padding: EdgeInsets.all(5.0),
     316           0 :           child: CwtchButtonTextField(controller: ctrlrAttr1, readonly: false, onPressed: _saveMetadataPressed, icon: Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn),
     317             :         ),
     318           0 :         CwtchLabel(label: AppLocalizations.of(context)!.groupInfoExtra1),
     319           0 :         Padding(
     320           0 :           padding: EdgeInsets.all(5.0),
     321           0 :           child: CwtchButtonTextField(controller: ctrlrAttr2, readonly: false, onPressed: _saveMetadataPressed, icon: Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn),
     322             :         ),
     323           0 :         CwtchLabel(label: AppLocalizations.of(context)!.groupInfoExtra2),
     324           0 :         Padding(
     325           0 :           padding: EdgeInsets.all(5.0),
     326           0 :           child: CwtchButtonTextField(controller: ctrlrAttr3, readonly: false, onPressed: _saveMetadataPressed, icon: Icon(Icons.save), tooltip: AppLocalizations.of(context)!.saveBtn),
     327             :         ),
     328             :       ],
     329             :     );
     330             :   }
     331             : 
     332           0 :   void _saveMetadataPressed() {
     333           0 :     Provider.of<ContactInfoState>(context, listen: false).isOpPending = true;
     334           0 :     Provider.of<FlwtchState>(context, listen: false).cwtch.UpdateGroupMetadata(
     335           0 :       Provider.of<ContactInfoState>(context, listen: false).profileOnion,
     336           0 :       Provider.of<ContactInfoState>(context, listen: false).identifier,
     337           0 :       ctrlrRemoteName.text,
     338           0 :       ctrlrAttr1.text,
     339           0 :       ctrlrAttr2.text,
     340           0 :       ctrlrAttr3.text,
     341             :     );
     342             :     //final snackBar = SnackBar(content: Text(AppLocalizations.of(context)!.nickChangeSuccess));
     343             :     //ScaffoldMessenger.of(context).showSnackBar(snackBar);
     344             :   }
     345             : 
     346           0 :   Widget _buildModeBox(String mode, String mask) {
     347           0 :     return Column(
     348           0 :       children: [
     349           0 :         CwtchLabel(label: AppLocalizations.of(context)!.editGroupModeTitle + ": +" + mode),
     350           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeF, mode, "f", mask, false),
     351           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeS, mode, "s", mask, false),
     352           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeI, mode, "i", mask, false),
     353           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeO, mode, "o", mask, false),
     354           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeV, mode, "v", mask, false),
     355           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeM, mode, "m", mask, false),
     356           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeW, mode, "w", mask, false),
     357           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeE, mode, "e", mask, false),
     358           0 :         Text(AppLocalizations.of(context)!.footnoteMask),
     359             :       ],
     360             :     );
     361             :   }
     362             : 
     363           0 :   Widget _buildModeMaskBox(String mask) {
     364           0 :     return Column(
     365           0 :       children: [
     366           0 :         CwtchLabel(label: AppLocalizations.of(context)!.editGroupMaskTitle + ": +" + mask),
     367           0 :         Text(AppLocalizations.of(context)!.explainerMasks),
     368           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeF, "", "f", mask, true),
     369           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeS, "", "s", mask, true),
     370           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeI, "", "i", mask, true),
     371           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeO, "", "o", mask, true),
     372           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeV, "", "v", mask, true),
     373           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeM, "", "m", mask, true),
     374           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeW, "", "w", mask, true),
     375           0 :         _buildModeBoxEntry(AppLocalizations.of(context)!.groupModeE, "", "e", mask, true),
     376             :       ],
     377             :     );
     378             :   }
     379             : 
     380           0 :   Widget _buildModeBoxEntry(String label, String modeLine, String modeBit, String modeMask, bool isMaskBox) {
     381             :     String checkStr = isMaskBox ? modeMask : modeLine;
     382           0 :     bool pending = Provider.of<ContactInfoState>(context).isOpPending;
     383             : 
     384           0 :     return SwitchListTile(
     385           0 :       title: Text(label + (!isMaskBox && modeMask.contains(modeBit) ? "*" : ""), style: TextStyle(color: Provider.of<Settings>(context).current().mainTextColor)),
     386           0 :       subtitle: Text(_descriptionOf(modeBit)),
     387           0 :       value: checkStr.contains(modeBit),
     388           0 :       onChanged: !CanToggleMode(modeBit, isMaskBox) || pending
     389             :           ? null
     390           0 :           : (bool newVal) {
     391           0 :               _toggleModePressed(modeBit, newVal, isMaskBox);
     392             :             },
     393           0 :       activeTrackColor: Provider.of<Settings>(context).theme.defaultButtonColor,
     394           0 :       inactiveTrackColor: Provider.of<Settings>(context).theme.defaultButtonDisabledColor,
     395           0 :       secondary: _iconFor(modeBit),
     396             :     );
     397             :   }
     398             : 
     399           0 :   void _toggleModePressed(String modeBit, bool enable, bool isMaskBox) {
     400           0 :     Provider.of<ContactInfoState>(context, listen: false).isOpPending = true;
     401             :     if (isMaskBox) {
     402           0 :       Provider.of<FlwtchState>(
     403           0 :         context,
     404             :         listen: false,
     405           0 :       ).cwtch.ChangeGroupMask(Provider.of<ProfileInfoState>(context, listen: false).onion, Provider.of<ContactInfoState>(context, listen: false).identifier, (enable ? "+" : "-") + modeBit);
     406             :     } else {
     407           0 :       Provider.of<FlwtchState>(
     408           0 :         context,
     409             :         listen: false,
     410           0 :       ).cwtch.ChangeGroupMode(Provider.of<ProfileInfoState>(context, listen: false).onion, Provider.of<ContactInfoState>(context, listen: false).identifier, (enable ? "+" : "-") + modeBit);
     411             :     }
     412             :   }
     413             : 
     414           0 :   String? MyRole() {
     415           0 :     return Provider.of<ContactInfoState>(context).members.firstWhere((x) => x.onion == Provider.of<ProfileInfoState>(context, listen: false).onion).role;
     416             :   }
     417             : 
     418           0 :   bool CanUpdateMetadata() {
     419           0 :     return Provider.of<ContactInfoState>(context).isOnline() && ['admin', 'op'].contains(MyRole());
     420             :   }
     421             : 
     422           0 :   bool CanToggleMode(String modeBit, bool isMaskBox) {
     423             :     if (isMaskBox) {
     424           0 :       return Provider.of<ContactInfoState>(context).isOnline() && ['admin', 'op'].contains(MyRole());
     425             :     } else {
     426           0 :       return Provider.of<ContactInfoState>(context).isOnline() && (['admin', 'op'].contains(MyRole()) || (MyRole() == 'mod' && Provider.of<ContactInfoState>(context).modeMask.contains(modeBit)));
     427             :     }
     428             :   }
     429             : 
     430           0 :   Icon _iconFor(String modeBit) {
     431             :     switch (modeBit) {
     432           0 :       case 'f':
     433           0 :         return Icon(CwtchIcons.attach_file_24px, color: Provider.of<Settings>(context).current().mainTextColor);
     434           0 :       case 's':
     435           0 :         return Icon(Icons.hourglass_empty, color: Provider.of<Settings>(context).current().mainTextColor);
     436           0 :       case 'i':
     437           0 :         return Icon(CwtchIcons.send_invitation, color: Provider.of<Settings>(context).current().mainTextColor);
     438           0 :       case 'o':
     439           0 :         return Icon(CwtchIcons.person_add_alt_1_24px, color: Provider.of<Settings>(context).current().mainTextColor);
     440           0 :       case 'v':
     441           0 :         return Icon(CwtchIcons.member_voice, color: Provider.of<Settings>(context).current().mainTextColor);
     442           0 :       case 'm':
     443           0 :         return Icon(CwtchIcons.eye_closed, color: Provider.of<Settings>(context).current().mainTextColor);
     444           0 :       case 'w':
     445           0 :         return Icon(Icons.how_to_vote, color: Provider.of<Settings>(context).current().mainTextColor);
     446           0 :       case 'e':
     447           0 :         return Icon(CwtchIcons.swap_horiz, color: Provider.of<Settings>(context).current().mainTextColor);
     448             :       default:
     449           0 :         return Icon(CwtchIcons.block_peer, color: Provider.of<Settings>(context).current().mainTextColor);
     450             :     }
     451             :   }
     452             : 
     453           0 :   String _descriptionOf(String modeBit) {
     454             :     switch (modeBit) {
     455           0 :       case 'f':
     456           0 :         return AppLocalizations.of(context)!.groupModeFDescription;
     457           0 :       case 's':
     458           0 :         return AppLocalizations.of(context)!.groupModeSDescription;
     459           0 :       case 'i':
     460           0 :         return AppLocalizations.of(context)!.groupModeIDescription;
     461           0 :       case 'o':
     462           0 :         return AppLocalizations.of(context)!.groupModeODescription;
     463           0 :       case 'v':
     464           0 :         return AppLocalizations.of(context)!.groupModeVDescription;
     465           0 :       case 'm':
     466           0 :         return AppLocalizations.of(context)!.groupModeMDescription;
     467           0 :       case 'w':
     468           0 :         return AppLocalizations.of(context)!.groupModeWDescription;
     469           0 :       case 'e':
     470           0 :         return AppLocalizations.of(context)!.groupModeEDescription;
     471             :       default:
     472             :         return '';
     473             :     }
     474             :   }
     475             : }

Generated by: LCOV version 1.14