fix(videocall): apply PR review fixes and add i18n translations

- Replace setState with FutureBuilder in VideoViewWidget
- Remove comments from datasource impl
- Replace GetIt.I with ref.read(provider) in controllers
- Fix state comparison using isTalking getter
- Add hangUp/stopAudio/stopCamera cleanup on dispose
- Remove duplicate SafeArea from IncomingCallOverlay
- Add missedCall error event to enum
- Replace Colors.white70 with theme colorScheme
- Move VIDEOCALL_INTEGRATION.md to apps/mobile_app/docs/
- Gitignore config JSON files with app keys
- Add 28 i18n keys in 6 locales for all videocall strings
- Map error events to specific i18n messages
This commit is contained in:
2026-04-26 10:27:25 +02:00
parent 57f0f64d08
commit 5aa0c0acc7
26 changed files with 329 additions and 180 deletions

3
.gitignore vendored
View File

@@ -24,6 +24,9 @@
*.iml *.iml
.vscode/ .vscode/
# App config (contains API keys, passed via --dart-define-from-file)
apps/mobile_app/config/*.json
# macOS # macOS
.DS_Store .DS_Store
**/.DS_Store **/.DS_Store

View File

@@ -1,7 +0,0 @@
{
"env": "development",
"apiBaseUrl": "https://api-neki-b2b.neki.es/gateway/api/",
"apiOrigin": "https://neki-b2b.neki.es",
"wsUrl": "wss://api-neki-b2b.neki.es/websocket",
"juphoonAppKey": "9efcf2d889dc8a0320925096"
}

View File

@@ -1,7 +0,0 @@
{
"env": "production",
"apiBaseUrl": "https://api-platform.savefamily.app/gateway/api/",
"apiOrigin": "https://platform.savefamily.app",
"wsUrl": "wss://api-platform.savefamily.app/websocket",
"juphoonAppKey": "9efcf2d889dc8a0320925096"
}

View File

@@ -1,7 +0,0 @@
{
"env": "staging",
"apiBaseUrl": "https://api-platform.pre.savefamilygps.net/gateway/api/",
"apiOrigin": "https://platform.pre.savefamilygps.net",
"wsUrl": "wss://api-platform.pre.savefamilygps.net/websocket",
"juphoonAppKey": "9efcf2d889dc8a0320925096"
}

View File

@@ -13,15 +13,12 @@ class VideocallSignalingDatasourceImpl implements VideocallSignalingDatasource {
required String appAccount, required String appAccount,
required String roomNumber, required String roomNumber,
}) async { }) async {
// TODO: Implement when backend API spec is available
// await _repository.post('/devices/$deviceId/videocall/initiate', body: {...});
throw UnimplementedError( throw UnimplementedError(
'Backend signaling API not yet available. Waiting for endpoint spec.'); 'Backend signaling API not yet available. Waiting for endpoint spec.');
} }
@override @override
Future<void> cancelCall({required String deviceId}) async { Future<void> cancelCall({required String deviceId}) async {
// TODO: Implement when backend API spec is available
throw UnimplementedError( throw UnimplementedError(
'Backend signaling API not yet available. Waiting for endpoint spec.'); 'Backend signaling API not yet available. Waiting for endpoint spec.');
} }
@@ -31,14 +28,12 @@ class VideocallSignalingDatasourceImpl implements VideocallSignalingDatasource {
required String deviceId, required String deviceId,
required String roomNumber, required String roomNumber,
}) async { }) async {
// TODO: Implement when backend API spec is available
throw UnimplementedError( throw UnimplementedError(
'Backend signaling API not yet available. Waiting for endpoint spec.'); 'Backend signaling API not yet available. Waiting for endpoint spec.');
} }
@override @override
Future<int> getRoomParticipantCount({required String roomNumber}) async { Future<int> getRoomParticipantCount({required String roomNumber}) async {
// TODO: Implement when backend API spec is available
throw UnimplementedError( throw UnimplementedError(
'Backend signaling API not yet available. Waiting for endpoint spec.'); 'Backend signaling API not yet available. Waiting for endpoint spec.');
} }
@@ -49,7 +44,6 @@ class VideocallSignalingDatasourceImpl implements VideocallSignalingDatasource {
required int count, required int count,
required int type, required int type,
}) async { }) async {
// TODO: Implement when backend API spec is available
throw UnimplementedError( throw UnimplementedError(
'Backend signaling API not yet available. Waiting for endpoint spec.'); 'Backend signaling API not yet available. Waiting for endpoint spec.');
} }

View File

