Solian/lib/widgets/chat/call/participant.dart

198 lines
5.5 KiB
Dart
Raw Normal View History

2024-04-27 16:07:32 +00:00
import 'dart:convert';
2024-04-27 05:12:26 +00:00
import 'package:flutter/material.dart';
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
2024-04-27 16:07:32 +00:00
import 'package:solian/models/account.dart';
2024-04-27 05:12:26 +00:00
import 'package:solian/models/call.dart';
2024-04-27 16:07:32 +00:00
import 'package:solian/widgets/chat/call/no_content.dart';
2024-04-27 05:12:26 +00:00
import 'package:solian/widgets/chat/call/participant_info.dart';
import 'package:solian/widgets/chat/call/participant_stats.dart';
abstract class ParticipantWidget extends StatefulWidget {
2024-04-29 12:22:06 +00:00
static ParticipantWidget widgetFor(ParticipantTrack participantTrack,
{bool isFixed = false, bool showStatsLayer = false}) {
2024-04-27 05:12:26 +00:00
if (participantTrack.participant is LocalParticipant) {
2024-04-29 12:22:06 +00:00
return LocalParticipantWidget(
participantTrack.participant as LocalParticipant,
participantTrack.videoTrack,
isFixed,
participantTrack.isScreenShare,
showStatsLayer,
);
2024-04-27 05:12:26 +00:00
} else if (participantTrack.participant is RemoteParticipant) {
2024-04-29 12:22:06 +00:00
return RemoteParticipantWidget(
participantTrack.participant as RemoteParticipant,
participantTrack.videoTrack,
isFixed,
participantTrack.isScreenShare,
showStatsLayer,
);
2024-04-27 05:12:26 +00:00
}
throw UnimplementedError('Unknown participant type');
}
abstract final Participant participant;
abstract final VideoTrack? videoTrack;
abstract final bool isScreenShare;
2024-04-29 12:22:06 +00:00
abstract final bool isFixed;
2024-04-27 05:12:26 +00:00
abstract final bool showStatsLayer;
final VideoQuality quality;
const ParticipantWidget({
super.key,
this.quality = VideoQuality.MEDIUM,
});
}
class LocalParticipantWidget extends ParticipantWidget {
@override
final LocalParticipant participant;
@override
final VideoTrack? videoTrack;
@override
2024-04-29 12:22:06 +00:00
final bool isFixed;
@override
2024-04-27 05:12:26 +00:00
final bool isScreenShare;
@override
final bool showStatsLayer;
const LocalParticipantWidget(
this.participant,
this.videoTrack,
2024-04-29 12:22:06 +00:00
this.isFixed,
2024-04-27 05:12:26 +00:00
this.isScreenShare,
this.showStatsLayer, {
super.key,
});
@override
State<StatefulWidget> createState() => _LocalParticipantWidgetState();
}
class RemoteParticipantWidget extends ParticipantWidget {
@override
final RemoteParticipant participant;
@override
final VideoTrack? videoTrack;
@override
2024-04-29 12:22:06 +00:00
final bool isFixed;
@override
2024-04-27 05:12:26 +00:00
final bool isScreenShare;
@override
final bool showStatsLayer;
const RemoteParticipantWidget(
this.participant,
this.videoTrack,
2024-04-29 12:22:06 +00:00
this.isFixed,
2024-04-27 05:12:26 +00:00
this.isScreenShare,
this.showStatsLayer, {
super.key,
});
@override
State<StatefulWidget> createState() => _RemoteParticipantWidgetState();
}
2024-05-01 09:37:34 +00:00
abstract class _ParticipantWidgetState<T extends ParticipantWidget>
extends State<T> {
2024-04-27 05:12:26 +00:00
VideoTrack? get _activeVideoTrack;
TrackPublication? get _firstAudioPublication;
2024-04-27 16:07:32 +00:00
Account? _userinfoMetadata;
2024-04-27 05:12:26 +00:00
@override
void initState() {
super.initState();
widget.participant.addListener(onParticipantChanged);
onParticipantChanged();
}
@override
void dispose() {
widget.participant.removeListener(onParticipantChanged);
super.dispose();
}
@override
void didUpdateWidget(covariant T oldWidget) {
oldWidget.participant.removeListener(onParticipantChanged);
widget.participant.addListener(onParticipantChanged);
onParticipantChanged();
super.didUpdateWidget(oldWidget);
}
2024-04-27 16:07:32 +00:00
void onParticipantChanged() {
setState(() {
if (widget.participant.metadata != null) {
2024-05-01 09:37:34 +00:00
_userinfoMetadata =
Account.fromJson(jsonDecode(widget.participant.metadata!));
2024-04-27 16:07:32 +00:00
}
});
}
2024-04-27 05:12:26 +00:00
@override
2024-04-27 16:07:32 +00:00
Widget build(BuildContext ctx) {
2024-04-27 16:10:20 +00:00
return Stack(
children: [
_activeVideoTrack != null && !_activeVideoTrack!.muted
? VideoTrackRenderer(
_activeVideoTrack!,
fit: RTCVideoViewObjectFit.RTCVideoViewObjectFitContain,
)
: NoContentWidget(
userinfo: _userinfoMetadata,
2024-04-29 12:22:06 +00:00
isFixed: widget.isFixed,
isSpeaking: widget.participant.isSpeaking,
),
2024-04-27 16:10:20 +00:00
if (widget.showStatsLayer)
Positioned(
top: 30,
right: 30,
2024-04-29 12:22:06 +00:00
child: ParticipantStatsWidget(participant: widget.participant),
2024-04-27 16:07:32 +00:00
),
2024-04-27 16:10:20 +00:00
Align(
alignment: Alignment.bottomCenter,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: [
ParticipantInfoWidget(
2024-05-01 09:37:34 +00:00
title: widget.participant.name.isNotEmpty
? widget.participant.name
: widget.participant.identity,
audioAvailable: _firstAudioPublication?.muted == false &&
_firstAudioPublication?.subscribed == true,
2024-04-27 16:10:20 +00:00
connectionQuality: widget.participant.connectionQuality,
isScreenShare: widget.isScreenShare,
),
],
),
),
],
2024-04-27 16:07:32 +00:00
);
}
2024-04-27 05:12:26 +00:00
}
2024-05-01 09:37:34 +00:00
class _LocalParticipantWidgetState
extends _ParticipantWidgetState<LocalParticipantWidget> {
2024-04-27 05:12:26 +00:00
@override
LocalTrackPublication<LocalAudioTrack>? get _firstAudioPublication =>
widget.participant.audioTrackPublications.firstOrNull;
@override
VideoTrack? get _activeVideoTrack => widget.videoTrack;
}
2024-05-01 09:37:34 +00:00
class _RemoteParticipantWidgetState
extends _ParticipantWidgetState<RemoteParticipantWidget> {
2024-04-27 05:12:26 +00:00
@override
RemoteTrackPublication<RemoteAudioTrack>? get _firstAudioPublication =>
widget.participant.audioTrackPublications.firstOrNull;
@override
VideoTrack? get _activeVideoTrack => widget.videoTrack;
}