import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:livekit_client/livekit_client.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; import 'package:surface/providers/chat_call.dart'; import 'package:surface/types/chat.dart'; import 'package:surface/widgets/dialog.dart'; class ChatCallPrejoinPopup extends StatefulWidget { final SnChatCall ongoingCall; final SnChannel channel; final void Function() onJoin; const ChatCallPrejoinPopup({ super.key, required this.ongoingCall, required this.channel, required this.onJoin, }); @override State createState() => _ChatCallPrejoinPopupState(); } class _ChatCallPrejoinPopupState extends State { bool _isBusy = false; late final ChatCallProvider _call = context.read(); void _performJoin() async { setState(() => _isBusy = true); _call.setCall(widget.ongoingCall, widget.channel); _call.setIsBusy(true); try { final resp = await _call.getRoomToken(); final token = resp.$1; final endpoint = resp.$2; _call.initRoom(); _call.setupRoomListeners( onDisconnected: (reason) { context.showSnackbar( 'callDisconnected'.tr(args: [reason.toString()]), ); }, ); await _call.joinRoom(endpoint, token); widget.onJoin(); if (!mounted) return; Navigator.pop(context); } catch (e) { if (!mounted) return; context.showErrorDialog(e); } finally { setState(() => _isBusy = false); } } @override void initState() { final call = context.read(); call.checkPermissions().then((_) { call.initHardware(); }); super.initState(); } @override Widget build(BuildContext context) { final call = context.read(); return ListenableBuilder( listenable: call, builder: (context, _) { return Center( child: Container( constraints: const BoxConstraints(maxWidth: 320), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('callMicrophone').tr(), Switch( value: call.enableAudio, onChanged: null, ), ], ).padding(bottom: 5), DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, disabledHint: Text('callMicrophoneDisabled').tr(), hint: Text('callMicrophoneSelect').tr(), items: call.enableAudio ? call.audioInputs .map( (item) => DropdownMenuItem( value: item, child: Text(item.label), ), ) .toList() .cast>() : [], value: call.audioDevice, onChanged: (MediaDevice? value) async { if (value != null) { call.setAudioDevice(value); await call.changeLocalAudioTrack(); } }, buttonStyleData: const ButtonStyleData( height: 40, width: 320, ), ), ).padding(bottom: 25), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text('callCamera').tr(), Switch( value: call.enableVideo, onChanged: (value) => call.setEnableAudio(value), ), ], ).padding(bottom: 5), DropdownButtonHideUnderline( child: DropdownButton2( isExpanded: true, disabledHint: Text('callCameraDisabled').tr(), hint: Text('callCameraSelect').tr(), items: call.enableVideo ? call.videoInputs .map( (item) => DropdownMenuItem( value: item, child: Text(item.label), ), ) .toList() .cast>() : [], value: call.videoDevice, onChanged: (MediaDevice? value) async { if (value != null) { call.setVideoDevice(value); await call.changeLocalVideoTrack(); } }, buttonStyleData: const ButtonStyleData( height: 40, width: 320, ), ), ).padding(bottom: 25), if (_isBusy) const Center(child: CircularProgressIndicator()) else ElevatedButton( style: ElevatedButton.styleFrom( minimumSize: const Size(320, 56), ), onPressed: _isBusy ? null : _performJoin, child: Text('callJoin').tr(), ), ], ), ), ); }, ); } @override void dispose() { _call ..deactivateHardware() ..disposeHardware(); super.dispose(); } }