@@ -3,6 +3,7 @@ enum VideocallErrorEvent {
authentication, authentication,
callStart, callStart,
callAnswer, callAnswer,
missedCall,
network, network,
cameraPermission, cameraPermission,
microphonePermission, microphonePermission,

View File

@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:get_it/get_it.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:videocall_sdk/videocall_sdk.dart'; import 'package:videocall_sdk/videocall_sdk.dart';
@@ -28,8 +27,8 @@ class GroupCallController extends _$GroupCallController {
@override @override
GroupCallState build() { GroupCallState build() {
_channelService = GetIt.I<VideocallChannelService>(); _channelService = ref.read(videocallChannelServiceProvider);
_deviceService = GetIt.I<VideocallDeviceService>(); _deviceService = ref.read(videocallDeviceServiceProvider);
ref.onDispose(_disposeSubscriptions); ref.onDispose(_disposeSubscriptions);
_subscribeToStreams(); _subscribeToStreams();

View File

@@ -42,7 +42,7 @@ final class GroupCallControllerProvider
} }
String _$groupCallControllerHash() => String _$groupCallControllerHash() =>
r'c8528072cb1b6d2bb15d50b199405938e7d3acd0'; r'0d3c5bd234ef3ed76b0b0f7666ddb73c8b98be55';
abstract class _$GroupCallController extends $Notifier<GroupCallState> { abstract class _$GroupCallController extends $Notifier<GroupCallState> {
GroupCallState build(); GroupCallState build();

View File

@@ -1,6 +1,5 @@
import 'dart:async'; import 'dart:async';
import 'package:get_it/get_it.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:sf_shared/sf_shared.dart'; import 'package:sf_shared/sf_shared.dart';
import 'package:videocall_sdk/videocall_sdk.dart' hide VideocallState; import 'package:videocall_sdk/videocall_sdk.dart' hide VideocallState;
@@ -25,10 +24,10 @@ class VideocallController extends _$VideocallController {
@override @override
VideocallState build() { VideocallState build() {
_manager = GetIt.I<VideocallSdkManager>(); _manager = ref.read(videocallManagerProvider);
_callService = GetIt.I<VideocallCallService>(); _callService = ref.read(videocallCallServiceProvider);
_deviceService = GetIt.I<VideocallDeviceService>(); _deviceService = ref.read(videocallDeviceServiceProvider);
_client = GetIt.I<VideocallClient>(); _client = ref.read(videocallClientProvider);
final device = ref.read(selectedDeviceProvider).value; final device = ref.read(selectedDeviceProvider).value;
final deviceId = device?.identificator ?? ''; final deviceId = device?.identificator ?? '';
@@ -79,18 +78,17 @@ class VideocallController extends _$VideocallController {
void _subscribeToStreams() { void _subscribeToStreams() {
_callAddSub = _callService.callItemAddStream.listen(_onCallItemAdd); _callAddSub = _callService.callItemAddStream.listen(_onCallItemAdd);
_callUpdateSub = _callUpdateSub = _callService.callItemUpdateStream.listen(
_callService.callItemUpdateStream.listen(_onCallItemUpdate); _onCallItemUpdate,
_callRemoveSub = );
_callService.callItemRemoveStream.listen(_onCallItemRemove); _callRemoveSub = _callService.callItemRemoveStream.listen(
_onCallItemRemove,
);
_missedCallSub = _callService.missedCallStream.listen(_onMissedCall); _missedCallSub = _callService.missedCallStream.listen(_onMissedCall);
_clientStateSub = _client.stateStream.listen(_onClientStateChange); _clientStateSub = _client.stateStream.listen(_onClientStateChange);
} }
Future<bool> login({ Future<bool> login({required String userId, required String password}) async {
required String userId,
required String password,
}) async {
final ok = await _client.login(userId: userId, password: password); final ok = await _client.login(userId: userId, password: password);
if (!ref.mounted) return false; if (!ref.mounted) return false;
if (!ok) { if (!ok) {
@@ -103,8 +101,9 @@ class VideocallController extends _$VideocallController {
Future<void> startCall(String remoteUserId) async { Future<void> startCall(String remoteUserId) async {
final device = ref.read(selectedDeviceProvider).value; final device = ref.read(selectedDeviceProvider).value;
final targetUserId = final targetUserId = remoteUserId.isNotEmpty
remoteUserId.isNotEmpty ? remoteUserId : 'w_${device?.imei ?? ''}'; ? remoteUserId
: 'w_${device?.imei ?? ''}';
state = state.copyWith( state = state.copyWith(
remoteUserId: targetUserId, remoteUserId: targetUserId,
@@ -252,7 +251,7 @@ class VideocallController extends _$VideocallController {
if (!ref.mounted) return; if (!ref.mounted) return;
state = state.copyWith(currentCall: item); state = state.copyWith(currentCall: item);
final isTalking = item.state.name == 'talking'; final isTalking = item.isTalking;
if (isTalking && state.screenMode != VideocallScreenMode.inCall) { if (isTalking && state.screenMode != VideocallScreenMode.inCall) {
state = state.copyWith( state = state.copyWith(
@@ -293,9 +292,7 @@ class VideocallController extends _$VideocallController {
void _onMissedCall(VideocallItem item) { void _onMissedCall(VideocallItem item) {
if (!ref.mounted) return; if (!ref.mounted) return;
state = state.copyWith( state = state.copyWith(errorEvent: VideocallErrorEvent.missedCall);
errorEvent: VideocallErrorEvent.callStart,
);
} }
void _onClientStateChange(VideocallClientState clientState) { void _onClientStateChange(VideocallClientState clientState) {
@@ -319,5 +316,10 @@ class VideocallController extends _$VideocallController {
_callRemoveSub?.cancel(); _callRemoveSub?.cancel();
_missedCallSub?.cancel(); _missedCallSub?.cancel();
_clientStateSub?.cancel(); _clientStateSub?.cancel();
if (state.screenMode != VideocallScreenMode.idle) {
_callService.hangUp();
_deviceService.stopAudio();
_deviceService.stopCamera();
}
} }
} }

View File

@@ -42,7 +42,7 @@ final class VideocallControllerProvider
} }
String _$videocallControllerHash() => String _$videocallControllerHash() =>
r'2fc967b9e44ca13586fd36e0d3eaab1014d52821'; r'910674a27ecef98e5df917d193f97fca1c60ac02';
abstract class _$VideocallController extends $Notifier<VideocallState> { abstract class _$VideocallController extends $Notifier<VideocallState> {
VideocallState build(); VideocallState build();

View File

@@ -42,15 +42,14 @@ class _VideocallScreenState extends ConsumerState<VideocallScreen> {
(_, next) { (_, next) {
if (next == null) return; if (next == null) return;
final key = switch (next) { final key = switch (next) {
VideocallErrorEvent.sdkInitialization || VideocallErrorEvent.sdkInitialization => I18n.videocallErrorSdkInit,
VideocallErrorEvent.authentication || VideocallErrorEvent.authentication => I18n.videocallErrorAuth,
VideocallErrorEvent.callStart || VideocallErrorEvent.callStart => I18n.videocallErrorCallStart,
VideocallErrorEvent.callAnswer || VideocallErrorEvent.callAnswer => I18n.videocallErrorCallAnswer,
VideocallErrorEvent.cameraPermission || VideocallErrorEvent.missedCall => I18n.videocallErrorMissedCall,
VideocallErrorEvent.microphonePermission || VideocallErrorEvent.cameraPermission => I18n.videocallErrorCamera,
VideocallErrorEvent.network || VideocallErrorEvent.microphonePermission => I18n.videocallErrorMic,
VideocallErrorEvent.generic => VideocallErrorEvent.network || VideocallErrorEvent.generic => I18n.errorGeneric,
I18n.errorGeneric,
}; };
showErrorDialog(context, key); showErrorDialog(context, key);
vm.clearError(); vm.clearError();
@@ -109,7 +108,7 @@ class _IdleView extends StatelessWidget {
CircularProgressIndicator(color: colorScheme.onSurface), CircularProgressIndicator(color: colorScheme.onSurface),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Inicializando SDK...', context.translate(I18n.videocallInitializingSdk),
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.7)), style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.7)),
), ),
], ],
@@ -125,7 +124,7 @@ class _IdleView extends StatelessWidget {
Icon(Icons.videocam, color: colorScheme.onSurface, size: 64), Icon(Icons.videocam, color: colorScheme.onSurface, size: 64),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
'Videollamada', context.translate(I18n.videocallTitle),
style: TextStyle( style: TextStyle(
color: colorScheme.onSurface, color: colorScheme.onSurface,
fontSize: 24, fontSize: 24,
@@ -137,7 +136,7 @@ class _IdleView extends StatelessWidget {
controller: controller, controller: controller,
style: TextStyle(color: colorScheme.onSurface), style: TextStyle(color: colorScheme.onSurface),
decoration: InputDecoration( decoration: InputDecoration(
hintText: 'User ID del destinatario', hintText: context.translate(I18n.videocallRecipientUserId),
hintStyle: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)), hintStyle: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)),
enabledBorder: OutlineInputBorder( enabledBorder: OutlineInputBorder(
borderSide: BorderSide(color: colorScheme.onSurface.withValues(alpha: 0.24)), borderSide: BorderSide(color: colorScheme.onSurface.withValues(alpha: 0.24)),
@@ -162,7 +161,7 @@ class _IdleView extends StatelessWidget {
vm.startCall(userId); vm.startCall(userId);
}, },
icon: const Icon(Icons.videocam), icon: const Icon(Icons.videocam),
label: const Text('Iniciar videollamada'), label: Text(context.translate(I18n.videocallStart)),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: context.sfColors.legacyPrimary, backgroundColor: context.sfColors.legacyPrimary,
foregroundColor: colorScheme.onPrimary, foregroundColor: colorScheme.onPrimary,
@@ -185,7 +184,7 @@ class _IdleView extends StatelessWidget {
), ),
if (state.localUserId.isNotEmpty) if (state.localUserId.isNotEmpty)
Text( Text(
'Logged in as: ${state.localUserId}', context.translate(I18n.videocallLoggedInAs, args: {'userId': state.localUserId}),
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38), fontSize: 12), style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38), fontSize: 12),
), ),
], ],
@@ -252,7 +251,7 @@ class _InCallView extends StatelessWidget {
Icon(Icons.person, color: colorScheme.onSurface.withValues(alpha: 0.24), size: 96), Icon(Icons.person, color: colorScheme.onSurface.withValues(alpha: 0.24), size: 96),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'Esperando video remoto...', context.translate(I18n.videocallWaitingRemoteVideo),
style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)), style: TextStyle(color: colorScheme.onSurface.withValues(alpha: 0.38)),
), ),
], ],

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
class CallControlsWidget extends StatelessWidget { class CallControlsWidget extends StatelessWidget {
const CallControlsWidget({ const CallControlsWidget({
@@ -29,7 +30,7 @@ class CallControlsWidget extends StatelessWidget {
children: [ children: [
_ControlButton( _ControlButton(
icon: isMicEnabled ? Icons.mic : Icons.mic_off, icon: isMicEnabled ? Icons.mic : Icons.mic_off,
label: isMicEnabled ? 'Mic On' : 'Mic Off', label: context.translate(isMicEnabled ? I18n.videocallMicOn : I18n.videocallMicOff),
isActive: isMicEnabled, isActive: isMicEnabled,
onPressed: onToggleMic, onPressed: onToggleMic,
), ),
@@ -37,19 +38,19 @@ class CallControlsWidget extends StatelessWidget {
icon: isSpeakerEnabled icon: isSpeakerEnabled
? Icons.volume_up ? Icons.volume_up
: Icons.volume_off, : Icons.volume_off,
label: isSpeakerEnabled ? 'Speaker' : 'Earpiece', label: context.translate(isSpeakerEnabled ? I18n.videocallSpeaker : I18n.videocallEarpiece),
isActive: isSpeakerEnabled, isActive: isSpeakerEnabled,
onPressed: onToggleSpeaker, onPressed: onToggleSpeaker,
), ),
_ControlButton( _ControlButton(
icon: Icons.cameraswitch, icon: Icons.cameraswitch,
label: isFrontCamera ? 'Front' : 'Back', label: context.translate(isFrontCamera ? I18n.videocallCameraFront : I18n.videocallCameraBack),
isActive: true, isActive: true,
onPressed: onSwitchCamera, onPressed: onSwitchCamera,
), ),
_ControlButton( _ControlButton(
icon: Icons.call_end, icon: Icons.call_end,
label: 'Hang Up', label: context.translate(I18n.videocallHangUp),
isActive: false, isActive: false,
isDestructive: true, isDestructive: true,
onPressed: onHangUp, onPressed: onHangUp,

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../../domain/entities/videocall_error.dart'; import '../../domain/entities/videocall_error.dart';
@@ -17,8 +18,8 @@ class CallStatusIndicator extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final (text, showCancel) = switch (screenMode) { final (text, showCancel) = switch (screenMode) {
VideocallScreenMode.outgoing => ('Llamando a $remoteUserId...', true), VideocallScreenMode.outgoing => (context.translate(I18n.videocallCalling, args: {'userId': remoteUserId}), true),
VideocallScreenMode.incoming => ('$remoteUserId te está llamando', false), VideocallScreenMode.incoming => (context.translate(I18n.videocallUserCalling, args: {'userId': remoteUserId}), false),
_ => ('', false), _ => ('', false),
}; };

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
class IncomingCallOverlay extends StatelessWidget { class IncomingCallOverlay extends StatelessWidget {
const IncomingCallOverlay({ const IncomingCallOverlay({
@@ -20,7 +21,6 @@ class IncomingCallOverlay extends StatelessWidget {
return Container( return Container(
color: colorScheme.surface, color: colorScheme.surface,
child: SafeArea(
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
@@ -32,7 +32,7 @@ class IncomingCallOverlay extends StatelessWidget {
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
Text( Text(
isVideo ? 'Videollamada entrante' : 'Llamada entrante', context.translate(isVideo ? I18n.videocallIncomingVideo : I18n.videocallIncomingAudio),
style: TextStyle( style: TextStyle(
color: colorScheme.onSurface.withValues(alpha: 0.7), color: colorScheme.onSurface.withValues(alpha: 0.7),
fontSize: 16, fontSize: 16,
@@ -56,13 +56,13 @@ class IncomingCallOverlay extends StatelessWidget {
_ActionButton( _ActionButton(
icon: Icons.call_end, icon: Icons.call_end,
color: colorScheme.error, color: colorScheme.error,
label: 'Rechazar', label: context.translate(I18n.videocallReject),
onPressed: onReject, onPressed: onReject,
), ),
_ActionButton( _ActionButton(
icon: isVideo ? Icons.videocam : Icons.call, icon: isVideo ? Icons.videocam : Icons.call,
color: Colors.green, color: Colors.green,
label: 'Aceptar', label: context.translate(I18n.videocallAccept),
onPressed: onAccept, onPressed: onAccept,
), ),
], ],
@@ -70,7 +70,6 @@ class IncomingCallOverlay extends StatelessWidget {
), ),
], ],
), ),
),
); );
} }
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../../domain/entities/videocall_participant.dart'; import '../../domain/entities/videocall_participant.dart';
import 'participant_tile_widget.dart'; import 'participant_tile_widget.dart';
@@ -14,10 +15,12 @@ class ParticipantGridWidget extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (participants.isEmpty) { if (participants.isEmpty) {
return const Center( return Center(
child: Text( child: Text(
'Esperando participantes...', context.translate(I18n.videocallWaitingParticipants),
style: TextStyle(color: Colors.white70), style: TextStyle(
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.7),
),
), ),
); );
} }

View File

@@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sf_localizations/sf_localizations.dart';
import '../../domain/entities/videocall_participant.dart'; import '../../domain/entities/videocall_participant.dart';
import 'video_view_widget.dart'; import 'video_view_widget.dart';
@@ -52,7 +53,7 @@ class ParticipantTileWidget extends StatelessWidget {
child: Icon(Icons.mic_off, color: colorScheme.error, size: 14), child: Icon(Icons.mic_off, color: colorScheme.error, size: 14),
), ),
Text( Text(
participant.isSelf ? '' : participant.userId, participant.isSelf ? context.translate(I18n.videocallYou) : participant.userId,
style: TextStyle(color: colorScheme.onSurface, fontSize: 12), style: TextStyle(color: colorScheme.onSurface, fontSize: 12),
), ),
], ],

View File

@@ -3,7 +3,7 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:videocall_sdk/videocall_sdk.dart'; import 'package:videocall_sdk/videocall_sdk.dart';
class VideoViewWidget extends StatefulWidget { class VideoViewWidget extends StatelessWidget {
const VideoViewWidget({ const VideoViewWidget({
super.key, super.key,
required this.canvas, required this.canvas,
@@ -17,51 +17,26 @@ class VideoViewWidget extends StatefulWidget {
final double height; final double height;
final BoxFit fit; final BoxFit fit;
@override Future<Widget> _buildVideoView() {
State<VideoViewWidget> createState() => _VideoViewWidgetState();
}
class _VideoViewWidgetState extends State<VideoViewWidget> {
Widget? _videoWidget;
@override
void initState() {
super.initState();
_initVideoView();
}
@override
void didUpdateWidget(VideoViewWidget oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.canvas.videoCanvasId != widget.canvas.videoCanvasId) {
_initVideoView();
}
}
Future<void> _initVideoView() async {
final Widget videoWidget;
if (Platform.isIOS) { if (Platform.isIOS) {
videoWidget = await widget.canvas.getIOSVideoView( return canvas.getIOSVideoView((_) {}, width, height);
(viewId) {},
widget.width,
widget.height,
);
} else {
videoWidget = await widget.canvas.getAndroidVideoView();
}
if (mounted) {
setState(() => _videoWidget = videoWidget);
} }
return canvas.getAndroidVideoView();
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
width: widget.width, width: width,
height: widget.height, height: height,
child: _videoWidget ?? child: FutureBuilder<Widget>(
const Center( future: _buildVideoView(),
builder: (context, snapshot) {
if (snapshot.hasData) return snapshot.data!;
return const Center(
child: CircularProgressIndicator(strokeWidth: 2), child: CircularProgressIndicator(strokeWidth: 2),
);
},
), ),
); );
} }

View File

@@ -683,6 +683,34 @@
"locationRevealSkip": "Überspringen", "locationRevealSkip": "Überspringen",
"locationDeleteGeofenceConfirm": "Möchten Sie diese sichere Zone wirklich löschen?", "locationDeleteGeofenceConfirm": "Möchten Sie diese sichere Zone wirklich löschen?",
"locationDeleteFrequentPlaceConfirm": "Möchten Sie diesen häufigen Ort wirklich löschen?", "locationDeleteFrequentPlaceConfirm": "Möchten Sie diesen häufigen Ort wirklich löschen?",
"videocallTitle": "Videoanruf",
"videocallInitializingSdk": "SDK wird initialisiert...",
"videocallRecipientUserId": "Benutzer-ID des Empfängers",
"videocallStart": "Videoanruf starten",
"videocallLoggedInAs": "Angemeldet als: {userId}",
"videocallCalling": "{userId} wird angerufen...",
"videocallUserCalling": "{userId} ruft dich an",
"videocallIncomingVideo": "Eingehender Videoanruf",
"videocallIncomingAudio": "Eingehender Anruf",
"videocallReject": "Ablehnen",
"videocallAccept": "Annehmen",
"videocallMicOn": "Mikro an",
"videocallMicOff": "Mikro aus",
"videocallSpeaker": "Lautsprecher",
"videocallEarpiece": "Hörer",
"videocallCameraFront": "Vorne",
"videocallCameraBack": "Hinten",
"videocallHangUp": "Auflegen",
"videocallWaitingParticipants": "Warten auf Teilnehmer...",
"videocallWaitingRemoteVideo": "Warten auf Remote-Video...",
"videocallYou": "Du",
"videocallErrorSdkInit": "Fehler beim Initialisieren des Videoanrufs",
"videocallErrorAuth": "Authentifizierungsfehler beim Videoanruf",
"videocallErrorCallStart": "Fehler beim Starten des Anrufs",
"videocallErrorCallAnswer": "Fehler beim Annehmen des Anrufs",
"videocallErrorCamera": "Kameraberechtigung erforderlich",
"videocallErrorMic": "Mikrofonberechtigung erforderlich",
"videocallErrorMissedCall": "Verpasster Anruf",
"positionUpdated": "Letzte verfügbare Position aktualisiert", "positionUpdated": "Letzte verfügbare Position aktualisiert",
"locationMapStyleLight": "Hell", "locationMapStyleLight": "Hell",
"locationMapStyleDark": "Dunkel", "locationMapStyleDark": "Dunkel",
@@ -757,7 +785,7 @@
"frequentPlaceCreated": "Häufiger Ort erstellt", "frequentPlaceCreated": "Häufiger Ort erstellt",
"frequentPlaceUpdated": "Häufiger Ort aktualisiert", "frequentPlaceUpdated": "Häufiger Ort aktualisiert",
"frequentPlaceDeleted": "Häufiger Ort gelöscht", "frequentPlaceDeleted": "Häufiger Ort gelöscht",
"errorGeneric": "Etwas ist schiefgelaufen. Bitte versuchen Sie es erneut.", "errorGeneric": "Ein unerwarteter Fehler ist aufgetreten.",
"pullDownToRetry": "Zum Wiederholen nach unten ziehen", "pullDownToRetry": "Zum Wiederholen nach unten ziehen",
"errorGeofenceCreate": "Die Sicherheitszone konnte nicht erstellt werden", "errorGeofenceCreate": "Die Sicherheitszone konnte nicht erstellt werden",
"errorGeofenceUpdate": "Die Sicherheitszone konnte nicht aktualisiert werden", "errorGeofenceUpdate": "Die Sicherheitszone konnte nicht aktualisiert werden",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.", "errorSessionExpired": "Deine Sitzung ist abgelaufen. Bitte melde dich erneut an.",
"errorValidation": "Die eingegebenen Daten sind ungültig.", "errorValidation": "Die eingegebenen Daten sind ungültig.",
"errorScaRequired": "Eine zusätzliche Authentifizierung ist erforderlich.", "errorScaRequired": "Eine zusätzliche Authentifizierung ist erforderlich.",
"errorDeviceNotOwned": "Du hast keine Berechtigung, auf dieses Gerät zuzugreifen.", "errorDeviceNotOwned": "Du hast keine Berechtigung, auf dieses Gerät zuzugreifen."
"errorGeneric": "Ein unerwarteter Fehler ist aufgetreten."
} }

View File

@@ -863,6 +863,34 @@
"locationRevealSkip": "Skip", "locationRevealSkip": "Skip",
"locationDeleteGeofenceConfirm": "Are you sure you want to delete this safe zone?", "locationDeleteGeofenceConfirm": "Are you sure you want to delete this safe zone?",
"locationDeleteFrequentPlaceConfirm": "Are you sure you want to delete this frequent place?", "locationDeleteFrequentPlaceConfirm": "Are you sure you want to delete this frequent place?",
"videocallTitle": "Video Call",
"videocallInitializingSdk": "Initializing SDK...",
"videocallRecipientUserId": "Recipient User ID",
"videocallStart": "Start video call",
"videocallLoggedInAs": "Logged in as: {userId}",
"videocallCalling": "Calling {userId}...",
"videocallUserCalling": "{userId} is calling you",
"videocallIncomingVideo": "Incoming video call",
"videocallIncomingAudio": "Incoming call",
"videocallReject": "Reject",
"videocallAccept": "Accept",
"videocallMicOn": "Mic On",
"videocallMicOff": "Mic Off",
"videocallSpeaker": "Speaker",
"videocallEarpiece": "Earpiece",
"videocallCameraFront": "Front",
"videocallCameraBack": "Back",
"videocallHangUp": "Hang Up",
"videocallWaitingParticipants": "Waiting for participants...",
"videocallWaitingRemoteVideo": "Waiting for remote video...",
"videocallYou": "You",
"videocallErrorSdkInit": "Error initializing video call",
"videocallErrorAuth": "Video call authentication error",
"videocallErrorCallStart": "Error starting the call",
"videocallErrorCallAnswer": "Error answering the call",
"videocallErrorCamera": "Camera permission required",
"videocallErrorMic": "Microphone permission required",
"videocallErrorMissedCall": "Missed call",
"positionUpdated": "Updated to latest available position", "positionUpdated": "Updated to latest available position",
"locationMapStyleLight": "Light", "locationMapStyleLight": "Light",
"locationMapStyleDark": "Dark", "locationMapStyleDark": "Dark",
@@ -931,7 +959,7 @@
"frequentPlaceCreated": "Frequent place created", "frequentPlaceCreated": "Frequent place created",
"frequentPlaceUpdated": "Frequent place updated", "frequentPlaceUpdated": "Frequent place updated",
"frequentPlaceDeleted": "Frequent place deleted", "frequentPlaceDeleted": "Frequent place deleted",
"errorGeneric": "Something went wrong. Please try again.", "errorGeneric": "An unexpected error occurred.",
"pullDownToRetry": "Pull down to retry", "pullDownToRetry": "Pull down to retry",
"errorGeofenceCreate": "Could not create the safety zone", "errorGeofenceCreate": "Could not create the safety zone",
"errorGeofenceUpdate": "Could not update the safety zone", "errorGeofenceUpdate": "Could not update the safety zone",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "Your session has expired. Please sign in again.", "errorSessionExpired": "Your session has expired. Please sign in again.",
"errorValidation": "The information you entered is not valid.", "errorValidation": "The information you entered is not valid.",
"errorScaRequired": "Additional authentication is required to continue.", "errorScaRequired": "Additional authentication is required to continue.",
"errorDeviceNotOwned": "You don't have permission to access this device.", "errorDeviceNotOwned": "You don't have permission to access this device."
"errorGeneric": "An unexpected error occurred."
} }

View File

@@ -864,6 +864,34 @@
"locationRevealSkip": "Saltar", "locationRevealSkip": "Saltar",
"locationDeleteGeofenceConfirm": "¿Seguro que quieres eliminar esta zona segura?", "locationDeleteGeofenceConfirm": "¿Seguro que quieres eliminar esta zona segura?",
"locationDeleteFrequentPlaceConfirm": "¿Seguro que quieres eliminar este lugar frecuente?", "locationDeleteFrequentPlaceConfirm": "¿Seguro que quieres eliminar este lugar frecuente?",
"videocallTitle": "Videollamada",
"videocallInitializingSdk": "Inicializando SDK...",
"videocallRecipientUserId": "User ID del destinatario",
"videocallStart": "Iniciar videollamada",
"videocallLoggedInAs": "Conectado como: {userId}",
"videocallCalling": "Llamando a {userId}...",
"videocallUserCalling": "{userId} te está llamando",
"videocallIncomingVideo": "Videollamada entrante",
"videocallIncomingAudio": "Llamada entrante",
"videocallReject": "Rechazar",
"videocallAccept": "Aceptar",
"videocallMicOn": "Mic On",
"videocallMicOff": "Mic Off",
"videocallSpeaker": "Altavoz",
"videocallEarpiece": "Auricular",
"videocallCameraFront": "Frontal",
"videocallCameraBack": "Trasera",
"videocallHangUp": "Colgar",
"videocallWaitingParticipants": "Esperando participantes...",
"videocallWaitingRemoteVideo": "Esperando video remoto...",
"videocallYou": "Tú",
"videocallErrorSdkInit": "Error al inicializar videollamada",
"videocallErrorAuth": "Error de autenticación de videollamada",
"videocallErrorCallStart": "Error al iniciar la llamada",
"videocallErrorCallAnswer": "Error al contestar la llamada",
"videocallErrorCamera": "Se requiere permiso de cámara",
"videocallErrorMic": "Se requiere permiso de micrófono",
"videocallErrorMissedCall": "Llamada perdida",
"positionUpdated": "Última posición disponible actualizada", "positionUpdated": "Última posición disponible actualizada",
"locationMapStyleLight": "Claro", "locationMapStyleLight": "Claro",
"locationMapStyleDark": "Oscuro", "locationMapStyleDark": "Oscuro",
@@ -932,7 +960,7 @@
"frequentPlaceCreated": "Lugar frecuente creado", "frequentPlaceCreated": "Lugar frecuente creado",
"frequentPlaceUpdated": "Lugar frecuente actualizado", "frequentPlaceUpdated": "Lugar frecuente actualizado",
"frequentPlaceDeleted": "Lugar frecuente eliminado", "frequentPlaceDeleted": "Lugar frecuente eliminado",
"errorGeneric": "Algo salió mal. Inténtalo de nuevo.", "errorGeneric": "Ha ocurrido un error inesperado.",
"pullDownToRetry": "Desliza hacia abajo para reintentar", "pullDownToRetry": "Desliza hacia abajo para reintentar",
"errorGeofenceCreate": "No se pudo crear la zona de seguridad", "errorGeofenceCreate": "No se pudo crear la zona de seguridad",
"errorGeofenceUpdate": "No se pudo actualizar la zona de seguridad", "errorGeofenceUpdate": "No se pudo actualizar la zona de seguridad",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "Tu sesión ha caducado. Vuelve a iniciar sesión.", "errorSessionExpired": "Tu sesión ha caducado. Vuelve a iniciar sesión.",
"errorValidation": "Los datos introducidos no son válidos.", "errorValidation": "Los datos introducidos no son válidos.",
"errorScaRequired": "Se requiere autenticación adicional para continuar.", "errorScaRequired": "Se requiere autenticación adicional para continuar.",
"errorDeviceNotOwned": "No tienes permiso para acceder a este dispositivo.", "errorDeviceNotOwned": "No tienes permiso para acceder a este dispositivo."
"errorGeneric": "Ha ocurrido un error inesperado."
} }

View File

@@ -683,6 +683,34 @@
"locationRevealSkip": "Passer", "locationRevealSkip": "Passer",
"locationDeleteGeofenceConfirm": "Voulez-vous supprimer cette zone sûre ?", "locationDeleteGeofenceConfirm": "Voulez-vous supprimer cette zone sûre ?",
"locationDeleteFrequentPlaceConfirm": "Voulez-vous supprimer ce lieu fréquent ?", "locationDeleteFrequentPlaceConfirm": "Voulez-vous supprimer ce lieu fréquent ?",
"videocallTitle": "Appel vidéo",
"videocallInitializingSdk": "Initialisation du SDK...",
"videocallRecipientUserId": "ID utilisateur du destinataire",
"videocallStart": "Démarrer l'appel vidéo",
"videocallLoggedInAs": "Connecté en tant que : {userId}",
"videocallCalling": "Appel de {userId}...",
"videocallUserCalling": "{userId} vous appelle",
"videocallIncomingVideo": "Appel vidéo entrant",
"videocallIncomingAudio": "Appel entrant",
"videocallReject": "Rejeter",
"videocallAccept": "Accepter",
"videocallMicOn": "Micro activé",
"videocallMicOff": "Micro désactivé",
"videocallSpeaker": "Haut-parleur",
"videocallEarpiece": "Écouteur",
"videocallCameraFront": "Avant",
"videocallCameraBack": "Arrière",
"videocallHangUp": "Raccrocher",
"videocallWaitingParticipants": "En attente de participants...",
"videocallWaitingRemoteVideo": "En attente de la vidéo distante...",
"videocallYou": "Vous",
"videocallErrorSdkInit": "Erreur d'initialisation de l'appel vidéo",
"videocallErrorAuth": "Erreur d'authentification de l'appel vidéo",
"videocallErrorCallStart": "Erreur lors du démarrage de l'appel",
"videocallErrorCallAnswer": "Erreur lors de la réponse à l'appel",
"videocallErrorCamera": "Autorisation de la caméra requise",
"videocallErrorMic": "Autorisation du microphone requise",
"videocallErrorMissedCall": "Appel manqué",
"positionUpdated": "Dernière position disponible mise à jour", "positionUpdated": "Dernière position disponible mise à jour",
"locationMapStyleLight": "Clair", "locationMapStyleLight": "Clair",
"locationMapStyleDark": "Sombre", "locationMapStyleDark": "Sombre",
@@ -757,7 +785,7 @@
"frequentPlaceCreated": "Lieu fréquent créé", "frequentPlaceCreated": "Lieu fréquent créé",
"frequentPlaceUpdated": "Lieu fréquent mis à jour", "frequentPlaceUpdated": "Lieu fréquent mis à jour",
"frequentPlaceDeleted": "Lieu fréquent supprimé", "frequentPlaceDeleted": "Lieu fréquent supprimé",
"errorGeneric": "Une erreur est survenue. Veuillez réessayer.", "errorGeneric": "Une erreur inattendue s'est produite.",
"pullDownToRetry": "Tirez vers le bas pour réessayer", "pullDownToRetry": "Tirez vers le bas pour réessayer",
"errorGeofenceCreate": "Impossible de créer la zone de sécurité", "errorGeofenceCreate": "Impossible de créer la zone de sécurité",
"errorGeofenceUpdate": "Impossible de mettre à jour la zone de sécurité", "errorGeofenceUpdate": "Impossible de mettre à jour la zone de sécurité",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "Votre session a expiré. Veuillez vous reconnecter.", "errorSessionExpired": "Votre session a expiré. Veuillez vous reconnecter.",
"errorValidation": "Les données saisies ne sont pas valides.", "errorValidation": "Les données saisies ne sont pas valides.",
"errorScaRequired": "Une authentification supplémentaire est requise pour continuer.", "errorScaRequired": "Une authentification supplémentaire est requise pour continuer.",
"errorDeviceNotOwned": "Vous n'avez pas l'autorisation d'accéder à cet appareil.", "errorDeviceNotOwned": "Vous n'avez pas l'autorisation d'accéder à cet appareil."
"errorGeneric": "Une erreur inattendue s'est produite."
} }

View File

@@ -683,6 +683,34 @@
"locationRevealSkip": "Salta", "locationRevealSkip": "Salta",
"locationDeleteGeofenceConfirm": "Sei sicuro di voler eliminare questa zona sicura?", "locationDeleteGeofenceConfirm": "Sei sicuro di voler eliminare questa zona sicura?",
"locationDeleteFrequentPlaceConfirm": "Sei sicuro di voler eliminare questo luogo frequente?", "locationDeleteFrequentPlaceConfirm": "Sei sicuro di voler eliminare questo luogo frequente?",
"videocallTitle": "Videochiamata",
"videocallInitializingSdk": "Inizializzazione SDK...",
"videocallRecipientUserId": "ID utente del destinatario",
"videocallStart": "Avvia videochiamata",
"videocallLoggedInAs": "Connesso come: {userId}",
"videocallCalling": "Chiamata a {userId}...",
"videocallUserCalling": "{userId} ti sta chiamando",
"videocallIncomingVideo": "Videochiamata in arrivo",
"videocallIncomingAudio": "Chiamata in arrivo",
"videocallReject": "Rifiuta",
"videocallAccept": "Accetta",
"videocallMicOn": "Micro attivo",
"videocallMicOff": "Micro disattivato",
"videocallSpeaker": "Altoparlante",
"videocallEarpiece": "Auricolare",
"videocallCameraFront": "Anteriore",
"videocallCameraBack": "Posteriore",
"videocallHangUp": "Riaggancia",
"videocallWaitingParticipants": "In attesa di partecipanti...",
"videocallWaitingRemoteVideo": "In attesa del video remoto...",
"videocallYou": "Tu",
"videocallErrorSdkInit": "Errore nell'inizializzazione della videochiamata",
"videocallErrorAuth": "Errore di autenticazione della videochiamata",
"videocallErrorCallStart": "Errore nell'avvio della chiamata",
"videocallErrorCallAnswer": "Errore nel rispondere alla chiamata",
"videocallErrorCamera": "Permesso fotocamera richiesto",
"videocallErrorMic": "Permesso microfono richiesto",
"videocallErrorMissedCall": "Chiamata persa",
"positionUpdated": "Ultima posizione disponibile aggiornata", "positionUpdated": "Ultima posizione disponibile aggiornata",
"locationMapStyleLight": "Chiaro", "locationMapStyleLight": "Chiaro",
"locationMapStyleDark": "Scuro", "locationMapStyleDark": "Scuro",
@@ -757,7 +785,7 @@
"frequentPlaceCreated": "Luogo frequente creato", "frequentPlaceCreated": "Luogo frequente creato",
"frequentPlaceUpdated": "Luogo frequente aggiornato", "frequentPlaceUpdated": "Luogo frequente aggiornato",
"frequentPlaceDeleted": "Luogo frequente eliminato", "frequentPlaceDeleted": "Luogo frequente eliminato",
"errorGeneric": "Qualcosa è andato storto. Riprova.", "errorGeneric": "Si è verificato un errore imprevisto.",
"pullDownToRetry": "Trascina verso il basso per riprovare", "pullDownToRetry": "Trascina verso il basso per riprovare",
"errorGeofenceCreate": "Impossibile creare la zona di sicurezza", "errorGeofenceCreate": "Impossibile creare la zona di sicurezza",
"errorGeofenceUpdate": "Impossibile aggiornare la zona di sicurezza", "errorGeofenceUpdate": "Impossibile aggiornare la zona di sicurezza",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "La tua sessione è scaduta. Accedi di nuovo.", "errorSessionExpired": "La tua sessione è scaduta. Accedi di nuovo.",
"errorValidation": "I dati inseriti non sono validi.", "errorValidation": "I dati inseriti non sono validi.",
"errorScaRequired": "È richiesta un'autenticazione aggiuntiva per continuare.", "errorScaRequired": "È richiesta un'autenticazione aggiuntiva per continuare.",
"errorDeviceNotOwned": "Non hai il permesso di accedere a questo dispositivo.", "errorDeviceNotOwned": "Non hai il permesso di accedere a questo dispositivo."
"errorGeneric": "Si è verificato un errore imprevisto."
} }

View File

@@ -683,6 +683,34 @@
"locationRevealSkip": "Saltar", "locationRevealSkip": "Saltar",
"locationDeleteGeofenceConfirm": "Tens a certeza que queres eliminar esta zona segura?", "locationDeleteGeofenceConfirm": "Tens a certeza que queres eliminar esta zona segura?",
"locationDeleteFrequentPlaceConfirm": "Tens a certeza que queres eliminar este lugar frequente?", "locationDeleteFrequentPlaceConfirm": "Tens a certeza que queres eliminar este lugar frequente?",
"videocallTitle": "Videochamada",
"videocallInitializingSdk": "A inicializar SDK...",
"videocallRecipientUserId": "ID do utilizador destinatário",
"videocallStart": "Iniciar videochamada",
"videocallLoggedInAs": "Conectado como: {userId}",
"videocallCalling": "A ligar para {userId}...",
"videocallUserCalling": "{userId} está a ligar-te",
"videocallIncomingVideo": "Videochamada recebida",
"videocallIncomingAudio": "Chamada recebida",
"videocallReject": "Rejeitar",
"videocallAccept": "Aceitar",
"videocallMicOn": "Micro ligado",
"videocallMicOff": "Micro desligado",
"videocallSpeaker": "Altifalante",
"videocallEarpiece": "Auricular",
"videocallCameraFront": "Frontal",
"videocallCameraBack": "Traseira",
"videocallHangUp": "Desligar",
"videocallWaitingParticipants": "A aguardar participantes...",
"videocallWaitingRemoteVideo": "A aguardar vídeo remoto...",
"videocallYou": "Tu",
"videocallErrorSdkInit": "Erro ao inicializar videochamada",
"videocallErrorAuth": "Erro de autenticação da videochamada",
"videocallErrorCallStart": "Erro ao iniciar a chamada",
"videocallErrorCallAnswer": "Erro ao atender a chamada",
"videocallErrorCamera": "Permissão de câmara necessária",
"videocallErrorMic": "Permissão de microfone necessária",
"videocallErrorMissedCall": "Chamada perdida",
"positionUpdated": "Última posição disponível atualizada", "positionUpdated": "Última posição disponível atualizada",
"locationMapStyleLight": "Claro", "locationMapStyleLight": "Claro",
"locationMapStyleDark": "Escuro", "locationMapStyleDark": "Escuro",
@@ -757,7 +785,7 @@
"frequentPlaceCreated": "Local frequente criado", "frequentPlaceCreated": "Local frequente criado",
"frequentPlaceUpdated": "Local frequente atualizado", "frequentPlaceUpdated": "Local frequente atualizado",
"frequentPlaceDeleted": "Local frequente eliminado", "frequentPlaceDeleted": "Local frequente eliminado",
"errorGeneric": "Algo correu mal. Tente novamente.", "errorGeneric": "Ocorreu um erro inesperado.",
"pullDownToRetry": "Deslize para baixo para tentar novamente", "pullDownToRetry": "Deslize para baixo para tentar novamente",
"errorGeofenceCreate": "Não foi possível criar a zona de segurança", "errorGeofenceCreate": "Não foi possível criar a zona de segurança",
"errorGeofenceUpdate": "Não foi possível atualizar a zona de segurança", "errorGeofenceUpdate": "Não foi possível atualizar a zona de segurança",
@@ -1060,6 +1088,5 @@
"errorSessionExpired": "A tua sessão expirou. Inicia sessão novamente.", "errorSessionExpired": "A tua sessão expirou. Inicia sessão novamente.",
"errorValidation": "Os dados introduzidos não são válidos.", "errorValidation": "Os dados introduzidos não são válidos.",
"errorScaRequired": "É necessária autenticação adicional para continuar.", "errorScaRequired": "É necessária autenticação adicional para continuar.",
"errorDeviceNotOwned": "Não tens permissão para aceder a este dispositivo.", "errorDeviceNotOwned": "Não tens permissão para aceder a este dispositivo."
"errorGeneric": "Ocorreu um erro inesperado."
} }

View File

@@ -1006,6 +1006,34 @@ class I18n {
static const String verifyAccount = 'verifyAccount'; static const String verifyAccount = 'verifyAccount';
static const String vibrationOnly = 'vibrationOnly'; static const String vibrationOnly = 'vibrationOnly';
static const String videoCall = 'videoCall'; static const String videoCall = 'videoCall';
static const String videocallAccept = 'videocallAccept';
static const String videocallCalling = 'videocallCalling';
static const String videocallCameraBack = 'videocallCameraBack';
static const String videocallCameraFront = 'videocallCameraFront';
static const String videocallEarpiece = 'videocallEarpiece';
static const String videocallErrorAuth = 'videocallErrorAuth';
static const String videocallErrorCallAnswer = 'videocallErrorCallAnswer';
static const String videocallErrorCallStart = 'videocallErrorCallStart';
static const String videocallErrorCamera = 'videocallErrorCamera';
static const String videocallErrorMic = 'videocallErrorMic';
static const String videocallErrorMissedCall = 'videocallErrorMissedCall';
static const String videocallErrorSdkInit = 'videocallErrorSdkInit';
static const String videocallHangUp = 'videocallHangUp';
static const String videocallIncomingAudio = 'videocallIncomingAudio';
static const String videocallIncomingVideo = 'videocallIncomingVideo';
static const String videocallInitializingSdk = 'videocallInitializingSdk';
static const String videocallLoggedInAs = 'videocallLoggedInAs';
static const String videocallMicOff = 'videocallMicOff';
static const String videocallMicOn = 'videocallMicOn';
static const String videocallRecipientUserId = 'videocallRecipientUserId';
static const String videocallReject = 'videocallReject';
static const String videocallSpeaker = 'videocallSpeaker';
static const String videocallStart = 'videocallStart';
static const String videocallTitle = 'videocallTitle';
static const String videocallUserCalling = 'videocallUserCalling';
static const String videocallWaitingParticipants = 'videocallWaitingParticipants';
static const String videocallWaitingRemoteVideo = 'videocallWaitingRemoteVideo';
static const String videocallYou = 'videocallYou';
static const String volumeAlarm = 'volumeAlarm'; static const String volumeAlarm = 'volumeAlarm';
static const String volumeControl = 'volumeControl'; static const String volumeControl = 'volumeControl';
static const String volumeHint = 'volumeHint'; static const String volumeHint = 'volumeHint';

View File

@@ -18,6 +18,8 @@ class VideocallItem {
final bool uploadVideoStreamSelf; final bool uploadVideoStreamSelf;
final bool uploadVideoStreamOther; final bool uploadVideoStreamOther;
bool get isTalking => state == VideocallState.talking;
VideocallItem copyWith({ VideocallItem copyWith({
String? userId, String? userId,
bool? isVideo, bool? isVideo,