feat(vide_calls): gms calls handling

This commit is contained in:
misha
2026-05-25 08:07:20 +03:00
parent 1acb158f36
commit 73cec1a01e
35 changed files with 2288 additions and 618 deletions

View File

@@ -140,26 +140,34 @@ class VideoCallConnectingEntranceScreen extends StatelessWidget {
const SizedBox(height: _topContentSpacing),
_avatar(context, counterpartDisplayName, counterpartAvatarUrl),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: appTheme.greenA700,
shape: BoxShape.circle,
if (canEnterCall)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: 8,
height: 8,
decoration: BoxDecoration(
color: appTheme.greenA700,
shape: BoxShape.circle,
),
),
),
const SizedBox(width: 8),
Text(
s.video_call_status_online,
style: CustomTextStyles.bodySmallMulishBlack900
.copyWith(height: 14 / 12),
),
],
),
const SizedBox(height: 20),
const SizedBox(width: 8),
Text(
s.video_call_status_online,
style: CustomTextStyles.bodySmallMulishBlack900
.copyWith(height: 14 / 12),
),
],
)
else
Text(
s.video_call_screen_connecting_subtitle,
textAlign: TextAlign.center,
style: CustomTextStyles.bodySmallMulishBlack900
.copyWith(height: 14 / 12),
),
SizedBox(height: canEnterCall ? 20 : 16),
Text(
s.video_call_screen_connecting_title,
textAlign: TextAlign.center,

View File

@@ -1,6 +1,8 @@
import 'dart:async';
import 'dart:math' as math;
import 'package:eatvio_app/core/app_router.dart';
import 'package:eatvio_app/core/locator_service.dart';
import 'package:eatvio_app/features/video_calls/presentation/bloc/video_call_session/video_call_session_cubit.dart';
import 'package:eatvio_app/features/video_calls/presentation/bloc/video_call_session/video_call_session_state.dart';
import 'package:eatvio_app/features/video_calls/presentation/pages/video_call_screen.dart';
@@ -41,6 +43,7 @@ class _VideoCallGlobalPipLayerState extends State<VideoCallGlobalPipLayer> {
void _openFullscreenRoute(VideoCallSessionState s) {
final ctx = AppRouter.navigatorKey.currentContext;
if (ctx == null) return;
unawaited(sl<VideoCallSessionCubit>().restoreFullscreenFromPip());
GoRouter.of(ctx).pushNamed(
VideoCallScreen.name,
extra: <String, dynamic>{
@@ -71,6 +74,7 @@ class _VideoCallGlobalPipLayerState extends State<VideoCallGlobalPipLayer> {
@override
Widget build(BuildContext context) {
return BlocBuilder<VideoCallSessionCubit, VideoCallSessionState>(
bloc: sl<VideoCallSessionCubit>(),
buildWhen: (a, b) =>
a.uiMode != b.uiMode ||
a.connectPhase != b.connectPhase ||

View File

@@ -8,6 +8,11 @@ abstract final class VideoCallLiveKitTrackUtils {
static lk.VideoTrack? visibleVideoFromPublication(lk.TrackPublication? pub) {
if (pub == null || pub.muted) return null;
if (pub is lk.RemoteTrackPublication) {
if (!pub.subscribed || pub.streamState != lk.StreamState.active) {
return null;
}
}
final v = asVideoTrack(pub.track);
if (v == null || v.muted) return null;
return v;

View File

@@ -24,11 +24,14 @@ class VideoCallRemoteVideoStage extends StatelessWidget {
if (remote != null) {
return lk.VideoTrackRenderer(remote, fit: lk.VideoViewFit.cover);
}
final showConnectError =
connectError != null &&
room.connectionState != lk.ConnectionState.connected;
return Stack(
fit: StackFit.expand,
children: [
const VideoCallNoVideoBackground(),
if (connectError != null)
if (showConnectError)
Center(
child: Padding(
padding: const EdgeInsets.all(24),

View File

@@ -1,6 +1,9 @@
import 'dart:math' as math;
import 'package:eatvio_app/core/theme/theme_helper.dart';
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_controls.dart';
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_top_overlay.dart';
import 'package:eatvio_app/generated/l10n.dart';
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_livekit_track_utils.dart';
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_name_chips.dart';
import 'package:eatvio_app/features/video_calls/presentation/widgets/video_call_no_video_background.dart';
@@ -25,6 +28,7 @@ class VideoCallStageLayout extends StatefulWidget {
required this.localDisplayName,
required this.remoteDisplayName,
required this.micEnabled,
this.systemAudioInterrupted = false,
required this.videoEnabled,
required this.splitParticipantView,
required this.mainStageShowsLocalVideo,
@@ -49,6 +53,7 @@ class VideoCallStageLayout extends StatefulWidget {
final String localDisplayName;
final String remoteDisplayName;
final bool micEnabled;
final bool systemAudioInterrupted;
final bool videoEnabled;
final bool splitParticipantView;
final bool mainStageShowsLocalVideo;
@@ -161,10 +166,23 @@ class _VideoCallStageLayoutState extends State<VideoCallStageLayout> {
),
..._bottomBarGradient(),
..._bottomControlsRow(),
..._systemInterruptionBanner(),
],
);
}
List<Widget> _systemInterruptionBanner() {
if (!widget.systemAudioInterrupted) return const [];
return [
Positioned(
left: 16,
right: 16,
top: widget.topInset + 8,
child: const _VideoCallSystemInterruptionBanner(),
),
];
}
Widget _buildFullscreenLayout(BoxConstraints constraints) {
final maxW = constraints.maxWidth;
final maxH = constraints.maxHeight;
@@ -203,6 +221,7 @@ class _VideoCallStageLayoutState extends State<VideoCallStageLayout> {
),
..._bottomBarGradient(),
..._bottomControlsRow(),
..._systemInterruptionBanner(),
Positioned(
top: widget.topInset + _edgeInset + _shrinkExpandExtraTop,
left: _controlsHorizontalInset,
@@ -487,3 +506,57 @@ class _VideoCallPipLocalContent extends StatelessWidget {
);
}
}
class _VideoCallSystemInterruptionBanner extends StatelessWidget {
const _VideoCallSystemInterruptionBanner();
@override
Widget build(BuildContext context) {
final s = S.of(context);
return Material(
color: appTheme.videoCallNoticeBannerBackground,
borderRadius: BorderRadius.circular(12),
elevation: 6,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SvgPicture.asset(
VideoCallTopOverlay.warningSvg,
width: 22,
height: 22,
),
const SizedBox(width: 10),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
s.video_call_mic_muted_phone_call,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
height: 1.25,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 4),
Text(
s.video_call_system_interruption,
style: const TextStyle(
color: Colors.white,
fontSize: 13,
height: 1.25,
fontWeight: FontWeight.w400,
),
),
],
),
),
],
),
),
);
}
}