@@ -1031,6 +1031,66 @@ Future<void> _uploadFiles({
10311031  }
10321032}
10331033
1034+ class  _AttachVideoChatUrlButton  extends  StatelessWidget  {
1035+   const  _AttachVideoChatUrlButton ({
1036+     required  this .controller,
1037+     required  this .enabled,
1038+   });
1039+ 
1040+   final  ComposeBoxController  controller;
1041+   final  bool  enabled;
1042+ 
1043+   static  const  int  jitsi =  1 ;
1044+   static  const  int  zoom =  2 ;
1045+   static  const  int  googleMeet =  5 ;
1046+ 
1047+   String  _generateJitsiUrl (String  serverUrl, String  linkText) {
1048+     final  id =  List .generate (15 , (_) =>  Random .secure ().nextInt (10 )).join ();
1049+     return  '[$linkText ]($serverUrl /$id #config.startWithVideoMuted=false)\n ' ;
1050+   }
1051+ 
1052+   String ?  _getMeetingUrl (BuildContext  context, int ?  provider, String ?  jitsiServerUrl) {
1053+     if  (jitsiServerUrl ==  null ) return  null ;
1054+ 
1055+     final  linkText =  ZulipLocalizations .of (context).composeBoxAttachFromVideoCallTooltip;
1056+ 
1057+     switch  (provider) {
1058+       case  jitsi:  return  _generateJitsiUrl (jitsiServerUrl, linkText);
1059+       case  zoom:  return  'https://zoom.us/start/meeting' ;
1060+       case  googleMeet:  return  'https://meet.google.com/new' ;
1061+       default :  return  null ;
1062+     }
1063+   }
1064+ 
1065+   void  _handlePress (BuildContext  context) {
1066+     final  store =  PerAccountStoreWidget .of (context);
1067+     final  url =  _getMeetingUrl (context, store.realmVideoChatProvider, store.realmJitsiServerUrl);
1068+ 
1069+     if  (url ==  null ) return ;
1070+ 
1071+     final  text =  controller.content.text;
1072+     final  selection =  controller.content.selection;
1073+ 
1074+     controller
1075+       ..content.text =  text.replaceRange (selection.start, selection.end, url)
1076+       ..content.selection =  TextSelection .collapsed (
1077+         offset:  selection.start +  url.length);
1078+   }
1079+ 
1080+   @override 
1081+   Widget  build (BuildContext  context) {
1082+     final  designVariables =  DesignVariables .of (context);
1083+     final  zulipLocalizations =  ZulipLocalizations .of (context);
1084+ 
1085+     return  SizedBox (
1086+       width:  _composeButtonSize,
1087+       child:  IconButton (
1088+         icon:  Icon (ZulipIcons .video, color:  designVariables.foreground.withFadedAlpha (0.5 )),
1089+         tooltip:  zulipLocalizations.composeBoxAttachFromVideoCallTooltip,
1090+         onPressed:  enabled ?  () =>  _handlePress (context) :  null ));
1091+   }
1092+ }
1093+ 
10341094abstract  class  _AttachUploadsButton  extends  StatelessWidget  {
10351095  const  _AttachUploadsButton ({required  this .controller, required  this .enabled});
10361096
@@ -1469,6 +1529,7 @@ abstract class _ComposeBoxBody extends StatelessWidget {
14691529      _AttachFileButton (controller:  controller, enabled:  composeButtonsEnabled),
14701530      _AttachMediaButton (controller:  controller, enabled:  composeButtonsEnabled),
14711531      _AttachFromCameraButton (controller:  controller, enabled:  composeButtonsEnabled),
1532+       _AttachVideoChatUrlButton (controller:  controller, enabled:  composeButtonsEnabled),
14721533    ];
14731534
14741535    final  topicInput =  buildTopicInput ();
0 commit comments