import 'package:fluffychat/utils/voip/group_call_state.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_webrtc/flutter_webrtc.dart'; import 'package:matrix/matrix.dart'; class GroupCallView extends StatefulWidget { final GroupCallSessionState call; const GroupCallView({ Key? key, required this.call, }) : super(key: key); @override State createState() => _GroupCallViewState(); } class _GroupCallViewState extends State { WrappedMediaStream? get primaryStream => widget.call.primaryStream; List get screenSharingStreams => widget.call.screenSharingStreams; List get userMediaStreams => widget.call.userMediaStreams; WrappedMediaStream? get primaryScreenShare => widget.call.screenSharingStreams.first; void updateStreams() { Logs().i('Group calls, updating streams'); widget.call.groupCall.onStreamAdd.stream.listen((event) { if (event.purpose == SDPStreamMetadataPurpose.Usermedia) { if (userMediaStreams.indexWhere((element) => element == event) == -1) { setState(() { userMediaStreams.add(event); }); } } else if (event.purpose == SDPStreamMetadataPurpose.Screenshare) { if (screenSharingStreams.indexWhere((element) => element == event) == -1) { setState(() { screenSharingStreams.add(event); }); } } }); widget.call.groupCall.onStreamRemoved.stream.listen((event) { if (event.purpose == SDPStreamMetadataPurpose.Usermedia) { userMediaStreams .removeWhere((element) => element.stream!.id == event.stream!.id); setState(() { userMediaStreams.remove(event); }); } else if (event.purpose == SDPStreamMetadataPurpose.Screenshare) { screenSharingStreams .removeWhere((element) => element.stream!.id == event.stream!.id); setState(() { screenSharingStreams.remove(event); }); } }); } @override void initState() { updateStreams(); super.initState(); } @override Widget build(BuildContext context) { Logs().e('Group call state: ' + widget.call.groupCall.state); Logs().e('Group call state: ' + widget.call.groupCall.participants.length.toString()); if (screenSharingStreams.isNotEmpty) { return Center( child: ListView( children: [ RTCVideoView( primaryScreenShare!.renderer as RTCVideoRenderer, mirror: primaryScreenShare!.isLocal(), objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, ), const SizedBox( height: 100, ), CallGrid( call: widget.call, screenSharing: true, userMediaStreams: userMediaStreams, ), ], ), ); } else { // No one is screen sharing, show avatars and user streams here return Center( child: CallGrid( call: widget.call, screenSharing: false, userMediaStreams: userMediaStreams, ), ); } } } class CallGrid extends StatefulWidget { final GroupCallSessionState call; final bool screenSharing; final List userMediaStreams; const CallGrid( {Key? key, required this.call, required this.screenSharing, required this.userMediaStreams}) : super(key: key); @override State createState() => _CallGridState(); } class _CallGridState extends State { @override Widget build(BuildContext context) { final client = Matrix.of(context).client; return GridView.builder( itemCount: widget.call.groupCall.participants.length, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: widget.screenSharing ? 4 : 2), itemBuilder: (context, index) { final participant = widget.call.groupCall.participants[index]; Logs().e('Group calls participants - ' + participant.displayName.toString()); if (widget.userMediaStreams .map((stream) => stream.userId) .contains(participant.id)) { return Container( color: Colors.amber, child: RTCVideoView( widget.userMediaStreams .firstWhere((element) => element.userId == participant.id) .renderer as RTCVideoRenderer, mirror: false, objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain, ), ); } else { return Center( child: Container( height: 200, width: 200, color: Colors.red, child: Avatar( mxContent: participant.avatarUrl, name: participant.displayName, size: 24, client: client, ), ), ); } }); } }