import 'package:flutter/material.dart'; import 'package:collection/collection.dart'; import 'package:flutter_reorderable_grid_view/entities/order_update_entity.dart'; import 'package:flutter_reorderable_grid_view/widgets/widgets.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/pages/voip/utils/group_call_session_state.dart'; import 'package:fluffychat/pages/voip/utils/stream_view.dart'; import 'dialer/dialer.dart'; class GroupCallView extends StatefulWidget { final GroupCallSessionState call; final Client client; const GroupCallView({ Key? key, required this.call, required this.client, }) : 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) { if (mounted) { setState(() { userMediaStreams.add(event); }); } } } else if (event.purpose == SDPStreamMetadataPurpose.Screenshare) { if (screenSharingStreams.indexWhere((element) => element == event) == -1) { if (mounted) { 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); if (mounted) { setState(() { userMediaStreams.remove(event); }); } } else if (event.purpose == SDPStreamMetadataPurpose.Screenshare) { screenSharingStreams .removeWhere((element) => element.stream!.id == event.stream!.id); if (mounted) { setState(() { screenSharingStreams.remove(event); }); } } }); } @override void initState() { updateStreams(); super.initState(); } @override Widget build(BuildContext context) { if (screenSharingStreams.isNotEmpty) { return Center( child: Stack( children: [ StreamView( screenSharingStreams.first, matrixClient: widget.client, ), Positioned( bottom: 150, child: SizedBox( // height: 400, width: MediaQuery.of(context).size.width, child: CallGrid( call: widget.call, screenSharing: true, userMediaStreams: userMediaStreams, client: widget.client, )), ), ], ), ); } else { // No one is screen sharing, show avatars and user streams here return Center( child: CallGrid( call: widget.call, screenSharing: false, userMediaStreams: userMediaStreams, client: widget.client, ), ); } } } class CallGrid extends StatefulWidget { final GroupCallSessionState call; final Client client; final bool screenSharing; final List userMediaStreams; const CallGrid( {Key? key, required this.call, required this.client, required this.screenSharing, required this.userMediaStreams}) : super(key: key); @override State createState() => _CallGridState(); } class _CallGridState extends State { int axisCount() { var orientation = MediaQuery.of(context).orientation; if (MediaQuery.of(context).size.width >= 600) { orientation = Orientation.landscape; } if (widget.screenSharing) { return orientation == Orientation.portrait ? 4 : 6; } if (widget.call.groupCall.participants.length > 2 || MediaQuery.of(context).size.width >= 600) { return orientation == Orientation.portrait ? 2 : 4; } else { return 1; } } final _scrollController = ScrollController(); final _gridViewKey = GlobalKey(); @override Widget build(BuildContext context) { final participants = widget.call.groupCall.participants; Logs().w(participants .map((e) => widget.userMediaStreams .firstWhereOrNull((element) => element.userId == e.id)) .toString()); // adding a user to a call sometimes results in multiple userMediaStreams // for the saem user, Just pick the latest one then. final generatedChildren = participants .map( (participant) => widget.userMediaStreams .lastWhereOrNull((stream) => stream.userId == participant.id), ) .where((element) => element != null) .map( (userMediaStream) => StreamView( userMediaStream!, key: Key(userMediaStream.userId), matrixClient: widget.client, ), ) .toList(); if (widget.screenSharing && MediaQuery.of(context).orientation == Orientation.landscape) { return Container(); } return ReorderableBuilder( scrollController: _scrollController, onReorder: (List orderUpdateEntities) { for (final orderUpdateEntity in orderUpdateEntities) { final reorderParticipant = participants.removeAt(orderUpdateEntity.oldIndex); participants.insert(orderUpdateEntity.newIndex, reorderParticipant); } }, builder: (children) { return GridView( shrinkWrap: true, key: _gridViewKey, controller: _scrollController, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: axisCount(), ), children: children, ); }, children: generatedChildren, ); } }