diff --git a/lib/models/chat.dart b/lib/models/chat.dart index 7b37effc..34b22a20 100644 --- a/lib/models/chat.dart +++ b/lib/models/chat.dart @@ -152,6 +152,7 @@ sealed class CallParticipant with _$CallParticipant { required String accountId, @Default(null) SnAccount? account, required DateTime joinedAt, + @Default(false) bool isLocal, }) = _CallParticipant; factory CallParticipant.fromJson(Map json) => diff --git a/lib/models/chat.freezed.dart b/lib/models/chat.freezed.dart index 378bcc03..646c8e49 100644 --- a/lib/models/chat.freezed.dart +++ b/lib/models/chat.freezed.dart @@ -2241,7 +2241,7 @@ as List, /// @nodoc mixin _$CallParticipant { - String get identity; String get name; String get accountId; SnAccount? get account; DateTime get joinedAt; + String get identity; String get name; String get accountId; SnAccount? get account; DateTime get joinedAt; bool get isLocal; /// Create a copy of CallParticipant /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -2254,16 +2254,16 @@ $CallParticipantCopyWith get copyWith => _$CallParticipantCopyW @override 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)); + 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)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt); +int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt,isLocal); @override String toString() { - return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt)'; + return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt, isLocal: $isLocal)'; } @@ -2274,7 +2274,7 @@ abstract mixin class $CallParticipantCopyWith<$Res> { factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl; @useResult $Res call({ - String identity, String name, String accountId, SnAccount? account, DateTime joinedAt + String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal }); @@ -2291,14 +2291,15 @@ class _$CallParticipantCopyWithImpl<$Res> /// Create a copy of CallParticipant /// 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,}) { +@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,}) { return _then(_self.copyWith( 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,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 SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable -as DateTime, +as DateTime,isLocal: null == isLocal ? _self.isLocal : isLocal // ignore: cast_nullable_to_non_nullable +as bool, )); } /// Create a copy of CallParticipant @@ -2392,10 +2393,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _CallParticipant() when $default != null: -return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);case _: +return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);case _: return orElse(); } @@ -2413,10 +2414,10 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal) $default,) {final _that = this; switch (_that) { case _CallParticipant(): -return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);} +return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);} } /// A variant of `when` that fallback to returning `null` /// @@ -2430,10 +2431,10 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal)? $default,) {final _that = this; switch (_that) { case _CallParticipant() when $default != null: -return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt);case _: +return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.joinedAt,_that.isLocal);case _: return null; } @@ -2445,7 +2446,7 @@ return $default(_that.identity,_that.name,_that.accountId,_that.account,_that.jo @JsonSerializable() class _CallParticipant implements CallParticipant { - const _CallParticipant({required this.identity, required this.name, required this.accountId, this.account = null, required this.joinedAt}); + const _CallParticipant({required this.identity, required this.name, required this.accountId, this.account = null, required this.joinedAt, this.isLocal = false}); factory _CallParticipant.fromJson(Map json) => _$CallParticipantFromJson(json); @override final String identity; @@ -2453,6 +2454,7 @@ class _CallParticipant implements CallParticipant { @override final String accountId; @override@JsonKey() final SnAccount? account; @override final DateTime joinedAt; +@override@JsonKey() final bool isLocal; /// Create a copy of CallParticipant /// with the given fields replaced by the non-null parameter values. @@ -2467,16 +2469,16 @@ Map toJson() { @override 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)); + 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)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt); +int get hashCode => Object.hash(runtimeType,identity,name,accountId,account,joinedAt,isLocal); @override String toString() { - return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt)'; + return 'CallParticipant(identity: $identity, name: $name, accountId: $accountId, account: $account, joinedAt: $joinedAt, isLocal: $isLocal)'; } @@ -2487,7 +2489,7 @@ abstract mixin class _$CallParticipantCopyWith<$Res> implements $CallParticipant factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl; @override @useResult $Res call({ - String identity, String name, String accountId, SnAccount? account, DateTime joinedAt + String identity, String name, String accountId, SnAccount? account, DateTime joinedAt, bool isLocal }); @@ -2504,14 +2506,15 @@ class __$CallParticipantCopyWithImpl<$Res> /// Create a copy of CallParticipant /// 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,}) { +@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,}) { return _then(_CallParticipant( 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,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 SnAccount?,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable -as DateTime, +as DateTime,isLocal: null == isLocal ? _self.isLocal : isLocal // ignore: cast_nullable_to_non_nullable +as bool, )); } diff --git a/lib/models/chat.g.dart b/lib/models/chat.g.dart index 2b559957..39438c1a 100644 --- a/lib/models/chat.g.dart +++ b/lib/models/chat.g.dart @@ -281,6 +281,7 @@ _CallParticipant _$CallParticipantFromJson(Map json) => ? null : SnAccount.fromJson(json['account'] as Map), joinedAt: DateTime.parse(json['joined_at'] as String), + isLocal: json['is_local'] as bool? ?? false, ); Map _$CallParticipantToJson(_CallParticipant instance) => @@ -290,6 +291,7 @@ Map _$CallParticipantToJson(_CallParticipant instance) => 'account_id': instance.accountId, 'account': instance.account?.toJson(), 'joined_at': instance.joinedAt.toIso8601String(), + 'is_local': instance.isLocal, }; _SnRealtimeCall _$SnRealtimeCallFromJson(Map json) => diff --git a/lib/pods/chat/call.dart b/lib/pods/chat/call.dart index 10992875..88944ecd 100644 --- a/lib/pods/chat/call.dart +++ b/lib/pods/chat/call.dart @@ -178,6 +178,7 @@ class CallNotifier extends _$CallNotifier { accountId: p.userinfo.id, account: p.userinfo, joinedAt: DateTime.now(), + isLocal: true, ); return CallParticipantLive( participant: participantInfo, diff --git a/lib/pods/chat/call.g.dart b/lib/pods/chat/call.g.dart index a5c0ec1f..e22eca71 100644 --- a/lib/pods/chat/call.g.dart +++ b/lib/pods/chat/call.g.dart @@ -6,7 +6,7 @@ part of 'call.dart'; // RiverpodGenerator // ************************************************************************** -String _$callNotifierHash() => r'91e546c8711d1b46740ad592cbe481173b227e7b'; +String _$callNotifierHash() => r'6db330370d473eaea313dd9f9439d261c355095f'; /// See also [CallNotifier]. @ProviderFor(CallNotifier) diff --git a/lib/pods/chat/webrtc_signaling.dart b/lib/pods/chat/webrtc_signaling.dart index 4379de4f..46f3e44a 100644 --- a/lib/pods/chat/webrtc_signaling.dart +++ b/lib/pods/chat/webrtc_signaling.dart @@ -57,6 +57,7 @@ class WebRTCSignaling { final StreamController _welcomeController = StreamController.broadcast(); WebSocketChannel? _channel; + Timer? _heartbeatTimer; Stream get messages => _messageController.stream; Stream get welcomeMessages => _welcomeController.stream; @@ -88,6 +89,9 @@ class WebRTCSignaling { } await _channel!.ready; + // Start heartbeat timer + _heartbeatTimer = Timer.periodic(const Duration(seconds: 30), (timer) => _sendHeartbeat()); + _channel!.stream.listen( (data) { final dataStr = @@ -196,7 +200,15 @@ class WebRTCSignaling { ); } + void _sendHeartbeat() { + if (_channel == null) return; + talker.info('[WebRTC Signaling] Sending heartbeat'); + final packet = WebSocketPacket(type: 'heartbeat', data: null); + _channel!.sink.add(jsonEncode(packet.toJson())); + } + void disconnect() { + _heartbeatTimer?.cancel(); _channel?.sink.close(); _messageController.close(); _welcomeController.close(); diff --git a/setup.iss b/setup.iss index 2510baf8..3bcac580 100644 --- a/setup.iss +++ b/setup.iss @@ -1,6 +1,6 @@ ; ================================================== -#define AppVersion "3.2.0" -#define BuildNumber "134" +#define AppVersion "3.3.0" +#define BuildNumber "136" ; ================================================== #define FullVersion AppVersion + "." + BuildNumber