🧱 Realtime call infra

This commit is contained in:
LittleSheep 2025-05-25 17:40:52 +08:00
parent 9abc61a310
commit edf4ff1c5b
30 changed files with 1454 additions and 563 deletions

View File

@ -1,4 +1,6 @@
PODS:
- connectivity_plus (0.0.1):
- Flutter
- croppy (0.0.1):
- Flutter
- device_info_plus (0.0.1):
@ -84,6 +86,9 @@ PODS:
- flutter_udid (0.0.1):
- Flutter
- SAMKeychain
- flutter_webrtc (0.14.0):
- Flutter
- WebRTC-SDK (= 125.6422.07)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
- PromisesObjC (~> 2.4)
@ -116,6 +121,10 @@ PODS:
- irondash_engine_context (0.0.1):
- Flutter
- Kingfisher (8.3.2)
- livekit_client (2.4.7):
- Flutter
- flutter_webrtc
- WebRTC-SDK (= 125.6422.07)
- media_kit_libs_ios_video (1.0.4):
- Flutter
- media_kit_video (0.0.1):
@ -173,8 +182,10 @@ PODS:
- Flutter
- wakelock_plus (0.0.1):
- Flutter
- WebRTC-SDK (125.6422.07)
DEPENDENCIES:
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- croppy (from `.symlinks/plugins/croppy/ios`)
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
- file_picker (from `.symlinks/plugins/file_picker/ios`)
@ -185,9 +196,11 @@ DEPENDENCIES:
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_platform_alert (from `.symlinks/plugins/flutter_platform_alert/ios`)
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
- image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`)
- irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
- Kingfisher (~> 8.0)
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
@ -219,8 +232,11 @@ SPEC REPOS:
- SDWebImage
- sqlite3
- SwiftyGif
- WebRTC-SDK
EXTERNAL SOURCES:
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
croppy:
:path: ".symlinks/plugins/croppy/ios"
device_info_plus:
@ -241,10 +257,14 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_platform_alert/ios"
flutter_udid:
:path: ".symlinks/plugins/flutter_udid/ios"
flutter_webrtc:
:path: ".symlinks/plugins/flutter_webrtc/ios"
image_picker_ios:
:path: ".symlinks/plugins/image_picker_ios/ios"
irondash_engine_context:
:path: ".symlinks/plugins/irondash_engine_context/ios"
livekit_client:
:path: ".symlinks/plugins/livekit_client/ios"
media_kit_libs_ios_video:
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
media_kit_video:
@ -269,6 +289,7 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/wakelock_plus/ios"
SPEC CHECKSUMS:
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
croppy: 979e8ddc254f4642bffe7d52dc7193354b27ba30
device_info_plus: 21fcca2080fbcd348be798aa36c3e5ed849eefbe
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
@ -286,11 +307,13 @@ SPEC CHECKSUMS:
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
flutter_platform_alert: bf3b5fcd4ac14bd637e20527e9c471633071afd3
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
flutter_webrtc: fd0d3bdef8766a0736dbbe2e5b7e85f1f3c52117
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
image_picker_ios: 7fe1ff8e34c1790d6fff70a32484959f563a928a
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
Kingfisher: 0621d0ac0c78fecb19f6dc5303bde2b52abaf2f5
livekit_client: c30950bf36aa4c0244dd5551b1818cd15f90ba32
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
@ -309,6 +332,7 @@ SPEC CHECKSUMS:
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
volume_controller: 3657a1f65bedb98fa41ff7dc5793537919f31b12
wakelock_plus: e29112ab3ef0b318e58cfa5c32326458be66b556
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e
PODFILE CHECKSUM: 4c3fad73fbbc9053a824d880097bda7884240991

View File

@ -26,6 +26,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
@ -54,6 +55,7 @@
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"

View File

@ -37,6 +37,7 @@ sealed class SnChatMessage with _$SnChatMessage {
required DateTime updatedAt,
DateTime? deletedAt,
required String id,
@Default('text') String type,
String? content,
String? nonce,
@Default({}) Map<String, dynamic> meta,
@ -139,10 +140,35 @@ sealed class MessageSyncResponse with _$MessageSyncResponse {
@freezed
sealed class ChatRealtimeJoinResponse with _$ChatRealtimeJoinResponse {
const factory ChatRealtimeJoinResponse({
required String provider,
required String endpoint,
required String token,
required Map<String, dynamic> config,
required String callId,
required String roomName,
required bool isAdmin,
}) = _ChatRealtimeJoinResponse;
factory ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) =>
_$ChatRealtimeJoinResponseFromJson(json);
}
@freezed
sealed class SnRealtimeCall with _$SnRealtimeCall {
const factory SnRealtimeCall({
required String id,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,
required DateTime? endedAt,
required String senderId,
required SnChatMember sender,
required String roomId,
required SnChatRoom room,
required Map<String, dynamic> upstreamConfig,
String? providerName,
String? sessionId,
}) = _SnRealtimeCall;
factory SnRealtimeCall.fromJson(Map<String, dynamic> json) =>
_$SnRealtimeCallFromJson(json);
}

View File

@ -271,7 +271,7 @@ $SnRealmCopyWith<$Res>? get realm {
/// @nodoc
mixin _$SnChatMessage {
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String? get content; String? get nonce; Map<String, dynamic> get meta; List<String> get membersMetioned; DateTime? get editedAt; List<SnCloudFile> get attachments; List<SnChatReaction> get reactions; String? get repliedMessageId; String? get forwardedMessageId; String get senderId; SnChatMember get sender; String get chatRoomId;
DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; String get id; String get type; String? get content; String? get nonce; Map<String, dynamic> get meta; List<String> get membersMetioned; DateTime? get editedAt; List<SnCloudFile> get attachments; List<SnChatReaction> get reactions; String? get repliedMessageId; String? get forwardedMessageId; String get senderId; SnChatMember get sender; String get chatRoomId;
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -284,16 +284,16 @@ $SnChatMessageCopyWith<SnChatMessage> get copyWith => _$SnChatMessageCopyWithImp
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other.meta, meta)&&const DeepCollectionEquality().equals(other.membersMetioned, membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other.meta, meta)&&const DeepCollectionEquality().equals(other.membersMetioned, membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other.attachments, attachments)&&const DeepCollectionEquality().equals(other.reactions, reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,content,nonce,const DeepCollectionEquality().hash(meta),const DeepCollectionEquality().hash(membersMetioned),editedAt,const DeepCollectionEquality().hash(attachments),const DeepCollectionEquality().hash(reactions),repliedMessageId,forwardedMessageId,senderId,sender,chatRoomId);
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,type,content,nonce,const DeepCollectionEquality().hash(meta),const DeepCollectionEquality().hash(membersMetioned),editedAt,const DeepCollectionEquality().hash(attachments),const DeepCollectionEquality().hash(reactions),repliedMessageId,forwardedMessageId,senderId,sender,chatRoomId);
@override
String toString() {
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, forwardedMessageId: $forwardedMessageId, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, type: $type, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, forwardedMessageId: $forwardedMessageId, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
}
@ -304,7 +304,7 @@ abstract mixin class $SnChatMessageCopyWith<$Res> {
factory $SnChatMessageCopyWith(SnChatMessage value, $Res Function(SnChatMessage) _then) = _$SnChatMessageCopyWithImpl;
@useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, String? forwardedMessageId, String senderId, SnChatMember sender, String chatRoomId
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String type, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, String? forwardedMessageId, String senderId, SnChatMember sender, String chatRoomId
});
@ -321,12 +321,13 @@ class _$SnChatMessageCopyWithImpl<$Res>
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? forwardedMessageId = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? type = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? forwardedMessageId = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
return _then(_self.copyWith(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String?,meta: null == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
@ -359,13 +360,14 @@ $SnChatMemberCopyWith<$Res> get sender {
@JsonSerializable()
class _SnChatMessage implements SnChatMessage {
const _SnChatMessage({required this.createdAt, required this.updatedAt, this.deletedAt, required this.id, this.content, this.nonce, final Map<String, dynamic> meta = const {}, final List<String> membersMetioned = const [], this.editedAt, final List<SnCloudFile> attachments = const [], final List<SnChatReaction> reactions = const [], this.repliedMessageId, this.forwardedMessageId, required this.senderId, required this.sender, required this.chatRoomId}): _meta = meta,_membersMetioned = membersMetioned,_attachments = attachments,_reactions = reactions;
const _SnChatMessage({required this.createdAt, required this.updatedAt, this.deletedAt, required this.id, this.type = 'text', this.content, this.nonce, final Map<String, dynamic> meta = const {}, final List<String> membersMetioned = const [], this.editedAt, final List<SnCloudFile> attachments = const [], final List<SnChatReaction> reactions = const [], this.repliedMessageId, this.forwardedMessageId, required this.senderId, required this.sender, required this.chatRoomId}): _meta = meta,_membersMetioned = membersMetioned,_attachments = attachments,_reactions = reactions;
factory _SnChatMessage.fromJson(Map<String, dynamic> json) => _$SnChatMessageFromJson(json);
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final String id;
@override@JsonKey() final String type;
@override final String? content;
@override final String? nonce;
final Map<String, dynamic> _meta;
@ -416,16 +418,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other._meta, _meta)&&const DeepCollectionEquality().equals(other._membersMetioned, _membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnChatMessage&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.id, id) || other.id == id)&&(identical(other.type, type) || other.type == type)&&(identical(other.content, content) || other.content == content)&&(identical(other.nonce, nonce) || other.nonce == nonce)&&const DeepCollectionEquality().equals(other._meta, _meta)&&const DeepCollectionEquality().equals(other._membersMetioned, _membersMetioned)&&(identical(other.editedAt, editedAt) || other.editedAt == editedAt)&&const DeepCollectionEquality().equals(other._attachments, _attachments)&&const DeepCollectionEquality().equals(other._reactions, _reactions)&&(identical(other.repliedMessageId, repliedMessageId) || other.repliedMessageId == repliedMessageId)&&(identical(other.forwardedMessageId, forwardedMessageId) || other.forwardedMessageId == forwardedMessageId)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.chatRoomId, chatRoomId) || other.chatRoomId == chatRoomId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,content,nonce,const DeepCollectionEquality().hash(_meta),const DeepCollectionEquality().hash(_membersMetioned),editedAt,const DeepCollectionEquality().hash(_attachments),const DeepCollectionEquality().hash(_reactions),repliedMessageId,forwardedMessageId,senderId,sender,chatRoomId);
int get hashCode => Object.hash(runtimeType,createdAt,updatedAt,deletedAt,id,type,content,nonce,const DeepCollectionEquality().hash(_meta),const DeepCollectionEquality().hash(_membersMetioned),editedAt,const DeepCollectionEquality().hash(_attachments),const DeepCollectionEquality().hash(_reactions),repliedMessageId,forwardedMessageId,senderId,sender,chatRoomId);
@override
String toString() {
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, forwardedMessageId: $forwardedMessageId, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
return 'SnChatMessage(createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, id: $id, type: $type, content: $content, nonce: $nonce, meta: $meta, membersMetioned: $membersMetioned, editedAt: $editedAt, attachments: $attachments, reactions: $reactions, repliedMessageId: $repliedMessageId, forwardedMessageId: $forwardedMessageId, senderId: $senderId, sender: $sender, chatRoomId: $chatRoomId)';
}
@ -436,7 +438,7 @@ abstract mixin class _$SnChatMessageCopyWith<$Res> implements $SnChatMessageCopy
factory _$SnChatMessageCopyWith(_SnChatMessage value, $Res Function(_SnChatMessage) _then) = __$SnChatMessageCopyWithImpl;
@override @useResult
$Res call({
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, String? forwardedMessageId, String senderId, SnChatMember sender, String chatRoomId
DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, String id, String type, String? content, String? nonce, Map<String, dynamic> meta, List<String> membersMetioned, DateTime? editedAt, List<SnCloudFile> attachments, List<SnChatReaction> reactions, String? repliedMessageId, String? forwardedMessageId, String senderId, SnChatMember sender, String chatRoomId
});
@ -453,12 +455,13 @@ class __$SnChatMessageCopyWithImpl<$Res>
/// Create a copy of SnChatMessage
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? forwardedMessageId = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? id = null,Object? type = null,Object? content = freezed,Object? nonce = freezed,Object? meta = null,Object? membersMetioned = null,Object? editedAt = freezed,Object? attachments = null,Object? reactions = null,Object? repliedMessageId = freezed,Object? forwardedMessageId = freezed,Object? senderId = null,Object? sender = null,Object? chatRoomId = null,}) {
return _then(_SnChatMessage(
createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable
as String,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable
as String?,nonce: freezed == nonce ? _self.nonce : nonce // ignore: cast_nullable_to_non_nullable
as String?,meta: null == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
@ -1339,7 +1342,7 @@ as DateTime,
/// @nodoc
mixin _$ChatRealtimeJoinResponse {
String get token; Map<String, dynamic> get config;
String get provider; String get endpoint; String get token; String get callId; String get roomName; bool get isAdmin;
/// Create a copy of ChatRealtimeJoinResponse
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -1352,16 +1355,16 @@ $ChatRealtimeJoinResponseCopyWith<ChatRealtimeJoinResponse> get copyWith => _$Ch
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChatRealtimeJoinResponse&&(identical(other.token, token) || other.token == token)&&const DeepCollectionEquality().equals(other.config, config));
return identical(this, other) || (other.runtimeType == runtimeType&&other is ChatRealtimeJoinResponse&&(identical(other.provider, provider) || other.provider == provider)&&(identical(other.endpoint, endpoint) || other.endpoint == endpoint)&&(identical(other.token, token) || other.token == token)&&(identical(other.callId, callId) || other.callId == callId)&&(identical(other.roomName, roomName) || other.roomName == roomName)&&(identical(other.isAdmin, isAdmin) || other.isAdmin == isAdmin));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,token,const DeepCollectionEquality().hash(config));
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin);
@override
String toString() {
return 'ChatRealtimeJoinResponse(token: $token, config: $config)';
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin)';
}
@ -1372,7 +1375,7 @@ abstract mixin class $ChatRealtimeJoinResponseCopyWith<$Res> {
factory $ChatRealtimeJoinResponseCopyWith(ChatRealtimeJoinResponse value, $Res Function(ChatRealtimeJoinResponse) _then) = _$ChatRealtimeJoinResponseCopyWithImpl;
@useResult
$Res call({
String token, Map<String, dynamic> config
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin
});
@ -1389,11 +1392,15 @@ class _$ChatRealtimeJoinResponseCopyWithImpl<$Res>
/// Create a copy of ChatRealtimeJoinResponse
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? token = null,Object? config = null,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? provider = null,Object? endpoint = null,Object? token = null,Object? callId = null,Object? roomName = null,Object? isAdmin = null,}) {
return _then(_self.copyWith(
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,config: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
provider: null == provider ? _self.provider : provider // ignore: cast_nullable_to_non_nullable
as String,endpoint: null == endpoint ? _self.endpoint : endpoint // ignore: cast_nullable_to_non_nullable
as String,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,callId: null == callId ? _self.callId : callId // ignore: cast_nullable_to_non_nullable
as String,roomName: null == roomName ? _self.roomName : roomName // ignore: cast_nullable_to_non_nullable
as String,isAdmin: null == isAdmin ? _self.isAdmin : isAdmin // ignore: cast_nullable_to_non_nullable
as bool,
));
}
@ -1404,17 +1411,15 @@ as Map<String, dynamic>,
@JsonSerializable()
class _ChatRealtimeJoinResponse implements ChatRealtimeJoinResponse {
const _ChatRealtimeJoinResponse({required this.token, required final Map<String, dynamic> config}): _config = config;
const _ChatRealtimeJoinResponse({required this.provider, required this.endpoint, required this.token, required this.callId, required this.roomName, required this.isAdmin});
factory _ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) => _$ChatRealtimeJoinResponseFromJson(json);
@override final String provider;
@override final String endpoint;
@override final String token;
final Map<String, dynamic> _config;
@override Map<String, dynamic> get config {
if (_config is EqualUnmodifiableMapView) return _config;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_config);
}
@override final String callId;
@override final String roomName;
@override final bool isAdmin;
/// Create a copy of ChatRealtimeJoinResponse
/// with the given fields replaced by the non-null parameter values.
@ -1429,16 +1434,16 @@ Map<String, dynamic> toJson() {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChatRealtimeJoinResponse&&(identical(other.token, token) || other.token == token)&&const DeepCollectionEquality().equals(other._config, _config));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChatRealtimeJoinResponse&&(identical(other.provider, provider) || other.provider == provider)&&(identical(other.endpoint, endpoint) || other.endpoint == endpoint)&&(identical(other.token, token) || other.token == token)&&(identical(other.callId, callId) || other.callId == callId)&&(identical(other.roomName, roomName) || other.roomName == roomName)&&(identical(other.isAdmin, isAdmin) || other.isAdmin == isAdmin));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,token,const DeepCollectionEquality().hash(_config));
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin);
@override
String toString() {
return 'ChatRealtimeJoinResponse(token: $token, config: $config)';
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin)';
}
@ -1449,7 +1454,7 @@ abstract mixin class _$ChatRealtimeJoinResponseCopyWith<$Res> implements $ChatRe
factory _$ChatRealtimeJoinResponseCopyWith(_ChatRealtimeJoinResponse value, $Res Function(_ChatRealtimeJoinResponse) _then) = __$ChatRealtimeJoinResponseCopyWithImpl;
@override @useResult
$Res call({
String token, Map<String, dynamic> config
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin
});
@ -1466,15 +1471,227 @@ class __$ChatRealtimeJoinResponseCopyWithImpl<$Res>
/// Create a copy of ChatRealtimeJoinResponse
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? token = null,Object? config = null,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? provider = null,Object? endpoint = null,Object? token = null,Object? callId = null,Object? roomName = null,Object? isAdmin = null,}) {
return _then(_ChatRealtimeJoinResponse(
token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,config: null == config ? _self._config : config // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,
provider: null == provider ? _self.provider : provider // ignore: cast_nullable_to_non_nullable
as String,endpoint: null == endpoint ? _self.endpoint : endpoint // ignore: cast_nullable_to_non_nullable
as String,token: null == token ? _self.token : token // ignore: cast_nullable_to_non_nullable
as String,callId: null == callId ? _self.callId : callId // ignore: cast_nullable_to_non_nullable
as String,roomName: null == roomName ? _self.roomName : roomName // ignore: cast_nullable_to_non_nullable
as String,isAdmin: null == isAdmin ? _self.isAdmin : isAdmin // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
/// @nodoc
mixin _$SnRealtimeCall {
String get id; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; DateTime? get endedAt; String get senderId; SnChatMember get sender; String get roomId; SnChatRoom get room; Map<String, dynamic> get upstreamConfig; String? get providerName; String? get sessionId;
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$SnRealtimeCallCopyWith<SnRealtimeCall> get copyWith => _$SnRealtimeCallCopyWithImpl<SnRealtimeCall>(this as SnRealtimeCall, _$identity);
/// Serializes this SnRealtimeCall to a JSON map.
Map<String, dynamic> toJson();
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnRealtimeCall&&(identical(other.id, id) || other.id == id)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.roomId, roomId) || other.roomId == roomId)&&(identical(other.room, room) || other.room == room)&&const DeepCollectionEquality().equals(other.upstreamConfig, upstreamConfig)&&(identical(other.providerName, providerName) || other.providerName == providerName)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,createdAt,updatedAt,deletedAt,endedAt,senderId,sender,roomId,room,const DeepCollectionEquality().hash(upstreamConfig),providerName,sessionId);
@override
String toString() {
return 'SnRealtimeCall(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, endedAt: $endedAt, senderId: $senderId, sender: $sender, roomId: $roomId, room: $room, upstreamConfig: $upstreamConfig, providerName: $providerName, sessionId: $sessionId)';
}
}
/// @nodoc
abstract mixin class $SnRealtimeCallCopyWith<$Res> {
factory $SnRealtimeCallCopyWith(SnRealtimeCall value, $Res Function(SnRealtimeCall) _then) = _$SnRealtimeCallCopyWithImpl;
@useResult
$Res call({
String id, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, DateTime? endedAt, String senderId, SnChatMember sender, String roomId, SnChatRoom room, Map<String, dynamic> upstreamConfig, String? providerName, String? sessionId
});
$SnChatMemberCopyWith<$Res> get sender;$SnChatRoomCopyWith<$Res> get room;
}
/// @nodoc
class _$SnRealtimeCallCopyWithImpl<$Res>
implements $SnRealtimeCallCopyWith<$Res> {
_$SnRealtimeCallCopyWithImpl(this._self, this._then);
final SnRealtimeCall _self;
final $Res Function(SnRealtimeCall) _then;
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? endedAt = freezed,Object? senderId = null,Object? sender = null,Object? roomId = null,Object? room = null,Object? upstreamConfig = null,Object? providerName = freezed,Object? sessionId = freezed,}) {
return _then(_self.copyWith(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,endedAt: freezed == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,roomId: null == roomId ? _self.roomId : roomId // ignore: cast_nullable_to_non_nullable
as String,room: null == room ? _self.room : room // ignore: cast_nullable_to_non_nullable
as SnChatRoom,upstreamConfig: null == upstreamConfig ? _self.upstreamConfig : upstreamConfig // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,providerName: freezed == providerName ? _self.providerName : providerName // ignore: cast_nullable_to_non_nullable
as String?,sessionId: freezed == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatRoomCopyWith<$Res> get room {
return $SnChatRoomCopyWith<$Res>(_self.room, (value) {
return _then(_self.copyWith(room: value));
});
}
}
/// @nodoc
@JsonSerializable()
class _SnRealtimeCall implements SnRealtimeCall {
const _SnRealtimeCall({required this.id, required this.createdAt, required this.updatedAt, required this.deletedAt, required this.endedAt, required this.senderId, required this.sender, required this.roomId, required this.room, required final Map<String, dynamic> upstreamConfig, this.providerName, this.sessionId}): _upstreamConfig = upstreamConfig;
factory _SnRealtimeCall.fromJson(Map<String, dynamic> json) => _$SnRealtimeCallFromJson(json);
@override final String id;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@override final DateTime? endedAt;
@override final String senderId;
@override final SnChatMember sender;
@override final String roomId;
@override final SnChatRoom room;
final Map<String, dynamic> _upstreamConfig;
@override Map<String, dynamic> get upstreamConfig {
if (_upstreamConfig is EqualUnmodifiableMapView) return _upstreamConfig;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_upstreamConfig);
}
@override final String? providerName;
@override final String? sessionId;
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SnRealtimeCallCopyWith<_SnRealtimeCall> get copyWith => __$SnRealtimeCallCopyWithImpl<_SnRealtimeCall>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$SnRealtimeCallToJson(this, );
}
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnRealtimeCall&&(identical(other.id, id) || other.id == id)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)&&(identical(other.endedAt, endedAt) || other.endedAt == endedAt)&&(identical(other.senderId, senderId) || other.senderId == senderId)&&(identical(other.sender, sender) || other.sender == sender)&&(identical(other.roomId, roomId) || other.roomId == roomId)&&(identical(other.room, room) || other.room == room)&&const DeepCollectionEquality().equals(other._upstreamConfig, _upstreamConfig)&&(identical(other.providerName, providerName) || other.providerName == providerName)&&(identical(other.sessionId, sessionId) || other.sessionId == sessionId));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,id,createdAt,updatedAt,deletedAt,endedAt,senderId,sender,roomId,room,const DeepCollectionEquality().hash(_upstreamConfig),providerName,sessionId);
@override
String toString() {
return 'SnRealtimeCall(id: $id, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt, endedAt: $endedAt, senderId: $senderId, sender: $sender, roomId: $roomId, room: $room, upstreamConfig: $upstreamConfig, providerName: $providerName, sessionId: $sessionId)';
}
}
/// @nodoc
abstract mixin class _$SnRealtimeCallCopyWith<$Res> implements $SnRealtimeCallCopyWith<$Res> {
factory _$SnRealtimeCallCopyWith(_SnRealtimeCall value, $Res Function(_SnRealtimeCall) _then) = __$SnRealtimeCallCopyWithImpl;
@override @useResult
$Res call({
String id, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt, DateTime? endedAt, String senderId, SnChatMember sender, String roomId, SnChatRoom room, Map<String, dynamic> upstreamConfig, String? providerName, String? sessionId
});
@override $SnChatMemberCopyWith<$Res> get sender;@override $SnChatRoomCopyWith<$Res> get room;
}
/// @nodoc
class __$SnRealtimeCallCopyWithImpl<$Res>
implements _$SnRealtimeCallCopyWith<$Res> {
__$SnRealtimeCallCopyWithImpl(this._self, this._then);
final _SnRealtimeCall _self;
final $Res Function(_SnRealtimeCall) _then;
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,Object? endedAt = freezed,Object? senderId = null,Object? sender = null,Object? roomId = null,Object? room = null,Object? upstreamConfig = null,Object? providerName = freezed,Object? sessionId = freezed,}) {
return _then(_SnRealtimeCall(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable
as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,endedAt: freezed == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,senderId: null == senderId ? _self.senderId : senderId // ignore: cast_nullable_to_non_nullable
as String,sender: null == sender ? _self.sender : sender // ignore: cast_nullable_to_non_nullable
as SnChatMember,roomId: null == roomId ? _self.roomId : roomId // ignore: cast_nullable_to_non_nullable
as String,room: null == room ? _self.room : room // ignore: cast_nullable_to_non_nullable
as SnChatRoom,upstreamConfig: null == upstreamConfig ? _self._upstreamConfig : upstreamConfig // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,providerName: freezed == providerName ? _self.providerName : providerName // ignore: cast_nullable_to_non_nullable
as String?,sessionId: freezed == sessionId ? _self.sessionId : sessionId // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatMemberCopyWith<$Res> get sender {
return $SnChatMemberCopyWith<$Res>(_self.sender, (value) {
return _then(_self.copyWith(sender: value));
});
}/// Create a copy of SnRealtimeCall
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$SnChatRoomCopyWith<$Res> get room {
return $SnChatRoomCopyWith<$Res>(_self.room, (value) {
return _then(_self.copyWith(room: value));
});
}
}
// dart format on

View File

@ -67,6 +67,7 @@ _SnChatMessage _$SnChatMessageFromJson(Map<String, dynamic> json) =>
? null
: DateTime.parse(json['deleted_at'] as String),
id: json['id'] as String,
type: json['type'] as String? ?? 'text',
content: json['content'] as String?,
nonce: json['nonce'] as String?,
meta: json['meta'] as Map<String, dynamic>? ?? const {},
@ -102,6 +103,7 @@ Map<String, dynamic> _$SnChatMessageToJson(_SnChatMessage instance) =>
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'id': instance.id,
'type': instance.type,
'content': instance.content,
'nonce': instance.nonce,
'meta': instance.meta,
@ -241,10 +243,59 @@ Map<String, dynamic> _$MessageSyncResponseToJson(
_ChatRealtimeJoinResponse _$ChatRealtimeJoinResponseFromJson(
Map<String, dynamic> json,
) => _ChatRealtimeJoinResponse(
provider: json['provider'] as String,
endpoint: json['endpoint'] as String,
token: json['token'] as String,
config: json['config'] as Map<String, dynamic>,
callId: json['call_id'] as String,
roomName: json['room_name'] as String,
isAdmin: json['is_admin'] as bool,
);
Map<String, dynamic> _$ChatRealtimeJoinResponseToJson(
_ChatRealtimeJoinResponse instance,
) => <String, dynamic>{'token': instance.token, 'config': instance.config};
) => <String, dynamic>{
'provider': instance.provider,
'endpoint': instance.endpoint,
'token': instance.token,
'call_id': instance.callId,
'room_name': instance.roomName,
'is_admin': instance.isAdmin,
};
_SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) =>
_SnRealtimeCall(
id: json['id'] as String,
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
endedAt:
json['ended_at'] == null
? null
: DateTime.parse(json['ended_at'] as String),
senderId: json['sender_id'] as String,
sender: SnChatMember.fromJson(json['sender'] as Map<String, dynamic>),
roomId: json['room_id'] as String,
room: SnChatRoom.fromJson(json['room'] as Map<String, dynamic>),
upstreamConfig: json['upstream_config'] as Map<String, dynamic>,
providerName: json['provider_name'] as String?,
sessionId: json['session_id'] as String?,
);
Map<String, dynamic> _$SnRealtimeCallToJson(_SnRealtimeCall instance) =>
<String, dynamic>{
'id': instance.id,
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'deleted_at': instance.deletedAt?.toIso8601String(),
'ended_at': instance.endedAt?.toIso8601String(),
'sender_id': instance.senderId,
'sender': instance.sender.toJson(),
'room_id': instance.roomId,
'room': instance.room.toJson(),
'upstream_config': instance.upstreamConfig,
'provider_name': instance.providerName,
'session_id': instance.sessionId,
};

View File

@ -1,7 +1,6 @@
import 'package:flutter_webrtc/flutter_webrtc.dart';
import 'package:livekit_client/livekit_client.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:dio/dio.dart';
import 'package:island/pods/network.dart';
part 'call.g.dart';
@ -18,61 +17,40 @@ sealed class CallState with _$CallState {
@riverpod
class CallNotifier extends _$CallNotifier {
RTCPeerConnection? _peerConnection;
MediaStream? _localStream;
final _localRenderer = RTCVideoRenderer();
Room? _room;
LocalParticipant? _localParticipant;
LocalAudioTrack? _localAudioTrack;
@override
CallState build() {
return const CallState(isMuted: false, isConnected: false);
}
Future<void> initialize() async {
try {
await _localRenderer.initialize();
// Get user media (audio)
_localStream = await navigator.mediaDevices.getUserMedia({
'audio': true,
'video': false,
});
// Create peer connection
_peerConnection = await createPeerConnection({
'iceServers': [
{'urls': 'stun:stun.l.google.com:19302'},
// Add your Cloudflare TURN servers here
],
});
// Add local stream to peer connection
_localStream!.getTracks().forEach((track) {
_peerConnection!.addTrack(track, _localStream!);
});
// Handle incoming tracks
_peerConnection!.onTrack = (RTCTrackEvent event) {
if (event.track.kind == 'audio') {
// Handle remote audio track
}
};
state = state.copyWith(isConnected: true);
} catch (e) {
state = state.copyWith(error: e.toString());
}
}
Future<void> createSession() async {
Future<void> joinRoom(String roomId) async {
try {
final apiClient = ref.read(apiClientProvider);
final response = await apiClient.post(
'YOUR_CLOUDFLARE_CALLS_ENDPOINT/sessions',
options: Options(headers: {'Content-Type': 'application/json'}),
);
final response = await apiClient.get('/chat/realtime/$roomId/join');
if (response.statusCode == 200 && response.data != null) {
final data = response.data;
final String endpoint = data['endpoint'];
final String token = data['token'];
// Connect to LiveKit
_room = Room();
await _room!.connect(endpoint, token);
_localParticipant = _room!.localParticipant;
// Create local audio track and publish
_localAudioTrack = await LocalAudioTrack.create();
await _localParticipant!.publishAudioTrack(_localAudioTrack!);
if (response.statusCode == 200) {
// Handle session creation
// Listen for connection updates
_room!.addListener(() {
state = state.copyWith(
isConnected: _room!.connectionState == ConnectionState.connected,
);
});
state = state.copyWith(isConnected: true);
} else {
state = state.copyWith(error: 'Failed to join room');
}
} catch (e) {
state = state.copyWith(error: e.toString());
@ -80,15 +58,26 @@ class CallNotifier extends _$CallNotifier {
}
void toggleMute() {
state = state.copyWith(isMuted: !state.isMuted);
_localStream?.getAudioTracks().forEach((track) {
track.enabled = !state.isMuted;
});
final newMuted = !state.isMuted;
state = state.copyWith(isMuted: newMuted);
if (_localAudioTrack != null) {
if (newMuted) {
_localAudioTrack!.mute();
} else {
_localAudioTrack!.unmute();
}
}
}
Future<void> disconnect() async {
if (_room != null) {
await _room!.disconnect();
state = state.copyWith(isConnected: false);
}
}
void dispose() {
_localStream?.dispose();
_peerConnection?.dispose();
_localRenderer.dispose();
_localAudioTrack?.dispose();
_room?.dispose();
}
}

View File

@ -6,7 +6,7 @@ part of 'call.dart';
// RiverpodGenerator
// **************************************************************************
String _$callNotifierHash() => r'a910dfa778c42888f8b8e5ab199b25ce8a74392b';
String _$callNotifierHash() => r'c39e8d88673113bde0b14eb16cd9d86fa549e42c';
/// See also [CallNotifier].
@ProviderFor(CallNotifier)

View File

@ -8,15 +8,15 @@ class AppRouter extends RootStackRouter {
@override
List<AutoRoute> get routes => [
RedirectRoute(path: '/', redirectTo: '/explore'),
AutoRoute(
page: ExploreShellRoute.page,
path: '/explore',
path: '/',
children: [
AutoRoute(page: ExploreRoute.page, path: ''),
AutoRoute(page: PostComposeRoute.page, path: 'posts/compose'),
AutoRoute(page: PostDetailRoute.page, path: 'posts/:id'),
AutoRoute(page: PostEditRoute.page, path: 'posts/:id/edit'),
AutoRoute(page: PublisherProfileRoute.page, path: 'publishers/:name'),
],
),
AutoRoute(
@ -28,12 +28,11 @@ class AppRouter extends RootStackRouter {
AutoRoute(page: WalletRoute.page, path: 'wallet'),
AutoRoute(page: RelationshipRoute.page, path: 'relationships'),
AutoRoute(page: AccountProfileRoute.page, path: ':name'),
AutoRoute(page: PublisherProfileRoute.page, path: ':name/calendar'),
AutoRoute(page: MyselfEventCalendarRoute.page, path: 'me/calendar'),
AutoRoute(page: UpdateProfileRoute.page, path: 'me/update'),
AutoRoute(page: AccountSettingsRoute.page, path: 'settings'),
],
),
AutoRoute(page: EventCalanderRoute.page, path: '/account/:name/calendar'),
AutoRoute(page: RealmListRoute.page, path: '/realms'),
AutoRoute(
page: ChatShellRoute.page,
@ -41,6 +40,7 @@ class AppRouter extends RootStackRouter {
children: [
AutoRoute(page: ChatListRoute.page, path: ''),
AutoRoute(page: ChatRoomRoute.page, path: ':id'),
AutoRoute(page: CallRoute.page, path: ':id/call'),
AutoRoute(page: NewChatRoute.page, path: 'new'),
AutoRoute(page: EditChatRoute.page, path: ':id/edit'),
AutoRoute(page: ChatDetailRoute.page, path: ':id/detail'),

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,9 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/activity.dart';
import 'package:island/pods/network.dart';
import 'package:island/screens/account/profile.dart';
import 'package:island/widgets/app_scaffold.dart';
import 'package:island/widgets/content/cloud_files.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:styled_widget/styled_widget.dart';
@ -39,8 +41,9 @@ Future<List<SnEventCalendarEntry>> accountEventCalendar(
}
@RoutePage()
class MyselfEventCalendarScreen extends HookConsumerWidget {
const MyselfEventCalendarScreen({super.key});
class EventCalanderScreen extends HookConsumerWidget {
final String name;
const EventCalanderScreen({super.key, @PathParam("name") required this.name});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -49,23 +52,18 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
final selectedDay = useState(DateTime.now());
final user = ref.watch(accountProvider(name));
final events = ref.watch(
accountEventCalendarProvider(
EventCalendarQuery(
uname: 'me',
uname: name,
year: selectedYear.value,
month: selectedMonth.value,
),
),
);
return AppScaffold(
appBar: AppBar(
leading: const PageBackButton(),
title: Text('eventCalander').tr(),
),
body: SingleChildScrollView(
child: Column(
final content = Column(
children: [
TableCalendar(
locale: EasyLocalization.of(context)!.locale.toString(),
@ -112,9 +110,7 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
fontSize: 9,
color:
isSameDay(selectedDay.value, day)
? Theme.of(
context,
).colorScheme.onPrimaryContainer
? Theme.of(context).colorScheme.onPrimaryContainer
: isSameDay(DateTime.now(), day)
? Theme.of(
context,
@ -171,8 +167,7 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
).padding(top: 4, right: 4),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(tip.title).bold(),
Text(tip.content),
@ -191,8 +186,46 @@ class MyselfEventCalendarScreen extends HookConsumerWidget {
},
),
),
],
if (name != 'me' && user.hasValue)
Container(
decoration: BoxDecoration(
border: Border.all(
width: 1 / MediaQuery.of(context).devicePixelRatio,
color: Theme.of(context).dividerColor,
),
borderRadius: BorderRadius.all(Radius.circular(8)),
),
margin: EdgeInsets.all(16),
child: Card(
margin: EdgeInsets.zero,
elevation: 0,
color: Colors.transparent,
child: ListTile(
leading: ProfilePictureWidget(
fileId: user.value!.profile.pictureId,
),
title: Text(user.value!.nick).bold(),
subtitle: Text('@${user.value!.name}'),
),
),
),
],
);
return AppScaffold(
noBackground: false,
appBar: AppBar(
leading: const PageBackButton(),
title: Text('eventCalander').tr(),
),
body: SingleChildScrollView(
child:
MediaQuery.of(context).size.width > 480
? ConstrainedBox(
constraints: BoxConstraints(maxWidth: 480),
child: Card(margin: EdgeInsets.all(16), child: content),
).center()
: content,
),
);
}

View File

@ -5,6 +5,7 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/user.dart';
import 'package:island/pods/network.dart';
import 'package:island/pods/userinfo.dart';
import 'package:island/widgets/account/badge.dart';
import 'package:island/widgets/account/leveling_progress.dart';
import 'package:island/widgets/account/status.dart';
@ -17,6 +18,12 @@ part 'profile.g.dart';
@riverpod
Future<SnAccount> account(Ref ref, String uname) async {
if (uname == 'me') {
final userInfo = ref.watch(userInfoProvider);
if (userInfo.hasValue && userInfo.value != null) {
return userInfo.value!;
}
}
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get("/accounts/$uname");
return SnAccount.fromJson(resp.data);

View File

@ -6,7 +6,7 @@ part of 'profile.dart';
// RiverpodGenerator
// **************************************************************************
String _$accountHash() => r'39003ef3250181b9290e0562329c7801d4841941';
String _$accountHash() => r'd2b0579617e6264452d98f47f695a9cdf45b24ec';
/// Copied from Dart SDK
class _SystemHash {

View File

@ -96,8 +96,7 @@ class RelationshipListTile extends StatelessWidget {
relationship.status == 0 && relationship.relatedId == currentUserId;
final isWaiting =
relationship.status == 0 && relationship.accountId == currentUserId;
final isEstablished =
relationship.status >= 100 || relationship.status <= -100;
final isEstablished = relationship.status == 1 || relationship.status == 2;
return ListTile(
contentPadding: const EdgeInsets.only(left: 16, right: 12),

View File

@ -75,6 +75,7 @@ class CreateAccountScreen extends HookConsumerWidget {
}
return AppScaffold(
noBackground: false,
appBar: AppBar(
leading: const PageBackButton(),
title: Text('createAccount').tr(),

View File

@ -41,6 +41,7 @@ class LoginScreen extends HookConsumerWidget {
final factors = useState<List<SnAuthFactor>>([]);
final factorPicked = useState<SnAuthFactor?>(null);
return AppScaffold(
noBackground: false,
appBar: AppBar(
leading: const PageBackButton(),
title: Text('login').tr(),

View File

@ -1,10 +1,13 @@
import 'package:auto_route/annotations.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/call.dart';
@RoutePage()
class CallScreen extends HookConsumerWidget {
const CallScreen({super.key});
final String roomId;
const CallScreen({super.key, @PathParam('id') required this.roomId});
@override
Widget build(BuildContext context, WidgetRef ref) {
@ -12,7 +15,7 @@ class CallScreen extends HookConsumerWidget {
final callNotifier = ref.read(callNotifierProvider.notifier);
useEffect(() {
callNotifier.initialize();
callNotifier.joinRoom(roomId);
return null;
}, []);

View File

@ -33,6 +33,7 @@ import 'package:material_symbols_icons/symbols.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:super_clipboard/super_clipboard.dart';
import 'chat.dart';
import 'package:island/widgets/chat/call_button.dart';
part 'room.g.dart';
@ -514,12 +515,7 @@ class ChatRoomScreen extends HookConsumerWidget {
),
),
actions: [
IconButton(
icon: const Icon(Symbols.video_call),
onPressed: () {
showInfoAlert('Oops', 'Not implemented yet...');
},
),
AudioCallButton(roomId: id),
IconButton(
icon: const Icon(Icons.more_vert),
onPressed: () {

View File

@ -13,6 +13,8 @@ import 'package:island/services/responsive.dart';
import 'package:material_symbols_icons/material_symbols_icons.dart';
import 'package:path_provider/path_provider.dart';
import 'package:styled_widget/styled_widget.dart';
import 'package:island/widgets/chat/call_overlay.dart';
import 'package:island/pods/call.dart';
class WindowScaffold extends HookConsumerWidget {
final Widget child;
@ -150,12 +152,17 @@ class AppScaffold extends StatelessWidget {
noBackground
? Colors.transparent
: Theme.of(context).scaffoldBackgroundColor,
body: SizedBox.expand(
body: Stack(
children: [
SizedBox.expand(
child:
noBackground
? content
: AppBackground(isRoot: true, child: content),
),
const _GlobalCallOverlay(),
],
),
appBar: appBar,
bottomNavigationBar: bottomNavigationBar,
bottomSheet: bottomSheet,
@ -192,6 +199,28 @@ class PageBackButton extends StatelessWidget {
const kAppBackgroundImagePath = 'island_app_background';
/// Global call overlay bar (appears when in a call but not on the call screen)
class _GlobalCallOverlay extends HookConsumerWidget {
const _GlobalCallOverlay();
@override
Widget build(BuildContext context, WidgetRef ref) {
final callState = ref.watch(callNotifierProvider);
// Find current route name
final modalRoute = ModalRoute.of(context);
final isOnCallScreen = modalRoute?.settings.name?.contains('call') ?? false;
// You may want to store roomId in callState for more robust navigation
final roomId =
(modalRoute?.settings.arguments is Map &&
(modalRoute!.settings.arguments as Map).containsKey('roomId'))
? (modalRoute.settings.arguments as Map)['roomId'] as String
: null;
if (callState.isConnected && !isOnCallScreen && roomId != null) {
return CallOverlayBar(roomId: roomId);
}
return const SizedBox.shrink();
}
}
final backgroundImageFileProvider = FutureProvider<File?>((ref) async {
if (kIsWeb) return null;
final dir = await getApplicationSupportDirectory();

View File

@ -0,0 +1,112 @@
import 'package:auto_route/auto_route.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/chat.dart';
import 'package:island/pods/call.dart';
import 'package:island/pods/network.dart';
import 'package:island/route.gr.dart';
import 'package:island/widgets/alert.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'call_button.g.dart';
@riverpod
Future<SnRealtimeCall?> ongoingCall(Ref ref, String roomId) async {
try {
final apiClient = ref.watch(apiClientProvider);
final resp = await apiClient.get('/chat/realtime/$roomId');
return SnRealtimeCall.fromJson(resp.data);
} catch (e) {
if (e is DioException && e.response?.statusCode == 404) {
return null;
}
showErrorAlert(e);
return null;
}
}
class AudioCallButton extends HookConsumerWidget {
final String roomId;
const AudioCallButton({super.key, required this.roomId});
@override
Widget build(BuildContext context, WidgetRef ref) {
final ongoingCall = ref.watch(ongoingCallProvider(roomId));
final callState = ref.watch(callNotifierProvider);
final callNotifier = ref.read(callNotifierProvider.notifier);
final isLoading = useState(false);
final apiClient = ref.watch(apiClientProvider);
Future<void> handleJoin() async {
isLoading.value = true;
try {
await apiClient.post('/chat/realtime/$roomId');
if (context.mounted) {
context.router.push(CallRoute(roomId: roomId));
}
} catch (e) {
showErrorAlert(e);
} finally {
isLoading.value = false;
}
}
Future<void> handleEnd() async {
isLoading.value = true;
try {
await apiClient.delete('/chat/realtime/$roomId');
callNotifier.dispose(); // Clean up call resources
} catch (e) {
showErrorAlert(e);
} finally {
isLoading.value = false;
}
}
if (isLoading.value) {
return IconButton(
icon: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
color: Theme.of(context).appBarTheme.foregroundColor!,
padding: EdgeInsets.all(4),
),
),
onPressed: null,
);
}
if (callState.isConnected) {
// Show end call button if in call
return IconButton(
icon: const Icon(Icons.call_end),
tooltip: 'End Call',
onPressed: handleEnd,
);
}
if (ongoingCall.value != null) {
// There is an ongoing call, offer to join it directly
return IconButton(
icon: const Icon(Icons.call),
tooltip: 'Join Ongoing Call',
onPressed: () {
if (context.mounted) {
context.router.push(CallRoute(roomId: roomId));
}
},
);
}
// Show join/start call button
return IconButton(
icon: const Icon(Icons.call),
tooltip: 'Start/Join Call',
onPressed: handleJoin,
);
}
}

View File

@ -0,0 +1,151 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'call_button.dart';
// **************************************************************************
// RiverpodGenerator
// **************************************************************************
String _$ongoingCallHash() => r'd8a942e6695a7da702daeaa452464c16761ef6e7';
/// Copied from Dart SDK
class _SystemHash {
_SystemHash._();
static int combine(int hash, int value) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + value);
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
return hash ^ (hash >> 6);
}
static int finish(int hash) {
// ignore: parameter_assignments
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
// ignore: parameter_assignments
hash = hash ^ (hash >> 11);
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
}
}
/// See also [ongoingCall].
@ProviderFor(ongoingCall)
const ongoingCallProvider = OngoingCallFamily();
/// See also [ongoingCall].
class OngoingCallFamily extends Family<AsyncValue<SnRealtimeCall?>> {
/// See also [ongoingCall].
const OngoingCallFamily();
/// See also [ongoingCall].
OngoingCallProvider call(String roomId) {
return OngoingCallProvider(roomId);
}
@override
OngoingCallProvider getProviderOverride(
covariant OngoingCallProvider provider,
) {
return call(provider.roomId);
}
static const Iterable<ProviderOrFamily>? _dependencies = null;
@override
Iterable<ProviderOrFamily>? get dependencies => _dependencies;
static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;
@override
Iterable<ProviderOrFamily>? get allTransitiveDependencies =>
_allTransitiveDependencies;
@override
String? get name => r'ongoingCallProvider';
}
/// See also [ongoingCall].
class OngoingCallProvider extends AutoDisposeFutureProvider<SnRealtimeCall?> {
/// See also [ongoingCall].
OngoingCallProvider(String roomId)
: this._internal(
(ref) => ongoingCall(ref as OngoingCallRef, roomId),
from: ongoingCallProvider,
name: r'ongoingCallProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$ongoingCallHash,
dependencies: OngoingCallFamily._dependencies,
allTransitiveDependencies: OngoingCallFamily._allTransitiveDependencies,
roomId: roomId,
);
OngoingCallProvider._internal(
super._createNotifier, {
required super.name,
required super.dependencies,
required super.allTransitiveDependencies,
required super.debugGetCreateSourceHash,
required super.from,
required this.roomId,
}) : super.internal();
final String roomId;
@override
Override overrideWith(
FutureOr<SnRealtimeCall?> Function(OngoingCallRef provider) create,
) {
return ProviderOverride(
origin: this,
override: OngoingCallProvider._internal(
(ref) => create(ref as OngoingCallRef),
from: from,
name: null,
dependencies: null,
allTransitiveDependencies: null,
debugGetCreateSourceHash: null,
roomId: roomId,
),
);
}
@override
AutoDisposeFutureProviderElement<SnRealtimeCall?> createElement() {
return _OngoingCallProviderElement(this);
}
@override
bool operator ==(Object other) {
return other is OngoingCallProvider && other.roomId == roomId;
}
@override
int get hashCode {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, roomId.hashCode);
return _SystemHash.finish(hash);
}
}
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
mixin OngoingCallRef on AutoDisposeFutureProviderRef<SnRealtimeCall?> {
/// The parameter `roomId` of this provider.
String get roomId;
}
class _OngoingCallProviderElement
extends AutoDisposeFutureProviderElement<SnRealtimeCall?>
with OngoingCallRef {
_OngoingCallProviderElement(super.provider);
@override
String get roomId => (origin as OngoingCallProvider).roomId;
}
// ignore_for_file: type=lint
// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package

View File

@ -0,0 +1,53 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/pods/call.dart';
import 'package:island/route.gr.dart';
/// A floating bar that appears when user is in a call but not on the call screen.
class CallOverlayBar extends HookConsumerWidget {
final String roomId;
const CallOverlayBar({super.key, required this.roomId});
@override
Widget build(BuildContext context, WidgetRef ref) {
final callState = ref.watch(callNotifierProvider);
// Only show if connected and not on the call screen
if (!callState.isConnected) return const SizedBox.shrink();
return Positioned(
left: 16,
right: 16,
bottom: 32,
child: GestureDetector(
onTap: () {
context.router.push(CallRoute(roomId: roomId));
},
child: Material(
elevation: 8,
borderRadius: BorderRadius.circular(24),
color: Theme.of(context).colorScheme.primary,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Row(
children: [
Icon(Icons.call, color: Colors.white),
const SizedBox(width: 12),
const Text(
'In call',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
),
],
),
const Icon(Icons.arrow_forward_ios, color: Colors.white, size: 18),
],
),
),
),
),
);
}
}

View File

@ -46,10 +46,6 @@ class MessageItem extends HookConsumerWidget {
isCurrentUser
? Theme.of(context).colorScheme.primaryContainer.withOpacity(0.5)
: Theme.of(context).colorScheme.surfaceContainer;
final linkColor = Color.alphaBlend(
Theme.of(context).colorScheme.primary,
containerColor,
);
final hasBackground =
ref.watch(backgroundImageFileProvider).valueOrNull != null;
@ -196,16 +192,8 @@ class MessageItem extends HookConsumerWidget {
textColor: textColor,
isReply: false,
).padding(vertical: 4),
if (remoteMessage.content?.isNotEmpty ?? false)
MarkdownTextContent(
content: remoteMessage.content!,
isSelectable: true,
linkStyle: TextStyle(color: linkColor),
textStyle: TextStyle(
color: textColor,
fontSize: 14,
),
),
if (_MessageItemContent.hasContent(remoteMessage))
_MessageItemContent(item: remoteMessage),
if (remoteMessage.attachments.isNotEmpty)
LayoutBuilder(
builder: (context, constraints) {
@ -360,7 +348,10 @@ class MessageQuoteWidget extends HookConsumerWidget {
: message.toRemoteMessage().forwardedMessageId!,
),
builder: (context, snapshot) {
if (snapshot.hasData) {
final remoteMessage =
snapshot.hasData ? snapshot.data!.toRemoteMessage() : null;
if (remoteMessage != null) {
return ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(8)),
child: Container(
@ -378,7 +369,7 @@ class MessageQuoteWidget extends HookConsumerWidget {
children: [
Icon(Symbols.reply, size: 16, color: textColor),
Text(
'Replying to ${snapshot.data!.toRemoteMessage().sender.account.nick}',
'Replying to ${remoteMessage.sender.account.nick}',
).textColor(textColor).bold(),
],
).padding(right: 8)
@ -389,16 +380,12 @@ class MessageQuoteWidget extends HookConsumerWidget {
children: [
Icon(Symbols.forward, size: 16, color: textColor),
Text(
'Forwarded from ${snapshot.data!.toRemoteMessage().sender.account.nick}',
'Forwarded from ${remoteMessage.sender.account.nick}',
).textColor(textColor).bold(),
],
).padding(right: 8),
if (snapshot.data!.toRemoteMessage().content?.isNotEmpty ??
false)
Text(
snapshot.data!.toRemoteMessage().content!,
style: TextStyle(color: textColor),
),
if (_MessageItemContent.hasContent(remoteMessage))
_MessageItemContent(item: remoteMessage),
],
),
),
@ -410,3 +397,62 @@ class MessageQuoteWidget extends HookConsumerWidget {
);
}
}
class _MessageItemContent extends StatelessWidget {
final SnChatMessage item;
const _MessageItemContent({required this.item});
@override
Widget build(BuildContext context) {
switch (item.type) {
case 'call.start':
case 'call.ended':
return _MessageContentCall(
isEnded: item.type == 'call.ended',
duration: item.meta['duration']?.toDouble(),
);
case 'text':
default:
return MarkdownTextContent(content: item.content!);
}
}
static bool hasContent(SnChatMessage item) {
return item.type == 'text' && (item.content?.isNotEmpty ?? false);
}
}
class _MessageContentCall extends StatelessWidget {
final bool isEnded;
final double? duration;
const _MessageContentCall({required this.isEnded, this.duration});
@override
Widget build(BuildContext context) {
String formatDuration(Duration duration) {
final hours = duration.inHours;
final minutes = duration.inMinutes.remainder(60);
final seconds = duration.inSeconds.remainder(60);
return '${hours == 0 ? '' : '$hours hours '}'
'${minutes.toString().padLeft(2, '0')}:${seconds.toString().padLeft(2, '0')}';
}
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
isEnded ? Symbols.call_end : Symbols.phone_in_talk,
size: 16,
color: Theme.of(context).colorScheme.primary,
),
Gap(4),
Text(
isEnded
? 'Call ended after ${formatDuration(Duration(seconds: duration!.toInt()))}'
: 'Call started',
style: TextStyle(color: Theme.of(context).colorScheme.primary),
),
],
);
}
}

View File

@ -42,11 +42,16 @@ class CheckInWidget extends HookConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final todayResult = ref.watch(checkInResultTodayProvider);
Future<void> checkIn() async {
Future<void> checkIn({String? captchatTk}) async {
final client = ref.read(apiClientProvider);
try {
await client.post('/accounts/me/check-in');
await client.post(
'/accounts/me/check-in',
data: captchatTk == null ? null : jsonEncode(captchatTk),
);
ref.invalidate(checkInResultTodayProvider);
final userNotifier = ref.read(userInfoProvider.notifier);
userNotifier.fetchUser();
} catch (err) {
if (err is DioException) {
if (err.response?.statusCode == 423 && context.mounted) {
@ -54,14 +59,7 @@ class CheckInWidget extends HookConsumerWidget {
context,
).push(MaterialPageRoute(builder: (context) => CaptchaScreen()));
if (captchaTk == null) return;
await client.post(
'/accounts/me/check-in',
data: jsonEncode(captchaTk),
);
ref.invalidate(checkInResultTodayProvider);
final userNotifier = ref.read(userInfoProvider.notifier);
userNotifier.fetchUser();
return;
return await checkIn(captchatTk: captchaTk);
}
}
showErrorAlert(err);
@ -139,7 +137,7 @@ class CheckInWidget extends HookConsumerWidget {
if (todayResult.valueOrNull == null) {
checkIn();
} else {
context.router.push(MyselfEventCalendarRoute());
context.router.push(EventCalanderRoute(name: 'me'));
}
},
icon: AnimatedSwitcher(

View File

@ -6,6 +6,7 @@ import FlutterMacOS
import Foundation
import bitsdojo_window_macos
import connectivity_plus
import device_info_plus
import file_picker
import file_selector_macos
@ -16,6 +17,7 @@ import flutter_platform_alert
import flutter_udid
import flutter_webrtc
import irondash_engine_context
import livekit_client
import media_kit_libs_macos_video
import media_kit_video
import package_info_plus
@ -30,6 +32,7 @@ import wakelock_plus
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
BitsdojoWindowPlugin.register(with: registry.registrar(forPlugin: "BitsdojoWindowPlugin"))
ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin"))
DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin"))
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
@ -40,6 +43,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterUdidPlugin.register(with: registry.registrar(forPlugin: "FlutterUdidPlugin"))
FlutterWebRTCPlugin.register(with: registry.registrar(forPlugin: "FlutterWebRTCPlugin"))
IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))

View File

@ -1,6 +1,8 @@
PODS:
- bitsdojo_window_macos (0.0.1):
- FlutterMacOS
- connectivity_plus (0.0.1):
- FlutterMacOS
- croppy (0.0.1):
- FlutterMacOS
- device_info_plus (0.0.1):
@ -50,6 +52,9 @@ PODS:
- flutter_udid (0.0.1):
- FlutterMacOS
- SAMKeychain
- flutter_webrtc (0.14.0):
- FlutterMacOS
- WebRTC-SDK (= 125.6422.07)
- FlutterMacOS (1.0.0)
- GoogleDataTransport (10.1.0):
- nanopb (~> 3.30910.0)
@ -80,6 +85,10 @@ PODS:
- GoogleUtilities/Privacy
- irondash_engine_context (0.0.1):
- FlutterMacOS
- livekit_client (2.4.7):
- flutter_webrtc
- FlutterMacOS
- WebRTC-SDK (= 125.6422.07)
- media_kit_libs_macos_video (1.0.4):
- FlutterMacOS
- media_kit_video (0.0.1):
@ -133,9 +142,11 @@ PODS:
- FlutterMacOS
- wakelock_plus (0.0.1):
- FlutterMacOS
- WebRTC-SDK (125.6422.07)
DEPENDENCIES:
- bitsdojo_window_macos (from `Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos`)
- connectivity_plus (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos`)
- croppy (from `Flutter/ephemeral/.symlinks/plugins/croppy/macos`)
- device_info_plus (from `Flutter/ephemeral/.symlinks/plugins/device_info_plus/macos`)
- file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`)
@ -145,8 +156,10 @@ DEPENDENCIES:
- flutter_inappwebview_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_inappwebview_macos/macos`)
- flutter_platform_alert (from `Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos`)
- flutter_udid (from `Flutter/ephemeral/.symlinks/plugins/flutter_udid/macos`)
- flutter_webrtc (from `Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- irondash_engine_context (from `Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos`)
- livekit_client (from `Flutter/ephemeral/.symlinks/plugins/livekit_client/macos`)
- media_kit_libs_macos_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos`)
- media_kit_video (from `Flutter/ephemeral/.symlinks/plugins/media_kit_video/macos`)
- package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`)
@ -173,10 +186,13 @@ SPEC REPOS:
- PromisesObjC
- SAMKeychain
- sqlite3
- WebRTC-SDK
EXTERNAL SOURCES:
bitsdojo_window_macos:
:path: Flutter/ephemeral/.symlinks/plugins/bitsdojo_window_macos/macos
connectivity_plus:
:path: Flutter/ephemeral/.symlinks/plugins/connectivity_plus/macos
croppy:
:path: Flutter/ephemeral/.symlinks/plugins/croppy/macos
device_info_plus:
@ -195,10 +211,14 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_platform_alert/macos
flutter_udid:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_udid/macos
flutter_webrtc:
:path: Flutter/ephemeral/.symlinks/plugins/flutter_webrtc/macos
FlutterMacOS:
:path: Flutter/ephemeral
irondash_engine_context:
:path: Flutter/ephemeral/.symlinks/plugins/irondash_engine_context/macos
livekit_client:
:path: Flutter/ephemeral/.symlinks/plugins/livekit_client/macos
media_kit_libs_macos_video:
:path: Flutter/ephemeral/.symlinks/plugins/media_kit_libs_macos_video/macos
media_kit_video:
@ -224,6 +244,7 @@ EXTERNAL SOURCES:
SPEC CHECKSUMS:
bitsdojo_window_macos: 7959fb0ca65a3ccda30095c181ecb856fae48ea9
connectivity_plus: 4adf20a405e25b42b9c9f87feff8f4b6fde18a4e
croppy: d9bfc8c02f3cd1851f669a421df298a474b78f43
device_info_plus: 4fb280989f669696856f8b129e4a5e3cd6c48f76
file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a
@ -238,10 +259,12 @@ SPEC CHECKSUMS:
flutter_inappwebview_macos: c2d68649f9f8f1831bfcd98d73fd6256366d9d1d
flutter_platform_alert: 8fa7a7c21f95b26d08b4a3891936ca27e375f284
flutter_udid: d26e455e8c06174e6aff476e147defc6cae38495
flutter_webrtc: a7eeb54859e672228c28f4b48b1fb61561976ea3
FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 00c88b9a86066ef77f0da2fab05f65d7768ed8e1
irondash_engine_context: 893c7d96d20ce361d7e996f39d360c4c2f9869ba
livekit_client: c5fc13c397f17de76534280744ddd8c232915057
media_kit_libs_macos_video: 85a23e549b5f480e72cae3e5634b5514bc692f65
media_kit_video: fa6564e3799a0a28bff39442334817088b7ca758
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
@ -258,6 +281,7 @@ SPEC CHECKSUMS:
url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673
volume_controller: 5c068e6d085c80dadd33fc2c918d2114b775b3dd
wakelock_plus: 21ddc249ac4b8d018838dbdabd65c5976c308497
WebRTC-SDK: dff00a3892bc570b6014e046297782084071657e
PODFILE CHECKSUM: 54d867c82ac51cbd61b565781b9fada492027009

View File

@ -384,14 +384,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
@ -427,14 +423,10 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
inputPaths = (
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";

View File

@ -313,6 +313,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
connectivity_plus:
dependency: transitive
description:
name: connectivity_plus
sha256: "051849e2bd7c7b3bc5844ea0d096609ddc3a859890ec3a9ac4a65a2620cc1f99"
url: "https://pub.dev"
source: hosted
version: "6.1.4"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
url: "https://pub.dev"
source: hosted
version: "2.0.1"
convert:
dependency: transitive
description:
@ -1149,6 +1165,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
livekit_client:
dependency: "direct main"
description:
name: livekit_client
sha256: dc8fe8295353a6f4c96915a49faece4ede2334d2b2bdf5ed4b985e07507bdf09
url: "https://pub.dev"
source: hosted
version: "2.4.7"
logging:
dependency: transitive
description:
@ -1285,6 +1309,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
mime_type:
dependency: transitive
description:
name: mime_type
sha256: d652b613e84dac1af28030a9fba82c0999be05b98163f9e18a0849c6e63838bb
url: "https://pub.dev"
source: hosted
version: "1.0.1"
modal_bottom_sheet:
dependency: "direct main"
description:
@ -1301,6 +1333,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
octo_image:
dependency: transitive
description:
@ -1453,6 +1493,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "6.0.2"
protobuf:
dependency: transitive
description:
name: protobuf
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
provider:
dependency: transitive
description:
@ -1581,6 +1629,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.1"
sdp_transform:
dependency: transitive
description:
name: sdp_transform
sha256: "73e412a5279a5c2de74001535208e20fff88f225c9a4571af0f7146202755e45"
url: "https://pub.dev"
source: hosted
version: "0.3.2"
shared_preferences:
dependency: "direct main"
description:

View File

@ -102,6 +102,7 @@ dependencies:
super_sliver_list: ^0.4.1
super_clipboard: ^0.9.0-dev.6
flutter_webrtc: ^0.14.1
livekit_client: ^2.4.7
dev_dependencies:
flutter_test:

View File

@ -7,6 +7,7 @@
#include "generated_plugin_registrant.h"
#include <bitsdojo_window_windows/bitsdojo_window_plugin.h>
#include <connectivity_plus/connectivity_plus_windows_plugin.h>
#include <file_selector_windows/file_selector_windows.h>
#include <firebase_core/firebase_core_plugin_c_api.h>
#include <flutter_inappwebview_windows/flutter_inappwebview_windows_plugin_c_api.h>
@ -14,6 +15,7 @@
#include <flutter_udid/flutter_udid_plugin_c_api.h>
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
#include <irondash_engine_context/irondash_engine_context_plugin_c_api.h>
#include <livekit_client/live_kit_plugin.h>
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
#include <media_kit_video/media_kit_video_plugin_c_api.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
@ -24,6 +26,8 @@
void RegisterPlugins(flutter::PluginRegistry* registry) {
BitsdojoWindowPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("BitsdojoWindowPlugin"));
ConnectivityPlusWindowsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
FirebaseCorePluginCApiRegisterWithRegistrar(
@ -38,6 +42,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
registry->GetRegistrarForPlugin("FlutterWebRTCPlugin"));
IrondashEngineContextPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi"));
LiveKitPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("LiveKitPlugin"));
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
MediaKitVideoPluginCApiRegisterWithRegistrar(

View File

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
bitsdojo_window_windows
connectivity_plus
file_selector_windows
firebase_core
flutter_inappwebview_windows
@ -11,6 +12,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
flutter_udid
flutter_webrtc
irondash_engine_context
livekit_client
media_kit_libs_windows_video
media_kit_video
sqlite3_flutter_libs