♻️ Proper singaling
This commit is contained in:
@@ -2241,7 +2241,7 @@ as List<CallParticipant>,
|
|||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$CallParticipant {
|
mixin _$CallParticipant {
|
||||||
|
|
||||||
String get identity; String get name; String get accountId; SnAccount? get account; DateTime get joinedAt; bool get isLocal;
|
String get identity; String get name; String get accountId; SnAccount? get account; DateTime get joinedAt;
|
||||||
/// Create a copy of CallParticipant
|
/// Create a copy of CallParticipant
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -2254,16 +2254,16 @@ $CallParticipantCopyWith<CallParticipant> get copyWith => _$CallParticipantCopyW
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.isLocal, isLocal) || other.isLocal == isLocal));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt,isLocal);
|
int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt, isLocal: $isLocal)';
|
return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2274,7 +2274,7 @@ abstract mixin class $CallParticipantCopyWith<$Res> {
|
|||||||
factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl;
|
factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal
|
String identity, String name, String accountId, SnAccount? account, DateTime joinedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -2291,15 +2291,14 @@ class _$CallParticipantCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of CallParticipant
|
/// Create a copy of CallParticipant
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? identity = null,Object? name = null,Object? accountId = null,Object? account = freezed,Object? joinedAt = null,Object? isLocal = null,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? identity = null,Object? name = null,Object? accountId = null,Object? account = freezed,Object? joinedAt = null,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
|
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
as SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,isLocal: null == isLocal ? _self.isLocal : isLocal // ignore: cast_nullable_to_non_nullable
|
as DateTime,
|
||||||
as bool,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
/// Create a copy of CallParticipant
|
/// Create a copy of CallParticipant
|
||||||
@@ -2393,10 +2392,10 @@ return $default(_that);case _:
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal)? $default,{required TResult orElse(),}) {final _that = this;
|
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _CallParticipant() when $default != null:
|
case _CallParticipant() when $default != null:
|
||||||
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);case _:
|
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);case _:
|
||||||
return orElse();
|
return orElse();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2414,10 +2413,10 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal) $default,) {final _that = this;
|
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt) $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _CallParticipant():
|
case _CallParticipant():
|
||||||
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);}
|
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);}
|
||||||
}
|
}
|
||||||
/// A variant of `when` that fallback to returning `null`
|
/// A variant of `when` that fallback to returning `null`
|
||||||
///
|
///
|
||||||
@@ -2431,10 +2430,10 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo
|
|||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
||||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal)? $default,) {final _that = this;
|
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt)? $default,) {final _that = this;
|
||||||
switch (_that) {
|
switch (_that) {
|
||||||
case _CallParticipant() when $default != null:
|
case _CallParticipant() when $default != null:
|
||||||
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);case _:
|
return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);case _:
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -2446,7 +2445,7 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
|
|
||||||
class _CallParticipant implements CallParticipant {
|
class _CallParticipant implements CallParticipant {
|
||||||
const _CallParticipant({required this.identity, required this.name, required this.accountId, this.account = null, required this.joinedAt, this.isLocal = false});
|
const _CallParticipant({required this.identity, required this.name, required this.accountId, this.account = null, required this.joinedAt});
|
||||||
factory _CallParticipant.fromJson(Map<String, dynamic> json) => _$CallParticipantFromJson(json);
|
factory _CallParticipant.fromJson(Map<String, dynamic> json) => _$CallParticipantFromJson(json);
|
||||||
|
|
||||||
@override final String identity;
|
@override final String identity;
|
||||||
@@ -2454,7 +2453,6 @@ class _CallParticipant implements CallParticipant {
|
|||||||
@override final String accountId;
|
@override final String accountId;
|
||||||
@override@JsonKey() final SnAccount? account;
|
@override@JsonKey() final SnAccount? account;
|
||||||
@override final DateTime joinedAt;
|
@override final DateTime joinedAt;
|
||||||
@override@JsonKey() final bool isLocal;
|
|
||||||
|
|
||||||
/// Create a copy of CallParticipant
|
/// Create a copy of CallParticipant
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -2469,16 +2467,16 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.isLocal, isLocal) || other.isLocal == isLocal));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallParticipant&&(identical(other.identity, identity) || other.identity == identity)&&(identical(other.name, name) || other.name == name)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.account, account) || other.account == account)&&(identical(other.joinedAt, joinedAt) || other.joinedAt == joinedAt));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt,isLocal);
|
int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt, isLocal: $isLocal)';
|
return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt)';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -2489,7 +2487,7 @@ abstract mixin class _$CallParticipantCopyWith<$Res> implements $CallParticipant
|
|||||||
factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl;
|
factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal
|
String identity, String name, String accountId, SnAccount? account, DateTime joinedAt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -2506,15 +2504,14 @@ class __$CallParticipantCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of CallParticipant
|
/// Create a copy of CallParticipant
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? identity = null,Object? name = null,Object? accountId = null,Object? account = freezed,Object? joinedAt = null,Object? isLocal = null,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? identity = null,Object? name = null,Object? accountId = null,Object? account = freezed,Object? joinedAt = null,}) {
|
||||||
return _then(_CallParticipant(
|
return _then(_CallParticipant(
|
||||||
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
|
identity: null == identity ? _self.identity : identity // ignore: cast_nullable_to_non_nullable
|
||||||
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
as String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
as String,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||||
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
|
||||||
as SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
as SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
||||||
as DateTime,isLocal: null == isLocal ? _self.isLocal : isLocal // ignore: cast_nullable_to_non_nullable
|
as DateTime,
|
||||||
as bool,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -281,7 +281,6 @@ _CallParticipant _$CallParticipantFromJson(Map<String, dynamic> json) =>
|
|||||||
? null
|
? null
|
||||||
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
|
||||||
joinedAt: DateTime.parse(json['joined_at'] as String),
|
joinedAt: DateTime.parse(json['joined_at'] as String),
|
||||||
isLocal: json['is_local'] as bool? ?? false,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
|
Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
|
||||||
@@ -291,7 +290,6 @@ Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
|
|||||||
'account_id': instance.accountId,
|
'account_id': instance.accountId,
|
||||||
'account': instance.account?.toJson(),
|
'account': instance.account?.toJson(),
|
||||||
'joined_at': instance.joinedAt.toIso8601String(),
|
'joined_at': instance.joinedAt.toIso8601String(),
|
||||||
'is_local': instance.isLocal,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) =>
|
_SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) =>
|
||||||
|
@@ -6,7 +6,7 @@ part of 'call.dart';
|
|||||||
// RiverpodGenerator
|
// RiverpodGenerator
|
||||||
// **************************************************************************
|
// **************************************************************************
|
||||||
|
|
||||||
String _$callNotifierHash() => r'6db330370d473eaea313dd9f9439d261c355095f';
|
String _$callNotifierHash() => r'4015d326388553c46859fe537e84d2c9da4236c9';
|
||||||
|
|
||||||
/// See also [CallNotifier].
|
/// See also [CallNotifier].
|
||||||
@ProviderFor(CallNotifier)
|
@ProviderFor(CallNotifier)
|
||||||
|
@@ -3,6 +3,7 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|||||||
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
import 'package:flutter_webrtc/flutter_webrtc.dart';
|
||||||
import 'package:island/models/account.dart';
|
import 'package:island/models/account.dart';
|
||||||
import 'package:island/pods/chat/webrtc_signaling.dart';
|
import 'package:island/pods/chat/webrtc_signaling.dart';
|
||||||
|
import 'package:island/pods/userinfo.dart';
|
||||||
import 'package:island/talker.dart';
|
import 'package:island/talker.dart';
|
||||||
|
|
||||||
class WebRTCParticipant {
|
class WebRTCParticipant {
|
||||||
@@ -11,6 +12,7 @@ class WebRTCParticipant {
|
|||||||
final SnAccount userinfo;
|
final SnAccount userinfo;
|
||||||
RTCPeerConnection? peerConnection;
|
RTCPeerConnection? peerConnection;
|
||||||
MediaStream? remoteStream;
|
MediaStream? remoteStream;
|
||||||
|
List<RTCIceCandidate> remoteCandidates = [];
|
||||||
bool isAudioEnabled = true;
|
bool isAudioEnabled = true;
|
||||||
bool isVideoEnabled = false;
|
bool isVideoEnabled = false;
|
||||||
bool isConnected = false;
|
bool isConnected = false;
|
||||||
@@ -20,6 +22,8 @@ class WebRTCParticipant {
|
|||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.userinfo,
|
required this.userinfo,
|
||||||
|
this.isAudioEnabled = true,
|
||||||
|
this.isVideoEnabled = false,
|
||||||
this.isLocal = false,
|
this.isLocal = false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -48,6 +52,10 @@ class WebRTCManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initialize(Ref ref) async {
|
Future<void> initialize(Ref ref) async {
|
||||||
|
final user = ref.watch(userInfoProvider).value!;
|
||||||
|
_signaling.userId = user.id;
|
||||||
|
_signaling.userName = user.name;
|
||||||
|
_signaling.user = user;
|
||||||
await _initializeLocalStream();
|
await _initializeLocalStream();
|
||||||
_setupSignalingListeners();
|
_setupSignalingListeners();
|
||||||
await _signaling.connect(ref);
|
await _signaling.connect(ref);
|
||||||
@@ -60,6 +68,19 @@ class WebRTCManager {
|
|||||||
'video': true,
|
'video': true,
|
||||||
});
|
});
|
||||||
talker.info('[WebRTC] Local stream initialized');
|
talker.info('[WebRTC] Local stream initialized');
|
||||||
|
|
||||||
|
// Add local participant
|
||||||
|
bool videoEnabled = _localStream!.getVideoTracks().isNotEmpty;
|
||||||
|
WebRTCParticipant localParticipant = WebRTCParticipant(
|
||||||
|
id: _signaling.userId,
|
||||||
|
name: _signaling.userName,
|
||||||
|
userinfo: _signaling.user,
|
||||||
|
isLocal: true,
|
||||||
|
isAudioEnabled: true,
|
||||||
|
isVideoEnabled: videoEnabled,
|
||||||
|
);
|
||||||
|
_participants[_signaling.userId] = localParticipant;
|
||||||
|
_participantController.add(localParticipant);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
talker.error('[WebRTC] Failed to initialize local stream: $e');
|
talker.error('[WebRTC] Failed to initialize local stream: $e');
|
||||||
rethrow;
|
rethrow;
|
||||||
@@ -156,6 +177,7 @@ class WebRTCManager {
|
|||||||
|
|
||||||
final peerConnection = await createPeerConnection(configuration);
|
final peerConnection = await createPeerConnection(configuration);
|
||||||
_peerConnections[participantId] = peerConnection;
|
_peerConnections[participantId] = peerConnection;
|
||||||
|
_participants[participantId]!.peerConnection = peerConnection;
|
||||||
|
|
||||||
if (_localStream != null) {
|
if (_localStream != null) {
|
||||||
for (final track in _localStream!.getTracks()) {
|
for (final track in _localStream!.getTracks()) {
|
||||||
@@ -233,6 +255,15 @@ class WebRTCManager {
|
|||||||
await peerConnection.setLocalDescription(answer);
|
await peerConnection.setLocalDescription(answer);
|
||||||
// CHANGED: Send answer to the specific participant
|
// CHANGED: Send answer to the specific participant
|
||||||
_signaling.sendAnswer(participantId, answer);
|
_signaling.sendAnswer(participantId, answer);
|
||||||
|
|
||||||
|
// Process any queued ICE candidates
|
||||||
|
final participant = _participants[participantId];
|
||||||
|
if (participant != null) {
|
||||||
|
for (final candidate in participant.remoteCandidates) {
|
||||||
|
await peerConnection.addCandidate(candidate);
|
||||||
|
}
|
||||||
|
participant.remoteCandidates.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _handleAnswer(String from, Map<String, dynamic> data) async {
|
Future<void> _handleAnswer(String from, Map<String, dynamic> data) async {
|
||||||
@@ -243,6 +274,15 @@ class WebRTCManager {
|
|||||||
final peerConnection = _peerConnections[participantId];
|
final peerConnection = _peerConnections[participantId];
|
||||||
if (peerConnection != null) {
|
if (peerConnection != null) {
|
||||||
await peerConnection.setRemoteDescription(answer);
|
await peerConnection.setRemoteDescription(answer);
|
||||||
|
|
||||||
|
// Process any queued ICE candidates
|
||||||
|
final participant = _participants[participantId];
|
||||||
|
if (participant != null) {
|
||||||
|
for (final candidate in participant.remoteCandidates) {
|
||||||
|
await peerConnection.addCandidate(candidate);
|
||||||
|
}
|
||||||
|
participant.remoteCandidates.clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,11 +297,14 @@ class WebRTCManager {
|
|||||||
data['sdpMLineIndex'],
|
data['sdpMLineIndex'],
|
||||||
);
|
);
|
||||||
|
|
||||||
final peerConnection = _peerConnections[participantId];
|
final participant = _participants[participantId];
|
||||||
if (peerConnection != null) {
|
if (participant != null) {
|
||||||
// It's possible for candidates to arrive before the remote description is set.
|
final pc = participant.peerConnection;
|
||||||
// A robust implementation might queue them, but for now, we'll just add them.
|
if (pc != null) {
|
||||||
await peerConnection.addCandidate(candidate);
|
await pc.addCandidate(candidate);
|
||||||
|
} else {
|
||||||
|
participant.remoteCandidates.add(candidate);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,7 +316,7 @@ class WebRTCManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_participants.values.where((e) => e.isLocal).firstOrNull?.isAudioEnabled = true;
|
_participants[_signaling.userId]?.isAudioEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> toggleCamera(bool enabled) async {
|
Future<void> toggleCamera(bool enabled) async {
|
||||||
@@ -283,7 +326,7 @@ class WebRTCManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_participants.values.where((e) => e.isLocal).firstOrNull?.isVideoEnabled = true;
|
_participants[_signaling.userId]?.isVideoEnabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<WebRTCParticipant> get participants => _participants.values.toList();
|
List<WebRTCParticipant> get participants => _participants.values.toList();
|
||||||
@@ -294,6 +337,7 @@ class WebRTCManager {
|
|||||||
pc.close();
|
pc.close();
|
||||||
}
|
}
|
||||||
_peerConnections.clear();
|
_peerConnections.clear();
|
||||||
|
_participants.values.forEach((p) => p.remoteCandidates.clear());
|
||||||
_participants.clear();
|
_participants.clear();
|
||||||
_localStream?.dispose();
|
_localStream?.dispose();
|
||||||
_participantController.close();
|
_participantController.close();
|
||||||
|
@@ -8,7 +8,6 @@ import 'package:island/models/account.dart';
|
|||||||
import 'package:island/models/chat.dart';
|
import 'package:island/models/chat.dart';
|
||||||
import 'package:island/pods/config.dart';
|
import 'package:island/pods/config.dart';
|
||||||
import 'package:island/pods/network.dart';
|
import 'package:island/pods/network.dart';
|
||||||
import 'package:island/pods/userinfo.dart';
|
|
||||||
import 'package:island/pods/websocket.dart';
|
import 'package:island/pods/websocket.dart';
|
||||||
import 'package:web_socket_channel/io.dart';
|
import 'package:web_socket_channel/io.dart';
|
||||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||||
@@ -51,7 +50,7 @@ class WebRTCSignaling {
|
|||||||
final String roomId;
|
final String roomId;
|
||||||
late final String userId;
|
late final String userId;
|
||||||
late final String userName;
|
late final String userName;
|
||||||
late final SnAccount user;
|
late SnAccount user;
|
||||||
final StreamController<SignalingMessage> _messageController =
|
final StreamController<SignalingMessage> _messageController =
|
||||||
StreamController<SignalingMessage>.broadcast();
|
StreamController<SignalingMessage>.broadcast();
|
||||||
final StreamController<WebRTCWelcomeMessage> _welcomeController =
|
final StreamController<WebRTCWelcomeMessage> _welcomeController =
|
||||||
@@ -65,13 +64,9 @@ class WebRTCSignaling {
|
|||||||
WebRTCSignaling({required this.roomId});
|
WebRTCSignaling({required this.roomId});
|
||||||
|
|
||||||
Future<void> connect(Ref ref) async {
|
Future<void> connect(Ref ref) async {
|
||||||
user = ref.watch(userInfoProvider).value!;
|
|
||||||
final baseUrl = ref.watch(serverUrlProvider);
|
final baseUrl = ref.watch(serverUrlProvider);
|
||||||
final token = await getToken(ref.watch(tokenProvider));
|
final token = await getToken(ref.watch(tokenProvider));
|
||||||
|
|
||||||
userId = user.id;
|
|
||||||
userName = user.name;
|
|
||||||
|
|
||||||
final url = '$baseUrl/sphere/chat/realtime/$roomId'.replaceFirst(
|
final url = '$baseUrl/sphere/chat/realtime/$roomId'.replaceFirst(
|
||||||
'http',
|
'http',
|
||||||
'ws',
|
'ws',
|
||||||
|
Reference in New Issue
Block a user