mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2025-04-22 18:07:54 +02:00
214 lines
6.3 KiB
Dart
214 lines
6.3 KiB
Dart
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<GroupCallView> createState() => _GroupCallViewState();
|
|
}
|
|
|
|
class _GroupCallViewState extends State<GroupCallView> {
|
|
WrappedMediaStream? get primaryStream => widget.call.primaryStream;
|
|
|
|
List<WrappedMediaStream> get screenSharingStreams =>
|
|
widget.call.screenSharingStreams;
|
|
List<WrappedMediaStream> 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<WrappedMediaStream> userMediaStreams;
|
|
const CallGrid(
|
|
{Key? key,
|
|
required this.call,
|
|
required this.client,
|
|
required this.screenSharing,
|
|
required this.userMediaStreams})
|
|
: super(key: key);
|
|
|
|
@override
|
|
State<CallGrid> createState() => _CallGridState();
|
|
}
|
|
|
|
class _CallGridState extends State<CallGrid> {
|
|
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<OrderUpdateEntity> 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,
|
|
);
|
|
}
|
|
}
|