✨ Realtime call
This commit is contained in:
parent
edf4ff1c5b
commit
f39a066f71
@ -41,5 +41,9 @@ end
|
||||
post_install do |installer|
|
||||
installer.pods_project.targets.each do |target|
|
||||
flutter_additional_ios_build_settings(target)
|
||||
target.build_configurations.each do |config|
|
||||
# Workaround for https://github.com/flutter/flutter/issues/64502
|
||||
config.build_settings['ONLY_ACTIVE_ARCH'] = 'YES' # <= this line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -26,6 +26,16 @@
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSCalendarsUsageDescription</key>
|
||||
<string>Grant access to Calander help us to shows Solar Calander with your own events.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Grant access to Camera will allow Solian take photo or video for your post.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Grant access to Microphone will allow Solian record audio for your post.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<key>UIBackgroundModes</key>
|
||||
@ -33,11 +43,14 @@
|
||||
<string>fetch</string>
|
||||
<string>audio</string>
|
||||
<string>remote-notification</string>
|
||||
<string>voip</string>
|
||||
</array>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
@ -51,17 +64,5 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>NSCalendarsUsageDescription</key>
|
||||
<string>Grant access to Calander help us to shows Solar Calander with your own events.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Grant access to Camera will allow Solian take photo or video for your post.</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Grant access to Microphone will allow Solian record audio for your post.</string>
|
||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||
<string>Grant access to Photo Library will allow Solian download photo to album for you.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Grant access to Photo Library will allow Solian upload photo or video for your post.</string>
|
||||
<key>UIStatusBarHidden</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
@ -85,4 +85,5 @@ class DefaultFirebaseOptions {
|
||||
storageBucket: 'solian-0x001.firebasestorage.app',
|
||||
measurementId: 'G-JD1YEG9D6F',
|
||||
);
|
||||
|
||||
}
|
@ -19,6 +19,7 @@ import 'package:island/pods/websocket.dart';
|
||||
import 'package:island/route.dart';
|
||||
import 'package:island/screens/auth/tabs.dart';
|
||||
import 'package:island/services/notify.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:relative_time/relative_time.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
@ -28,11 +29,21 @@ import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
void main() async {
|
||||
final widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
log(
|
||||
"[SplashScreen] Keeping the flash screen to loading other resources...",
|
||||
);
|
||||
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
|
||||
}
|
||||
|
||||
try {
|
||||
await EasyLocalization.ensureInitialized();
|
||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
);
|
||||
log("[SplashScreen] Firebase is ready!");
|
||||
} catch (err) {
|
||||
showErrorAlert(err);
|
||||
}
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@ -43,6 +54,7 @@ void main() async {
|
||||
appWindow.size = initialSize;
|
||||
appWindow.alignment = Alignment.center;
|
||||
appWindow.show();
|
||||
log("[SplashScreen] Desktop window is ready!");
|
||||
});
|
||||
}
|
||||
|
||||
@ -52,10 +64,12 @@ void main() async {
|
||||
if (imagePickerImplementation is ImagePickerAndroid) {
|
||||
imagePickerImplementation.useAndroidPhotoPicker = true;
|
||||
}
|
||||
log("[SplashScreen] Android image picker is ready!");
|
||||
}
|
||||
|
||||
if (!kIsWeb && (Platform.isAndroid || Platform.isIOS)) {
|
||||
FlutterNativeSplash.remove();
|
||||
log("[SplashScreen] Now hiding the splash screen...");
|
||||
}
|
||||
|
||||
runApp(
|
||||
|
@ -146,12 +146,27 @@ sealed class ChatRealtimeJoinResponse with _$ChatRealtimeJoinResponse {
|
||||
required String callId,
|
||||
required String roomName,
|
||||
required bool isAdmin,
|
||||
required List<CallParticipant> participants,
|
||||
}) = _ChatRealtimeJoinResponse;
|
||||
|
||||
factory ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) =>
|
||||
_$ChatRealtimeJoinResponseFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class CallParticipant with _$CallParticipant {
|
||||
const factory CallParticipant({
|
||||
required String identity,
|
||||
required String name,
|
||||
required DateTime joinedAt,
|
||||
required String? accountId,
|
||||
required SnChatMember? profile,
|
||||
}) = _CallParticipant;
|
||||
|
||||
factory CallParticipant.fromJson(Map<String, dynamic> json) =>
|
||||
_$CallParticipantFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class SnRealtimeCall with _$SnRealtimeCall {
|
||||
const factory SnRealtimeCall({
|
||||
|
@ -1342,7 +1342,7 @@ as DateTime,
|
||||
/// @nodoc
|
||||
mixin _$ChatRealtimeJoinResponse {
|
||||
|
||||
String get provider; String get endpoint; String get token; String get callId; String get roomName; bool get isAdmin;
|
||||
String get provider; String get endpoint; String get token; String get callId; String get roomName; bool get isAdmin; List<CallParticipant> get participants;
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -1355,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.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));
|
||||
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)&&const DeepCollectionEquality().equals(other.participants, participants));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin);
|
||||
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin,const DeepCollectionEquality().hash(participants));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin)';
|
||||
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin, participants: $participants)';
|
||||
}
|
||||
|
||||
|
||||
@ -1375,7 +1375,7 @@ abstract mixin class $ChatRealtimeJoinResponseCopyWith<$Res> {
|
||||
factory $ChatRealtimeJoinResponseCopyWith(ChatRealtimeJoinResponse value, $Res Function(ChatRealtimeJoinResponse) _then) = _$ChatRealtimeJoinResponseCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin
|
||||
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin, List<CallParticipant> participants
|
||||
});
|
||||
|
||||
|
||||
@ -1392,7 +1392,7 @@ 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? provider = null,Object? endpoint = null,Object? token = null,Object? callId = null,Object? roomName = null,Object? isAdmin = 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,Object? participants = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
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
|
||||
@ -1400,7 +1400,8 @@ as String,token: null == token ? _self.token : token // ignore: cast_nullable_to
|
||||
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,
|
||||
as bool,participants: null == participants ? _self.participants : participants // ignore: cast_nullable_to_non_nullable
|
||||
as List<CallParticipant>,
|
||||
));
|
||||
}
|
||||
|
||||
@ -1411,7 +1412,7 @@ as bool,
|
||||
@JsonSerializable()
|
||||
|
||||
class _ChatRealtimeJoinResponse implements ChatRealtimeJoinResponse {
|
||||
const _ChatRealtimeJoinResponse({required this.provider, required this.endpoint, required this.token, required this.callId, required this.roomName, required this.isAdmin});
|
||||
const _ChatRealtimeJoinResponse({required this.provider, required this.endpoint, required this.token, required this.callId, required this.roomName, required this.isAdmin, required final List<CallParticipant> participants}): _participants = participants;
|
||||
factory _ChatRealtimeJoinResponse.fromJson(Map<String, dynamic> json) => _$ChatRealtimeJoinResponseFromJson(json);
|
||||
|
||||
@override final String provider;
|
||||
@ -1420,6 +1421,13 @@ class _ChatRealtimeJoinResponse implements ChatRealtimeJoinResponse {
|
||||
@override final String callId;
|
||||
@override final String roomName;
|
||||
@override final bool isAdmin;
|
||||
final List<CallParticipant> _participants;
|
||||
@override List<CallParticipant> get participants {
|
||||
if (_participants is EqualUnmodifiableListView) return _participants;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableListView(_participants);
|
||||
}
|
||||
|
||||
|
||||
/// Create a copy of ChatRealtimeJoinResponse
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -1434,16 +1442,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
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));
|
||||
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)&&const DeepCollectionEquality().equals(other._participants, _participants));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin);
|
||||
int get hashCode => Object.hash(runtimeType,provider,endpoint,token,callId,roomName,isAdmin,const DeepCollectionEquality().hash(_participants));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin)';
|
||||
return 'ChatRealtimeJoinResponse(provider: $provider, endpoint: $endpoint, token: $token, callId: $callId, roomName: $roomName, isAdmin: $isAdmin, participants: $participants)';
|
||||
}
|
||||
|
||||
|
||||
@ -1454,7 +1462,7 @@ abstract mixin class _$ChatRealtimeJoinResponseCopyWith<$Res> implements $ChatRe
|
||||
factory _$ChatRealtimeJoinResponseCopyWith(_ChatRealtimeJoinResponse value, $Res Function(_ChatRealtimeJoinResponse) _then) = __$ChatRealtimeJoinResponseCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin
|
||||
String provider, String endpoint, String token, String callId, String roomName, bool isAdmin, List<CallParticipant> participants
|
||||
});
|
||||
|
||||
|
||||
@ -1471,7 +1479,7 @@ 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? provider = null,Object? endpoint = null,Object? token = null,Object? callId = null,Object? roomName = null,Object? isAdmin = 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,Object? participants = null,}) {
|
||||
return _then(_ChatRealtimeJoinResponse(
|
||||
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
|
||||
@ -1479,7 +1487,8 @@ as String,token: null == token ? _self.token : token // ignore: cast_nullable_to
|
||||
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,
|
||||
as bool,participants: null == participants ? _self._participants : participants // ignore: cast_nullable_to_non_nullable
|
||||
as List<CallParticipant>,
|
||||
));
|
||||
}
|
||||
|
||||
@ -1487,6 +1496,175 @@ as bool,
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CallParticipant {
|
||||
|
||||
String get identity; String get name; DateTime get joinedAt; String? get accountId; SnChatMember? get profile;
|
||||
/// Create a copy of CallParticipant
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CallParticipantCopyWith<CallParticipant> get copyWith => _$CallParticipantCopyWithImpl<CallParticipant>(this as CallParticipant, _$identity);
|
||||
|
||||
/// Serializes this CallParticipant to a JSON map.
|
||||
Map<String, dynamic> 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.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.profile, profile) || other.profile == profile));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identity,name,joinedAt,accountId,profile);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt, accountId: $accountId, profile: $profile)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CallParticipantCopyWith<$Res> {
|
||||
factory $CallParticipantCopyWith(CallParticipant value, $Res Function(CallParticipant) _then) = _$CallParticipantCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile
|
||||
});
|
||||
|
||||
|
||||
$SnChatMemberCopyWith<$Res>? get profile;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CallParticipantCopyWithImpl<$Res>
|
||||
implements $CallParticipantCopyWith<$Res> {
|
||||
_$CallParticipantCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CallParticipant _self;
|
||||
final $Res Function(CallParticipant) _then;
|
||||
|
||||
/// 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? joinedAt = null,Object? accountId = freezed,Object? profile = freezed,}) {
|
||||
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,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,profile: freezed == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||
as SnChatMember?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of CallParticipant
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnChatMemberCopyWith<$Res>? get profile {
|
||||
if (_self.profile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnChatMemberCopyWith<$Res>(_self.profile!, (value) {
|
||||
return _then(_self.copyWith(profile: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _CallParticipant implements CallParticipant {
|
||||
const _CallParticipant({required this.identity, required this.name, required this.joinedAt, required this.accountId, required this.profile});
|
||||
factory _CallParticipant.fromJson(Map<String, dynamic> json) => _$CallParticipantFromJson(json);
|
||||
|
||||
@override final String identity;
|
||||
@override final String name;
|
||||
@override final DateTime joinedAt;
|
||||
@override final String? accountId;
|
||||
@override final SnChatMember? profile;
|
||||
|
||||
/// Create a copy of CallParticipant
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CallParticipantCopyWith<_CallParticipant> get copyWith => __$CallParticipantCopyWithImpl<_CallParticipant>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$CallParticipantToJson(this, );
|
||||
}
|
||||
|
||||
@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.joinedAt, joinedAt) || other.joinedAt == joinedAt)&&(identical(other.accountId, accountId) || other.accountId == accountId)&&(identical(other.profile, profile) || other.profile == profile));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,identity,name,joinedAt,accountId,profile);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallParticipant(identity: $identity, name: $name, joinedAt: $joinedAt, accountId: $accountId, profile: $profile)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CallParticipantCopyWith<$Res> implements $CallParticipantCopyWith<$Res> {
|
||||
factory _$CallParticipantCopyWith(_CallParticipant value, $Res Function(_CallParticipant) _then) = __$CallParticipantCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String identity, String name, DateTime joinedAt, String? accountId, SnChatMember? profile
|
||||
});
|
||||
|
||||
|
||||
@override $SnChatMemberCopyWith<$Res>? get profile;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CallParticipantCopyWithImpl<$Res>
|
||||
implements _$CallParticipantCopyWith<$Res> {
|
||||
__$CallParticipantCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CallParticipant _self;
|
||||
final $Res Function(_CallParticipant) _then;
|
||||
|
||||
/// 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? joinedAt = null,Object? accountId = freezed,Object? profile = freezed,}) {
|
||||
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,joinedAt: null == joinedAt ? _self.joinedAt : joinedAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,accountId: freezed == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,profile: freezed == profile ? _self.profile : profile // ignore: cast_nullable_to_non_nullable
|
||||
as SnChatMember?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of CallParticipant
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnChatMemberCopyWith<$Res>? get profile {
|
||||
if (_self.profile == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnChatMemberCopyWith<$Res>(_self.profile!, (value) {
|
||||
return _then(_self.copyWith(profile: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnRealtimeCall {
|
||||
|
||||
|
@ -249,6 +249,10 @@ _ChatRealtimeJoinResponse _$ChatRealtimeJoinResponseFromJson(
|
||||
callId: json['call_id'] as String,
|
||||
roomName: json['room_name'] as String,
|
||||
isAdmin: json['is_admin'] as bool,
|
||||
participants:
|
||||
(json['participants'] as List<dynamic>)
|
||||
.map((e) => CallParticipant.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ChatRealtimeJoinResponseToJson(
|
||||
@ -260,6 +264,28 @@ Map<String, dynamic> _$ChatRealtimeJoinResponseToJson(
|
||||
'call_id': instance.callId,
|
||||
'room_name': instance.roomName,
|
||||
'is_admin': instance.isAdmin,
|
||||
'participants': instance.participants.map((e) => e.toJson()).toList(),
|
||||
};
|
||||
|
||||
_CallParticipant _$CallParticipantFromJson(Map<String, dynamic> json) =>
|
||||
_CallParticipant(
|
||||
identity: json['identity'] as String,
|
||||
name: json['name'] as String,
|
||||
joinedAt: DateTime.parse(json['joined_at'] as String),
|
||||
accountId: json['account_id'] as String?,
|
||||
profile:
|
||||
json['profile'] == null
|
||||
? null
|
||||
: SnChatMember.fromJson(json['profile'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CallParticipantToJson(_CallParticipant instance) =>
|
||||
<String, dynamic>{
|
||||
'identity': instance.identity,
|
||||
'name': instance.name,
|
||||
'joined_at': instance.joinedAt.toIso8601String(),
|
||||
'account_id': instance.accountId,
|
||||
'profile': instance.profile?.toJson(),
|
||||
};
|
||||
|
||||
_SnRealtimeCall _$SnRealtimeCallFromJson(Map<String, dynamic> json) =>
|
||||
|
@ -1,7 +1,12 @@
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/screens/chat/chat.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'dart:async';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/models/chat.dart';
|
||||
import 'package:island/pods/websocket.dart';
|
||||
|
||||
part 'call.g.dart';
|
||||
part 'call.freezed.dart';
|
||||
@ -9,43 +14,244 @@ part 'call.freezed.dart';
|
||||
@freezed
|
||||
sealed class CallState with _$CallState {
|
||||
const factory CallState({
|
||||
required bool isMuted,
|
||||
required bool isConnected,
|
||||
required bool isMicrophoneEnabled,
|
||||
required bool isCameraEnabled,
|
||||
required bool isScreenSharing,
|
||||
String? error,
|
||||
}) = _CallState;
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class CallParticipantLive with _$CallParticipantLive {
|
||||
const CallParticipantLive._();
|
||||
|
||||
const factory CallParticipantLive({
|
||||
required CallParticipant participant,
|
||||
required Participant remoteParticipant,
|
||||
}) = _CallParticipantLive;
|
||||
|
||||
bool get isSpeaking => remoteParticipant.isSpeaking;
|
||||
bool get isMuted => remoteParticipant.isMuted;
|
||||
bool get isScreenSharing => remoteParticipant.isScreenShareEnabled();
|
||||
bool get isScreenSharingWithAudio =>
|
||||
remoteParticipant.isScreenShareAudioEnabled();
|
||||
|
||||
bool get hasVideo => remoteParticipant.hasVideo;
|
||||
bool get hasAudio => remoteParticipant.hasAudio;
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class CallNotifier extends _$CallNotifier {
|
||||
Room? _room;
|
||||
LocalParticipant? _localParticipant;
|
||||
LocalAudioTrack? _localAudioTrack;
|
||||
List<CallParticipantLive> _participants = [];
|
||||
final Map<String, CallParticipant> _participantInfoByIdentity = {};
|
||||
StreamSubscription? _wsSubscription;
|
||||
EventsListener? _roomListener;
|
||||
|
||||
List<CallParticipantLive> get participants =>
|
||||
List.unmodifiable(_participants);
|
||||
LocalParticipant? get localParticipant => _localParticipant;
|
||||
|
||||
@override
|
||||
CallState build() {
|
||||
return const CallState(isMuted: false, isConnected: false);
|
||||
// Subscribe to websocket updates
|
||||
_subscribeToParticipantsUpdate();
|
||||
return const CallState(
|
||||
isConnected: false,
|
||||
isMicrophoneEnabled: true,
|
||||
isCameraEnabled: false,
|
||||
isScreenSharing: false,
|
||||
);
|
||||
}
|
||||
|
||||
void _subscribeToParticipantsUpdate() {
|
||||
// Only subscribe once
|
||||
if (_wsSubscription != null) return;
|
||||
final ws = ref.read(websocketProvider);
|
||||
_wsSubscription = ws.dataStream.listen((packet) {
|
||||
if (packet.type == 'call.participants.update' && packet.data != null) {
|
||||
final participantsData = packet.data!["participants"];
|
||||
if (participantsData is List) {
|
||||
final parsed =
|
||||
participantsData
|
||||
.map(
|
||||
(e) =>
|
||||
CallParticipant.fromJson(Map<String, dynamic>.from(e)),
|
||||
)
|
||||
.toList();
|
||||
_updateLiveParticipants(parsed);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _initRoomListeners() {
|
||||
if (_room == null) return;
|
||||
_roomListener?.dispose();
|
||||
_roomListener = _room!.createListener();
|
||||
_room!.addListener(_onRoomChange);
|
||||
_roomListener!
|
||||
..on<ParticipantConnectedEvent>((e) {
|
||||
_refreshLiveParticipants();
|
||||
})
|
||||
..on<RoomDisconnectedEvent>((e) {
|
||||
_participants = [];
|
||||
state = state.copyWith();
|
||||
});
|
||||
}
|
||||
|
||||
void _onRoomChange() {
|
||||
_refreshLiveParticipants();
|
||||
}
|
||||
|
||||
void _refreshLiveParticipants() {
|
||||
if (_room == null) return;
|
||||
final remoteParticipants = _room!.remoteParticipants;
|
||||
_participants = [];
|
||||
// Add local participant first if available
|
||||
if (_localParticipant != null) {
|
||||
final localInfo = _buildParticipant();
|
||||
_participants.add(
|
||||
CallParticipantLive(
|
||||
participant: localInfo,
|
||||
remoteParticipant: _localParticipant!,
|
||||
),
|
||||
);
|
||||
}
|
||||
// Add remote participants
|
||||
_participants.addAll(
|
||||
remoteParticipants.values.map((remote) {
|
||||
final match =
|
||||
_participantInfoByIdentity[remote.identity] ??
|
||||
CallParticipant(
|
||||
identity: remote.identity,
|
||||
name: remote.identity,
|
||||
joinedAt: DateTime.now(),
|
||||
accountId: null,
|
||||
profile: null,
|
||||
);
|
||||
return CallParticipantLive(
|
||||
participant: match,
|
||||
remoteParticipant: remote,
|
||||
);
|
||||
}),
|
||||
);
|
||||
state = state.copyWith();
|
||||
}
|
||||
|
||||
/// Builds the CallParticipant object for the local participant.
|
||||
/// Optionally, pass [participants] if you want to prioritize info from the latest list.
|
||||
CallParticipant _buildParticipant({List<CallParticipant>? participants}) {
|
||||
if (_localParticipant == null) {
|
||||
throw StateError('No local participant available');
|
||||
}
|
||||
// Prefer info from the latest participants list if available
|
||||
if (participants != null) {
|
||||
final idx = participants.indexWhere(
|
||||
(p) => p.identity == _localParticipant!.identity,
|
||||
);
|
||||
if (idx != -1) return participants[idx];
|
||||
}
|
||||
|
||||
final userInfo = ref.read(userInfoProvider);
|
||||
final roomIdentity = ref.read(chatroomIdentityProvider(_roomId));
|
||||
// Otherwise, use info from the identity map or fallback to minimal
|
||||
return _participantInfoByIdentity[_localParticipant!.identity] ??
|
||||
CallParticipant(
|
||||
identity: _localParticipant!.identity,
|
||||
name: _localParticipant!.identity,
|
||||
joinedAt: DateTime.now(),
|
||||
accountId: userInfo.value?.id,
|
||||
profile: roomIdentity.value,
|
||||
);
|
||||
}
|
||||
|
||||
void _updateLiveParticipants(List<CallParticipant> participants) {
|
||||
// Update the info map for lookup
|
||||
for (final p in participants) {
|
||||
_participantInfoByIdentity[p.identity] = p;
|
||||
}
|
||||
if (_room == null) {
|
||||
// Can't build live objects, just store empty
|
||||
_participants = [];
|
||||
state = state.copyWith();
|
||||
return;
|
||||
}
|
||||
final remoteParticipants = _room!.remoteParticipants;
|
||||
final remotes = remoteParticipants.values.toList();
|
||||
_participants = [];
|
||||
// Add local participant if present in the list
|
||||
if (_localParticipant != null) {
|
||||
final localInfo = _buildParticipant(participants: participants);
|
||||
_participants.add(
|
||||
CallParticipantLive(
|
||||
participant: localInfo,
|
||||
remoteParticipant: _localParticipant!,
|
||||
),
|
||||
);
|
||||
}
|
||||
// Add remote participants
|
||||
_participants.addAll(
|
||||
participants.map((p) {
|
||||
RemoteParticipant? remote;
|
||||
for (final r in remotes) {
|
||||
if (r.identity == p.identity) {
|
||||
remote = r;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_localParticipant != null &&
|
||||
p.identity == _localParticipant!.identity) {
|
||||
return null; // Already added local
|
||||
}
|
||||
return remote != null
|
||||
? CallParticipantLive(participant: p, remoteParticipant: remote)
|
||||
: null;
|
||||
}).whereType<CallParticipantLive>(),
|
||||
);
|
||||
state = state.copyWith();
|
||||
}
|
||||
|
||||
String? _roomId;
|
||||
|
||||
Future<void> joinRoom(String roomId) async {
|
||||
_roomId = roomId;
|
||||
try {
|
||||
final apiClient = ref.read(apiClientProvider);
|
||||
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'];
|
||||
// Parse join response
|
||||
final joinResponse = ChatRealtimeJoinResponse.fromJson(data);
|
||||
final participants = joinResponse.participants;
|
||||
final String endpoint = joinResponse.endpoint;
|
||||
final String token = joinResponse.token;
|
||||
// Connect to LiveKit
|
||||
_room = Room();
|
||||
await _room!.connect(endpoint, token);
|
||||
|
||||
await _room!.connect(
|
||||
endpoint,
|
||||
token,
|
||||
connectOptions: ConnectOptions(autoSubscribe: true),
|
||||
roomOptions: RoomOptions(adaptiveStream: true, dynacast: true),
|
||||
fastConnectOptions: FastConnectOptions(
|
||||
microphone: TrackOption(enabled: true),
|
||||
),
|
||||
);
|
||||
_localParticipant = _room!.localParticipant;
|
||||
// Create local audio track and publish
|
||||
_localAudioTrack = await LocalAudioTrack.create();
|
||||
await _localParticipant!.publishAudioTrack(_localAudioTrack!);
|
||||
|
||||
_initRoomListeners();
|
||||
_updateLiveParticipants(participants);
|
||||
|
||||
// Listen for connection updates
|
||||
_room!.addListener(() {
|
||||
state = state.copyWith(
|
||||
isConnected: _room!.connectionState == ConnectionState.connected,
|
||||
isMicrophoneEnabled: _localParticipant!.isMicrophoneEnabled(),
|
||||
isCameraEnabled: _localParticipant!.isCameraEnabled(),
|
||||
isScreenSharing: _localParticipant!.isScreenShareEnabled(),
|
||||
);
|
||||
});
|
||||
state = state.copyWith(isConnected: true);
|
||||
@ -57,27 +263,55 @@ class CallNotifier extends _$CallNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
void toggleMute() {
|
||||
final newMuted = !state.isMuted;
|
||||
state = state.copyWith(isMuted: newMuted);
|
||||
if (_localAudioTrack != null) {
|
||||
if (newMuted) {
|
||||
_localAudioTrack!.mute();
|
||||
Future<void> toggleMicrophone() async {
|
||||
if (_localParticipant != null) {
|
||||
const autostop = true;
|
||||
final target = !_localParticipant!.isMicrophoneEnabled();
|
||||
state = state.copyWith(isMicrophoneEnabled: target);
|
||||
if (target) {
|
||||
await _localParticipant!.audioTrackPublications.firstOrNull?.unmute(
|
||||
stopOnMute: autostop,
|
||||
);
|
||||
} else {
|
||||
_localAudioTrack!.unmute();
|
||||
await _localParticipant!.audioTrackPublications.firstOrNull?.mute(
|
||||
stopOnMute: autostop,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleCamera() async {
|
||||
if (_localParticipant != null) {
|
||||
final target = !_localParticipant!.isCameraEnabled();
|
||||
state = state.copyWith(isCameraEnabled: target);
|
||||
await _localParticipant!.setCameraEnabled(target);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleScreenShare() async {
|
||||
if (_localParticipant != null) {
|
||||
final target = !_localParticipant!.isScreenShareEnabled();
|
||||
state = state.copyWith(isScreenSharing: target);
|
||||
await _localParticipant!.setScreenShareEnabled(target);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
if (_room != null) {
|
||||
await _room!.disconnect();
|
||||
state = state.copyWith(isConnected: false);
|
||||
state = state.copyWith(
|
||||
isConnected: false,
|
||||
isMicrophoneEnabled: false,
|
||||
isCameraEnabled: false,
|
||||
isScreenSharing: false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_localAudioTrack?.dispose();
|
||||
_wsSubscription?.cancel();
|
||||
_roomListener?.dispose();
|
||||
_room?.removeListener(_onRoomChange);
|
||||
_room?.dispose();
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$CallState {
|
||||
|
||||
bool get isMuted; bool get isConnected; String? get error;
|
||||
bool get isConnected; bool get isMicrophoneEnabled; bool get isCameraEnabled; bool get isScreenSharing; String? get error;
|
||||
/// Create a copy of CallState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -26,16 +26,16 @@ $CallStateCopyWith<CallState> get copyWith => _$CallStateCopyWithImpl<CallState>
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallState&&(identical(other.isMuted, isMuted) || other.isMuted == isMuted)&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.error, error) || other.error == error));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isMuted,isConnected,error);
|
||||
int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,error);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallState(isMuted: $isMuted, isConnected: $isConnected, error: $error)';
|
||||
return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, error: $error)';
|
||||
}
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ abstract mixin class $CallStateCopyWith<$Res> {
|
||||
factory $CallStateCopyWith(CallState value, $Res Function(CallState) _then) = _$CallStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
bool isMuted, bool isConnected, String? error
|
||||
bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, String? error
|
||||
});
|
||||
|
||||
|
||||
@ -63,10 +63,12 @@ class _$CallStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of CallState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isMuted = null,Object? isConnected = null,Object? error = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? error = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
isMuted: null == isMuted ? _self.isMuted : isMuted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
|
||||
isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
@ -79,11 +81,13 @@ as String?,
|
||||
|
||||
|
||||
class _CallState implements CallState {
|
||||
const _CallState({required this.isMuted, required this.isConnected, this.error});
|
||||
const _CallState({required this.isConnected, required this.isMicrophoneEnabled, required this.isCameraEnabled, required this.isScreenSharing, this.error});
|
||||
|
||||
|
||||
@override final bool isMuted;
|
||||
@override final bool isConnected;
|
||||
@override final bool isMicrophoneEnabled;
|
||||
@override final bool isCameraEnabled;
|
||||
@override final bool isScreenSharing;
|
||||
@override final String? error;
|
||||
|
||||
/// Create a copy of CallState
|
||||
@ -96,16 +100,16 @@ _$CallStateCopyWith<_CallState> get copyWith => __$CallStateCopyWithImpl<_CallSt
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallState&&(identical(other.isMuted, isMuted) || other.isMuted == isMuted)&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.error, error) || other.error == error));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallState&&(identical(other.isConnected, isConnected) || other.isConnected == isConnected)&&(identical(other.isMicrophoneEnabled, isMicrophoneEnabled) || other.isMicrophoneEnabled == isMicrophoneEnabled)&&(identical(other.isCameraEnabled, isCameraEnabled) || other.isCameraEnabled == isCameraEnabled)&&(identical(other.isScreenSharing, isScreenSharing) || other.isScreenSharing == isScreenSharing)&&(identical(other.error, error) || other.error == error));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,isMuted,isConnected,error);
|
||||
int get hashCode => Object.hash(runtimeType,isConnected,isMicrophoneEnabled,isCameraEnabled,isScreenSharing,error);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallState(isMuted: $isMuted, isConnected: $isConnected, error: $error)';
|
||||
return 'CallState(isConnected: $isConnected, isMicrophoneEnabled: $isMicrophoneEnabled, isCameraEnabled: $isCameraEnabled, isScreenSharing: $isScreenSharing, error: $error)';
|
||||
}
|
||||
|
||||
|
||||
@ -116,7 +120,7 @@ abstract mixin class _$CallStateCopyWith<$Res> implements $CallStateCopyWith<$Re
|
||||
factory _$CallStateCopyWith(_CallState value, $Res Function(_CallState) _then) = __$CallStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
bool isMuted, bool isConnected, String? error
|
||||
bool isConnected, bool isMicrophoneEnabled, bool isCameraEnabled, bool isScreenSharing, String? error
|
||||
});
|
||||
|
||||
|
||||
@ -133,10 +137,12 @@ class __$CallStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of CallState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isMuted = null,Object? isConnected = null,Object? error = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? isConnected = null,Object? isMicrophoneEnabled = null,Object? isCameraEnabled = null,Object? isScreenSharing = null,Object? error = freezed,}) {
|
||||
return _then(_CallState(
|
||||
isMuted: null == isMuted ? _self.isMuted : isMuted // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
|
||||
isConnected: null == isConnected ? _self.isConnected : isConnected // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isMicrophoneEnabled: null == isMicrophoneEnabled ? _self.isMicrophoneEnabled : isMicrophoneEnabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isCameraEnabled: null == isCameraEnabled ? _self.isCameraEnabled : isCameraEnabled // ignore: cast_nullable_to_non_nullable
|
||||
as bool,isScreenSharing: null == isScreenSharing ? _self.isScreenSharing : isScreenSharing // ignore: cast_nullable_to_non_nullable
|
||||
as bool,error: freezed == error ? _self.error : error // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
));
|
||||
@ -145,4 +151,152 @@ as String?,
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
mixin _$CallParticipantLive {
|
||||
|
||||
CallParticipant get participant; Participant get remoteParticipant;
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$CallParticipantLiveCopyWith<CallParticipantLive> get copyWith => _$CallParticipantLiveCopyWithImpl<CallParticipantLive>(this as CallParticipantLive, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is CallParticipantLive&&(identical(other.participant, participant) || other.participant == participant)&&(identical(other.remoteParticipant, remoteParticipant) || other.remoteParticipant == remoteParticipant));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,participant,remoteParticipant);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $CallParticipantLiveCopyWith<$Res> {
|
||||
factory $CallParticipantLiveCopyWith(CallParticipantLive value, $Res Function(CallParticipantLive) _then) = _$CallParticipantLiveCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
CallParticipant participant, Participant remoteParticipant
|
||||
});
|
||||
|
||||
|
||||
$CallParticipantCopyWith<$Res> get participant;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$CallParticipantLiveCopyWithImpl<$Res>
|
||||
implements $CallParticipantLiveCopyWith<$Res> {
|
||||
_$CallParticipantLiveCopyWithImpl(this._self, this._then);
|
||||
|
||||
final CallParticipantLive _self;
|
||||
final $Res Function(CallParticipantLive) _then;
|
||||
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? participant = null,Object? remoteParticipant = null,}) {
|
||||
return _then(_self.copyWith(
|
||||
participant: null == participant ? _self.participant : participant // ignore: cast_nullable_to_non_nullable
|
||||
as CallParticipant,remoteParticipant: null == remoteParticipant ? _self.remoteParticipant : remoteParticipant // ignore: cast_nullable_to_non_nullable
|
||||
as Participant,
|
||||
));
|
||||
}
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$CallParticipantCopyWith<$Res> get participant {
|
||||
|
||||
return $CallParticipantCopyWith<$Res>(_self.participant, (value) {
|
||||
return _then(_self.copyWith(participant: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
|
||||
|
||||
class _CallParticipantLive extends CallParticipantLive {
|
||||
const _CallParticipantLive({required this.participant, required this.remoteParticipant}): super._();
|
||||
|
||||
|
||||
@override final CallParticipant participant;
|
||||
@override final Participant remoteParticipant;
|
||||
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$CallParticipantLiveCopyWith<_CallParticipantLive> get copyWith => __$CallParticipantLiveCopyWithImpl<_CallParticipantLive>(this, _$identity);
|
||||
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CallParticipantLive&&(identical(other.participant, participant) || other.participant == participant)&&(identical(other.remoteParticipant, remoteParticipant) || other.remoteParticipant == remoteParticipant));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,participant,remoteParticipant);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CallParticipantLive(participant: $participant, remoteParticipant: $remoteParticipant)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$CallParticipantLiveCopyWith<$Res> implements $CallParticipantLiveCopyWith<$Res> {
|
||||
factory _$CallParticipantLiveCopyWith(_CallParticipantLive value, $Res Function(_CallParticipantLive) _then) = __$CallParticipantLiveCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
CallParticipant participant, Participant remoteParticipant
|
||||
});
|
||||
|
||||
|
||||
@override $CallParticipantCopyWith<$Res> get participant;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$CallParticipantLiveCopyWithImpl<$Res>
|
||||
implements _$CallParticipantLiveCopyWith<$Res> {
|
||||
__$CallParticipantLiveCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _CallParticipantLive _self;
|
||||
final $Res Function(_CallParticipantLive) _then;
|
||||
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? participant = null,Object? remoteParticipant = null,}) {
|
||||
return _then(_CallParticipantLive(
|
||||
participant: null == participant ? _self.participant : participant // ignore: cast_nullable_to_non_nullable
|
||||
as CallParticipant,remoteParticipant: null == remoteParticipant ? _self.remoteParticipant : remoteParticipant // ignore: cast_nullable_to_non_nullable
|
||||
as Participant,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of CallParticipantLive
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$CallParticipantCopyWith<$Res> get participant {
|
||||
|
||||
return $CallParticipantCopyWith<$Res>(_self.participant, (value) {
|
||||
return _then(_self.copyWith(participant: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
@ -6,7 +6,7 @@ part of 'call.dart';
|
||||
// RiverpodGenerator
|
||||
// **************************************************************************
|
||||
|
||||
String _$callNotifierHash() => r'c39e8d88673113bde0b14eb16cd9d86fa549e42c';
|
||||
String _$callNotifierHash() => r'5512070f943d98e999d97549c73e4d5f6e7b3ddd';
|
||||
|
||||
/// See also [CallNotifier].
|
||||
@ProviderFor(CallNotifier)
|
||||
|
@ -62,8 +62,8 @@ class AccountProfileRoute extends _i27.PageRouteInfo<AccountProfileRouteArgs> {
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<AccountProfileRouteArgs>(
|
||||
orElse: () =>
|
||||
AccountProfileRouteArgs(name: pathParams.getString('name')),
|
||||
orElse:
|
||||
() => AccountProfileRouteArgs(name: pathParams.getString('name')),
|
||||
);
|
||||
return _i1.AccountProfileScreen(key: args.key, name: args.name);
|
||||
},
|
||||
@ -81,6 +81,16 @@ class AccountProfileRouteArgs {
|
||||
String toString() {
|
||||
return 'AccountProfileRouteArgs{key: $key, name: $name}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! AccountProfileRouteArgs) return false;
|
||||
return key == other.key && name == other.name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -120,6 +130,16 @@ class AccountRouteArgs {
|
||||
String toString() {
|
||||
return 'AccountRouteArgs{key: $key, isAside: $isAside}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! AccountRouteArgs) return false;
|
||||
return key == other.key && isAside == other.isAside;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ isAside.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -193,6 +213,16 @@ class CallRouteArgs {
|
||||
String toString() {
|
||||
return 'CallRouteArgs{key: $key, roomId: $roomId}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! CallRouteArgs) return false;
|
||||
return key == other.key && roomId == other.roomId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ roomId.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -234,6 +264,16 @@ class ChatDetailRouteArgs {
|
||||
String toString() {
|
||||
return 'ChatDetailRouteArgs{key: $key, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ChatDetailRouteArgs) return false;
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -273,6 +313,16 @@ class ChatListRouteArgs {
|
||||
String toString() {
|
||||
return 'ChatListRouteArgs{key: $key, isAside: $isAside}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ChatListRouteArgs) return false;
|
||||
return key == other.key && isAside == other.isAside;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ isAside.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -314,6 +364,16 @@ class ChatRoomRouteArgs {
|
||||
String toString() {
|
||||
return 'ChatRoomRouteArgs{key: $key, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ChatRoomRouteArgs) return false;
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -385,6 +445,16 @@ class CreatorHubRouteArgs {
|
||||
String toString() {
|
||||
return 'CreatorHubRouteArgs{key: $key, isAside: $isAside}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! CreatorHubRouteArgs) return false;
|
||||
return key == other.key && isAside == other.isAside;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ isAside.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -439,6 +509,16 @@ class EditChatRouteArgs {
|
||||
String toString() {
|
||||
return 'EditChatRouteArgs{key: $key, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EditChatRouteArgs) return false;
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -480,6 +560,16 @@ class EditPublisherRouteArgs {
|
||||
String toString() {
|
||||
return 'EditPublisherRouteArgs{key: $key, name: $name}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EditPublisherRouteArgs) return false;
|
||||
return key == other.key && name == other.name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -521,6 +611,16 @@ class EditRealmRouteArgs {
|
||||
String toString() {
|
||||
return 'EditRealmRouteArgs{key: $key, slug: $slug}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EditRealmRouteArgs) return false;
|
||||
return key == other.key && slug == other.slug;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ slug.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -550,7 +650,8 @@ class EditStickerPacksRoute
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<EditStickerPacksRouteArgs>(
|
||||
orElse: () => EditStickerPacksRouteArgs(
|
||||
orElse:
|
||||
() => EditStickerPacksRouteArgs(
|
||||
pubName: pathParams.getString('name'),
|
||||
packId: pathParams.optString('packId'),
|
||||
),
|
||||
@ -581,6 +682,18 @@ class EditStickerPacksRouteArgs {
|
||||
String toString() {
|
||||
return 'EditStickerPacksRouteArgs{key: $key, pubName: $pubName, packId: $packId}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EditStickerPacksRouteArgs) return false;
|
||||
return key == other.key &&
|
||||
pubName == other.pubName &&
|
||||
packId == other.packId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ pubName.hashCode ^ packId.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -605,7 +718,8 @@ class EditStickersRoute extends _i27.PageRouteInfo<EditStickersRouteArgs> {
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<EditStickersRouteArgs>(
|
||||
orElse: () => EditStickersRouteArgs(
|
||||
orElse:
|
||||
() => EditStickersRouteArgs(
|
||||
packId: pathParams.getString('packId'),
|
||||
id: pathParams.optString('id'),
|
||||
),
|
||||
@ -636,6 +750,16 @@ class EditStickersRouteArgs {
|
||||
String toString() {
|
||||
return 'EditStickersRouteArgs{key: $key, packId: $packId, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EditStickersRouteArgs) return false;
|
||||
return key == other.key && packId == other.packId && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ packId.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -659,8 +783,8 @@ class EventCalanderRoute extends _i27.PageRouteInfo<EventCalanderRouteArgs> {
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<EventCalanderRouteArgs>(
|
||||
orElse: () =>
|
||||
EventCalanderRouteArgs(name: pathParams.getString('name')),
|
||||
orElse:
|
||||
() => EventCalanderRouteArgs(name: pathParams.getString('name')),
|
||||
);
|
||||
return _i14.EventCalanderScreen(key: args.key, name: args.name);
|
||||
},
|
||||
@ -678,6 +802,16 @@ class EventCalanderRouteArgs {
|
||||
String toString() {
|
||||
return 'EventCalanderRouteArgs{key: $key, name: $name}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! EventCalanderRouteArgs) return false;
|
||||
return key == other.key && name == other.name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -717,6 +851,16 @@ class ExploreRouteArgs {
|
||||
String toString() {
|
||||
return 'ExploreRouteArgs{key: $key, isAside: $isAside}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! ExploreRouteArgs) return false;
|
||||
return key == other.key && isAside == other.isAside;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ isAside.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -821,7 +965,8 @@ class NewStickerPacksRoute
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<NewStickerPacksRouteArgs>(
|
||||
orElse: () =>
|
||||
orElse:
|
||||
() =>
|
||||
NewStickerPacksRouteArgs(pubName: pathParams.getString('name')),
|
||||
);
|
||||
return _i12.NewStickerPacksScreen(key: args.key, pubName: args.pubName);
|
||||
@ -840,6 +985,16 @@ class NewStickerPacksRouteArgs {
|
||||
String toString() {
|
||||
return 'NewStickerPacksRouteArgs{key: $key, pubName: $pubName}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! NewStickerPacksRouteArgs) return false;
|
||||
return key == other.key && pubName == other.pubName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ pubName.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -863,8 +1018,8 @@ class NewStickersRoute extends _i27.PageRouteInfo<NewStickersRouteArgs> {
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<NewStickersRouteArgs>(
|
||||
orElse: () =>
|
||||
NewStickersRouteArgs(packId: pathParams.getString('packId')),
|
||||
orElse:
|
||||
() => NewStickersRouteArgs(packId: pathParams.getString('packId')),
|
||||
);
|
||||
return _i13.NewStickersScreen(key: args.key, packId: args.packId);
|
||||
},
|
||||
@ -882,6 +1037,16 @@ class NewStickersRouteArgs {
|
||||
String toString() {
|
||||
return 'NewStickersRouteArgs{key: $key, packId: $packId}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! NewStickersRouteArgs) return false;
|
||||
return key == other.key && packId == other.packId;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ packId.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -940,6 +1105,16 @@ class PostComposeRouteArgs {
|
||||
String toString() {
|
||||
return 'PostComposeRouteArgs{key: $key, originalPost: $originalPost}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! PostComposeRouteArgs) return false;
|
||||
return key == other.key && originalPost == other.originalPost;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ originalPost.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -981,6 +1156,16 @@ class PostDetailRouteArgs {
|
||||
String toString() {
|
||||
return 'PostDetailRouteArgs{key: $key, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! PostDetailRouteArgs) return false;
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1022,6 +1207,16 @@ class PostEditRouteArgs {
|
||||
String toString() {
|
||||
return 'PostEditRouteArgs{key: $key, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! PostEditRouteArgs) return false;
|
||||
return key == other.key && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1046,8 +1241,8 @@ class PublisherProfileRoute
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<PublisherProfileRouteArgs>(
|
||||
orElse: () =>
|
||||
PublisherProfileRouteArgs(name: pathParams.getString('name')),
|
||||
orElse:
|
||||
() => PublisherProfileRouteArgs(name: pathParams.getString('name')),
|
||||
);
|
||||
return _i20.PublisherProfileScreen(key: args.key, name: args.name);
|
||||
},
|
||||
@ -1065,6 +1260,16 @@ class PublisherProfileRouteArgs {
|
||||
String toString() {
|
||||
return 'PublisherProfileRouteArgs{key: $key, name: $name}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! PublisherProfileRouteArgs) return false;
|
||||
return key == other.key && name == other.name;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ name.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1106,6 +1311,16 @@ class RealmDetailRouteArgs {
|
||||
String toString() {
|
||||
return 'RealmDetailRouteArgs{key: $key, slug: $slug}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! RealmDetailRouteArgs) return false;
|
||||
return key == other.key && slug == other.slug;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ slug.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1179,7 +1394,8 @@ class StickerPackDetailRoute
|
||||
builder: (data) {
|
||||
final pathParams = data.inheritedPathParams;
|
||||
final args = data.argsAs<StickerPackDetailRouteArgs>(
|
||||
orElse: () => StickerPackDetailRouteArgs(
|
||||
orElse:
|
||||
() => StickerPackDetailRouteArgs(
|
||||
pubName: pathParams.getString('name'),
|
||||
id: pathParams.getString('packId'),
|
||||
),
|
||||
@ -1210,6 +1426,16 @@ class StickerPackDetailRouteArgs {
|
||||
String toString() {
|
||||
return 'StickerPackDetailRouteArgs{key: $key, pubName: $pubName, id: $id}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! StickerPackDetailRouteArgs) return false;
|
||||
return key == other.key && pubName == other.pubName && id == other.id;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ pubName.hashCode ^ id.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1251,6 +1477,16 @@ class StickersRouteArgs {
|
||||
String toString() {
|
||||
return 'StickersRouteArgs{key: $key, pubName: $pubName}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! StickersRouteArgs) return false;
|
||||
return key == other.key && pubName == other.pubName;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ pubName.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
@ -1300,6 +1536,16 @@ class TabsNavigationWidgetArgs {
|
||||
String toString() {
|
||||
return 'TabsNavigationWidgetArgs{key: $key, child: $child, router: $router}';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
if (other is! TabsNavigationWidgetArgs) return false;
|
||||
return key == other.key && child == other.child && router == other.router;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => key.hashCode ^ child.hashCode ^ router.hashCode;
|
||||
}
|
||||
|
||||
/// generated route for
|
||||
|
@ -1,8 +1,18 @@
|
||||
import 'package:auto_route/annotations.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/call.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/screens/chat/chat.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/chat/call_button.dart';
|
||||
import 'package:island/widgets/chat/call_participant_tile.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
@RoutePage()
|
||||
class CallScreen extends HookConsumerWidget {
|
||||
@ -11,6 +21,9 @@ class CallScreen extends HookConsumerWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final ongoingCall = ref.watch(ongoingCallProvider(roomId));
|
||||
final userInfo = ref.watch(userInfoProvider);
|
||||
final chatRoom = ref.watch(chatroomProvider(roomId));
|
||||
final callState = ref.watch(callNotifierProvider);
|
||||
final callNotifier = ref.read(callNotifierProvider.notifier);
|
||||
|
||||
@ -19,25 +32,327 @@ class CallScreen extends HookConsumerWidget {
|
||||
return null;
|
||||
}, []);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Audio Call')),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
if (callState.error != null)
|
||||
Text(callState.error!, style: const TextStyle(color: Colors.red)),
|
||||
IconButton(
|
||||
icon: Icon(callState.isMuted ? Icons.mic_off : Icons.mic),
|
||||
onPressed: callNotifier.toggleMute,
|
||||
final actionButtonStyle = ButtonStyle(
|
||||
minimumSize: const MaterialStatePropertyAll(Size(24, 24)),
|
||||
);
|
||||
|
||||
final viewMode = useState<String>('grid');
|
||||
|
||||
return AppScaffold(
|
||||
appBar: AppBar(
|
||||
leading: PageBackButton(
|
||||
onWillPop: () {
|
||||
callNotifier.disconnect().then((_) {
|
||||
callNotifier.dispose();
|
||||
});
|
||||
},
|
||||
),
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
chatRoom.whenOrNull()?.name ?? 'loading'.tr(),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
Text(
|
||||
callState.isConnected
|
||||
? Duration(
|
||||
milliseconds:
|
||||
(DateTime.now().millisecondsSinceEpoch -
|
||||
(ongoingCall
|
||||
.value
|
||||
?.createdAt
|
||||
.millisecondsSinceEpoch ??
|
||||
0)),
|
||||
).toString()
|
||||
: 'Connecting',
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.grid_view),
|
||||
tooltip: 'Grid View',
|
||||
onPressed: () => viewMode.value = 'grid',
|
||||
color:
|
||||
viewMode.value == 'grid'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(Icons.view_agenda),
|
||||
tooltip: 'Stage View',
|
||||
onPressed: () => viewMode.value = 'stage',
|
||||
color:
|
||||
viewMode.value == 'stage'
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(8),
|
||||
],
|
||||
),
|
||||
body:
|
||||
callState.error != null
|
||||
? Center(
|
||||
child: Text(
|
||||
callState.error!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
)
|
||||
: Column(
|
||||
children: [
|
||||
Card(
|
||||
margin: const EdgeInsets.only(left: 12, right: 12, top: 8),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
Builder(
|
||||
builder: (context) {
|
||||
if (callNotifier.localParticipant == null) {
|
||||
return CircularProgressIndicator().center();
|
||||
}
|
||||
return SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
child:
|
||||
SpeakingRippleAvatar(
|
||||
isSpeaking:
|
||||
callNotifier
|
||||
.localParticipant!
|
||||
.isSpeaking,
|
||||
audioLevel:
|
||||
callNotifier
|
||||
.localParticipant!
|
||||
.audioLevel,
|
||||
pictureId:
|
||||
userInfo.value?.profile.pictureId,
|
||||
size: 36,
|
||||
).center(),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
callState.isMicrophoneEnabled
|
||||
? Icons.mic
|
||||
: Icons.mic_off,
|
||||
),
|
||||
onPressed: () {
|
||||
callNotifier.toggleMicrophone();
|
||||
},
|
||||
style: actionButtonStyle,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
callState.isCameraEnabled
|
||||
? Icons.videocam
|
||||
: Icons.videocam_off,
|
||||
),
|
||||
onPressed: () {
|
||||
callNotifier.toggleCamera();
|
||||
},
|
||||
style: actionButtonStyle,
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
callState.isScreenSharing
|
||||
? Icons.stop_screen_share
|
||||
: Icons.screen_share,
|
||||
),
|
||||
onPressed: () {
|
||||
callNotifier.toggleScreenShare();
|
||||
},
|
||||
style: actionButtonStyle,
|
||||
),
|
||||
],
|
||||
).padding(all: 16),
|
||||
),
|
||||
Expanded(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
if (!callState.isConnected) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
}
|
||||
if (callNotifier.participants.isEmpty) {
|
||||
return const Center(
|
||||
child: Text('No participants in call'),
|
||||
);
|
||||
}
|
||||
final participants = callNotifier.participants;
|
||||
final allAudioOnly = participants.every(
|
||||
(p) =>
|
||||
!(p.hasVideo &&
|
||||
p.remoteParticipant.trackPublications.values
|
||||
.any(
|
||||
(pub) =>
|
||||
pub.track != null &&
|
||||
pub.kind == TrackType.VIDEO,
|
||||
)),
|
||||
);
|
||||
if (allAudioOnly) {
|
||||
// Audio-only: show avatars in a compact row
|
||||
return Center(
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Wrap(
|
||||
crossAxisAlignment: WrapCrossAlignment.center,
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 8,
|
||||
runSpacing: 8,
|
||||
children: [
|
||||
for (final live in participants)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
child: SpeakingRippleAvatar(
|
||||
isSpeaking: live.isSpeaking,
|
||||
audioLevel:
|
||||
live.remoteParticipant.audioLevel,
|
||||
pictureId:
|
||||
live
|
||||
.participant
|
||||
.profile
|
||||
?.account
|
||||
.profile
|
||||
.pictureId,
|
||||
size: 72,
|
||||
),
|
||||
),
|
||||
if (callState.isConnected)
|
||||
const Text('Connected')
|
||||
else
|
||||
const CircularProgressIndicator(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (viewMode.value == 'stage') {
|
||||
// Stage view: show main speaker(s) large, others in row
|
||||
final mainSpeakers =
|
||||
participants
|
||||
.where(
|
||||
(p) => p
|
||||
.remoteParticipant
|
||||
.trackPublications
|
||||
.values
|
||||
.any(
|
||||
(pub) =>
|
||||
pub.track != null &&
|
||||
pub.kind == TrackType.VIDEO,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
if (mainSpeakers.isEmpty && participants.isNotEmpty) {
|
||||
mainSpeakers.add(participants.first);
|
||||
}
|
||||
final others =
|
||||
participants
|
||||
.where((p) => !mainSpeakers.contains(p))
|
||||
.toList();
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
for (final speaker in mainSpeakers)
|
||||
Expanded(
|
||||
child:
|
||||
AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
child: ClipRRect(
|
||||
borderRadius:
|
||||
BorderRadius.circular(8),
|
||||
child: Column(
|
||||
children: [
|
||||
CallParticipantTile(
|
||||
live: speaker,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
).center(),
|
||||
),
|
||||
],
|
||||
).padding(horizontal: 12),
|
||||
),
|
||||
if (others.isNotEmpty)
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: ListView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
children: [
|
||||
for (final other in others)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
),
|
||||
child: CallParticipantTile(
|
||||
live: other,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
// Default: grid view
|
||||
return GridView.builder(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12,
|
||||
vertical: 8,
|
||||
),
|
||||
gridDelegate:
|
||||
SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount:
|
||||
isWidestScreen(context)
|
||||
? 4
|
||||
: isWiderScreen(context)
|
||||
? 3
|
||||
: 2,
|
||||
childAspectRatio: 16 / 9,
|
||||
crossAxisSpacing: 8,
|
||||
mainAxisSpacing: 8,
|
||||
),
|
||||
itemCount: participants.length,
|
||||
itemBuilder: (context, idx) {
|
||||
final live = participants[idx];
|
||||
return AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: Card(
|
||||
margin: EdgeInsets.zero,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Column(
|
||||
children: [CallParticipantTile(live: live)],
|
||||
),
|
||||
),
|
||||
),
|
||||
).center();
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -244,6 +244,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
Tab(
|
||||
child: Text(
|
||||
'chatTabAll'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
),
|
||||
@ -252,6 +253,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
Tab(
|
||||
child: Text(
|
||||
'chatTabDirect'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
),
|
||||
@ -260,6 +262,7 @@ class ChatListScreen extends HookConsumerWidget {
|
||||
Tab(
|
||||
child: Text(
|
||||
'chatTabGroup'.tr(),
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).appBarTheme.foregroundColor!,
|
||||
),
|
||||
|
@ -179,12 +179,14 @@ class AppScaffold extends StatelessWidget {
|
||||
|
||||
class PageBackButton extends StatelessWidget {
|
||||
final List<Shadow>? shadows;
|
||||
const PageBackButton({super.key, this.shadows});
|
||||
final VoidCallback? onWillPop;
|
||||
const PageBackButton({super.key, this.shadows, this.onWillPop});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
onWillPop?.call();
|
||||
context.router.maybePop();
|
||||
},
|
||||
icon: Icon(
|
||||
|
114
lib/widgets/chat/call_participant_tile.dart
Normal file
114
lib/widgets/chat/call_participant_tile.dart
Normal file
@ -0,0 +1,114 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:island/pods/call.dart';
|
||||
import 'package:island/widgets/content/cloud_files.dart';
|
||||
import 'package:livekit_client/livekit_client.dart';
|
||||
|
||||
class SpeakingRippleAvatar extends StatelessWidget {
|
||||
final bool isSpeaking;
|
||||
final double audioLevel;
|
||||
final String? pictureId;
|
||||
final double size;
|
||||
|
||||
const SpeakingRippleAvatar({
|
||||
super.key,
|
||||
required this.isSpeaking,
|
||||
required this.audioLevel,
|
||||
required this.pictureId,
|
||||
this.size = 96,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final avatarRadius = size / 2;
|
||||
final clampedLevel = audioLevel.clamp(0.0, 1.0);
|
||||
final rippleRadius = avatarRadius + clampedLevel * (size * 0.333);
|
||||
return TweenAnimationBuilder<double>(
|
||||
tween: Tween<double>(
|
||||
begin: avatarRadius,
|
||||
end: isSpeaking ? rippleRadius : avatarRadius,
|
||||
),
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeOut,
|
||||
builder: (context, animatedRadius, child) {
|
||||
return Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
if (isSpeaking)
|
||||
Container(
|
||||
width: animatedRadius * 2,
|
||||
height: animatedRadius * 2,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: Colors.green.withOpacity(0.75 + 0.25 * clampedLevel),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: size,
|
||||
height: size,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(shape: BoxShape.circle),
|
||||
child: ProfilePictureWidget(fileId: pictureId, radius: size / 2),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CallParticipantTile extends StatelessWidget {
|
||||
final CallParticipantLive live;
|
||||
|
||||
const CallParticipantTile({super.key, required this.live});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final hasVideo =
|
||||
live.hasVideo &&
|
||||
live.remoteParticipant.trackPublications.values
|
||||
.where((pub) => pub.track != null && pub.kind == TrackType.VIDEO)
|
||||
.isNotEmpty;
|
||||
final audioLevel = live.remoteParticipant.audioLevel;
|
||||
|
||||
if (hasVideo) {
|
||||
return Stack(
|
||||
fit: StackFit.loose,
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 16 / 9,
|
||||
child: VideoTrackRenderer(
|
||||
live.remoteParticipant.trackPublications.values
|
||||
.where((track) => track.kind == TrackType.VIDEO)
|
||||
.first
|
||||
.track
|
||||
as VideoTrack,
|
||||
renderMode: VideoRenderMode.platformView,
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
left: 8,
|
||||
right: 8,
|
||||
bottom: 8,
|
||||
child: Text(
|
||||
live.participant.profile?.account.nick ??
|
||||
'${'unknown'.tr()}\'s video',
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 14, color: Colors.white),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
} else {
|
||||
return SpeakingRippleAvatar(
|
||||
isSpeaking: live.isSpeaking,
|
||||
audioLevel: audioLevel,
|
||||
pictureId: live.participant.profile?.account.profile.pictureId,
|
||||
size: 84,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "Solian.app"
|
||||
BuildableName = "island.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -31,7 +31,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "Solian.app"
|
||||
BuildableName = "island.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -66,7 +66,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "Solian.app"
|
||||
BuildableName = "island.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
@ -83,7 +83,7 @@
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "33CC10EC2044A3C60003C045"
|
||||
BuildableName = "Solian.app"
|
||||
BuildableName = "island.app"
|
||||
BlueprintName = "Runner"
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
|
@ -8,6 +8,8 @@
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
|
@ -4,5 +4,17 @@
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.audio-input</key>
|
||||
<true/>
|
||||
<key>com.apple.security.device.camera</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.downloads.read-write</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.client</key>
|
||||
<true/>
|
||||
<key>com.apple.security.network.server</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
|
12
pubspec.lock
12
pubspec.lock
@ -77,18 +77,18 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: auto_route
|
||||
sha256: "89bc5d17d8c575399891194b8cd02b39f52a8512c730052f17ebe443cdcb9109"
|
||||
sha256: eae18fcd3e3762eb6074a3560c0f411d1e36bd9f8d3eed9c15ed1c577e8d1815
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.1"
|
||||
version: "10.1.0"
|
||||
auto_route_generator:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: auto_route_generator
|
||||
sha256: "8e622d26dc6be4bf496d47969e3e9ba555c3abcf2290da6abfa43cbd4f57fa52"
|
||||
sha256: "9e3846fcbeacba5c362557328dd8c8fbc953b6a0cbc3395365e8d8f92eea29c4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.0.1"
|
||||
version: "10.1.0"
|
||||
avatar_stack:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1225,10 +1225,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: material_symbols_icons
|
||||
sha256: d45b6c36c3effa8cb51b1afb8698107d5ff1f88fa4631428f34a8a01abc295d7
|
||||
sha256: "7c50901b39d1ad645ee25d920aed008061e1fd541a897b4ebf2c01d966dbf16b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2815.0"
|
||||
version: "4.2815.1"
|
||||
media_kit:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
Loading…
x
Reference in New Issue
Block a user