diff --git a/.cspell/dart_flutter.txt b/.cspell/dart_flutter.txt index a618a83cf00..59636699ba9 100644 --- a/.cspell/dart_flutter.txt +++ b/.cspell/dart_flutter.txt @@ -14,4 +14,5 @@ sublist todos unawaited unfocus +videocam writeln diff --git a/.cspell/nextcloud.txt b/.cspell/nextcloud.txt index 16a4f5a97d8..d80edf30877 100644 --- a/.cspell/nextcloud.txt +++ b/.cspell/nextcloud.txt @@ -52,6 +52,7 @@ trashbin turnservers undelete unifiedpush +unmute unstar updatenotification uppush diff --git a/.cspell/tools.txt b/.cspell/tools.txt index d135e9991ca..0a37300b700 100644 --- a/.cspell/tools.txt +++ b/.cspell/tools.txt @@ -63,6 +63,7 @@ strfreev subprojects sysroot tsvg +webrtc werror xxxh xxxhdpi diff --git a/README.md b/README.md index e1cc420c716..2ae9245f990 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,7 @@ See [here](packages/app/README.md) for screenshots. | [News](packages/neon/neon_news) | :heavy_check_mark: | | [Notes](packages/neon/neon_notes) | :heavy_check_mark: | | [Notifications](packages/neon/neon_notifications) | :heavy_check_mark: | +| [Talk](packages/neon/neon_spreed) | :heavy_check_mark: | | Activity | :rocket: | | Calendar | :rocket: | | Contacts | :rocket: | @@ -62,7 +63,6 @@ See [here](packages/app/README.md) for screenshots. | Deck | :rocket: | | Photos | :rocket: | | Photos | :rocket: | -| Talk | :construction: | | Tasks | :rocket: | ## Platform support diff --git a/docs/architecture.puml b/docs/architecture.puml index 99b32e140df..df952333b37 100644 --- a/docs/architecture.puml +++ b/docs/architecture.puml @@ -13,6 +13,7 @@ package "Clients" { component neon_news component neon_notes component neon_notifications + component neon_spreed } package "OpenAPI" { @@ -27,12 +28,14 @@ app ..> neon_files app ..> neon_news app ..> neon_notes app ..> neon_notifications +app ..> neon_spreed neon_dashboard --> neon_framework neon_files --> neon_framework neon_news --> neon_framework neon_notes --> neon_framework neon_notifications --> neon_framework +neon_spreed --> neon_framework neon_framework --> nextcloud diff --git a/docs/architecture.svg b/docs/architecture.svg index e7e3ce00459..51be5110bf7 100644 --- a/docs/architecture.svg +++ b/docs/architecture.svg @@ -1 +1 @@ -Neon frameworkClientsOpenAPIneon_frameworknextcloudsort_boxfile_iconsneon_dashboardneon_filesneon_newsneon_notesneon_notificationsdynamitespecificationsapp \ No newline at end of file +Neon frameworkClientsOpenAPIneon_frameworknextcloudsort_boxfile_iconsneon_dashboardneon_filesneon_newsneon_notesneon_notificationsneon_spreeddynamitespecificationsapp \ No newline at end of file diff --git a/packages/app/android/app/src/main/AndroidManifest.xml b/packages/app/android/app/src/main/AndroidManifest.xml index 29f4f8ff0e2..4c385816b5f 100644 --- a/packages/app/android/app/src/main/AndroidManifest.xml +++ b/packages/app/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,15 @@ + + + + + + + + + #include #include +#include #include #include #include @@ -23,6 +24,9 @@ void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); file_selector_plugin_register_with_registrar(file_selector_linux_registrar); + g_autoptr(FlPluginRegistrar) flutter_webrtc_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterWebRTCPlugin"); + flutter_web_r_t_c_plugin_register_with_registrar(flutter_webrtc_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/packages/app/linux/flutter/generated_plugins.cmake b/packages/app/linux/flutter/generated_plugins.cmake index 64a9330bf03..f1c01d0d2ce 100644 --- a/packages/app/linux/flutter/generated_plugins.cmake +++ b/packages/app/linux/flutter/generated_plugins.cmake @@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color emoji_picker_flutter file_selector_linux + flutter_webrtc screen_retriever url_launcher_linux window_manager diff --git a/packages/app/pubspec.lock b/packages/app/pubspec.lock index 686cd3bf19f..c482668c8dd 100644 --- a/packages/app/pubspec.lock +++ b/packages/app/pubspec.lock @@ -273,6 +273,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.6" + dart_webrtc: + dependency: transitive + description: + name: dart_webrtc + sha256: "5cbc40bd9b33d0c9b8004cff52e9883c71f0f54799afc8faca77535eeb9ef857" + url: "https://pub.dev" + source: hosted + version: "1.2.1" dbus: dependency: transitive description: @@ -297,6 +305,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.16.4+3" + diffutil_dart: + dependency: transitive + description: + name: diffutil_dart + sha256: e0297e4600b9797edff228ed60f4169a778ea357691ec98408fa3b72994c7d06 + url: "https://pub.dev" + source: hosted + version: "3.0.0" dynamic_color: dependency: transitive description: @@ -320,6 +336,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.0" + equatable: + dependency: transitive + description: + name: equatable + sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + url: "https://pub.dev" + source: hosted + version: "2.0.5" fake_async: dependency: transitive description: @@ -412,6 +436,22 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_chat_types: + dependency: transitive + description: + name: flutter_chat_types + sha256: e285b588f6d19d907feb1f6d912deaf22e223656769c34093b64e1c59b094fb9 + url: "https://pub.dev" + source: hosted + version: "3.6.2" + flutter_chat_ui: + dependency: transitive + description: + name: flutter_chat_ui + sha256: c8580c85e2d29359ffc84147e643d08d883eb6e757208652377f0105ef58807f + url: "https://pub.dev" + source: hosted + version: "1.6.12" flutter_file_dialog: dependency: transitive description: @@ -428,6 +468,22 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.0-beta.2" + flutter_link_previewer: + dependency: transitive + description: + name: flutter_link_previewer + sha256: "007069e60f42419fb59872beb7a3cc3ea21e9f1bdff5d40239f376fa62ca9f20" + url: "https://pub.dev" + source: hosted + version: "3.2.2" + flutter_linkify: + dependency: transitive + description: + name: flutter_linkify + sha256: "74669e06a8f358fee4512b4320c0b80e51cffc496607931de68d28f099254073" + url: "https://pub.dev" + source: hosted + version: "6.0.0" flutter_local_notifications: dependency: transitive description: @@ -481,6 +537,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.10" + flutter_parsed_text: + dependency: transitive + description: + name: flutter_parsed_text + sha256: "529cf5793b7acdf16ee0f97b158d0d4ba0bf06e7121ef180abe1a5b59e32c1e2" + url: "https://pub.dev" + source: hosted + version: "2.2.1" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -507,6 +571,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_webrtc: + dependency: transitive + description: + name: flutter_webrtc + sha256: "2f17fb96e0c9c6ff75f6b1c36d94755461fc7f36a5c28386f5ee5a18b98688c8" + url: "https://pub.dev" + source: hosted + version: "0.9.48+hotfix.1" flutter_zxing: dependency: transitive description: @@ -707,6 +779,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.1" + linkify: + dependency: transitive + description: + name: linkify + sha256: "4139ea77f4651ab9c315b577da2dd108d9aa0bd84b5d03d33323f1970c645832" + url: "https://pub.dev" + source: hosted + version: "5.0.0" list_counter: dependency: transitive description: @@ -987,6 +1067,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.2" + photo_view: + dependency: transitive + description: + name: photo_view + sha256: "8036802a00bae2a78fc197af8a158e3e2f7b500561ed23b4c458107685e645bb" + url: "https://pub.dev" + source: hosted + version: "0.14.0" platform: dependency: transitive description: @@ -995,6 +1083,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.4" + platform_detect: + dependency: transitive + description: + name: platform_detect + sha256: "08f4ee79c0e1c4858d37e06b22352a3ebdef5466b613749a3adb03e703d4f5b0" + url: "https://pub.dev" + source: hosted + version: "2.0.11" plugin_platform_interface: dependency: transitive description: @@ -1115,6 +1211,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.1.9" + scroll_to_index: + dependency: transitive + description: + name: scroll_to_index + sha256: b707546e7500d9f070d63e5acf74fd437ec7eeeb68d3412ef7b0afada0b4f176 + url: "https://pub.dev" + source: hosted + version: "3.0.1" scrollable_positioned_list: dependency: transitive description: @@ -1487,6 +1591,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + visibility_detector: + dependency: transitive + description: + name: visibility_detector + sha256: dd5cc11e13494f432d15939c3aa8ae76844c42b723398643ce9addb88a5ed420 + url: "https://pub.dev" + source: hosted + version: "0.4.0+2" vm_service: dependency: transitive description: @@ -1527,6 +1639,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.5.0" + webrtc_interface: + dependency: transitive + description: + name: webrtc_interface + sha256: "2efbd3e4e5ebeb2914253bcc51dafd3053c4b87b43f3076c74835a9deecbae3a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" webview_flutter: dependency: transitive description: diff --git a/packages/neon/neon_talk/analysis_options.yaml b/packages/neon/neon_talk/analysis_options.yaml index 66de1efdbdd..6c0ada98cc1 100644 --- a/packages/neon/neon_talk/analysis_options.yaml +++ b/packages/neon/neon_talk/analysis_options.yaml @@ -1,5 +1,10 @@ include: package:neon_lints/flutter.yaml +linter: + rules: + # TODO + public_member_api_docs: false + analyzer: exclude: - lib/l10n/** diff --git a/packages/neon/neon_talk/lib/l10n/en.arb b/packages/neon/neon_talk/lib/l10n/en.arb index ccf41500b73..23fde3234a3 100644 --- a/packages/neon/neon_talk/lib/l10n/en.arb +++ b/packages/neon/neon_talk/lib/l10n/en.arb @@ -1,5 +1,18 @@ { "@@locale": "en", "actorSelf": "You", - "actorGuest": "Guest" + "actorGuest": "Guest", + "roomCreate": "Create room", + "roomCreateUserName": "User name", + "roomCreateGroupName": "Group name", + "roomCreateRoomName": "Room name", + "roomTypeOneToOne": "Private", + "roomTypeGroup": "Group", + "roomTypePublic": "Public", + "callStart": "Start call", + "callJoin": "Join call", + "callLeave": "Leave call", + "screenSharingSelectScreen": "Select screen", + "screenSharingSelectScreenScreens": "Screens", + "screenSharingSelectScreenWindows": "Windows" } diff --git a/packages/neon/neon_talk/lib/l10n/localizations.dart b/packages/neon/neon_talk/lib/l10n/localizations.dart index e1fe29054c2..486819dc971 100644 --- a/packages/neon/neon_talk/lib/l10n/localizations.dart +++ b/packages/neon/neon_talk/lib/l10n/localizations.dart @@ -100,6 +100,84 @@ abstract class TalkLocalizations { /// In en, this message translates to: /// **'Guest'** String get actorGuest; + + /// No description provided for @roomCreate. + /// + /// In en, this message translates to: + /// **'Create room'** + String get roomCreate; + + /// No description provided for @roomCreateUserName. + /// + /// In en, this message translates to: + /// **'User name'** + String get roomCreateUserName; + + /// No description provided for @roomCreateGroupName. + /// + /// In en, this message translates to: + /// **'Group name'** + String get roomCreateGroupName; + + /// No description provided for @roomCreateRoomName. + /// + /// In en, this message translates to: + /// **'Room name'** + String get roomCreateRoomName; + + /// No description provided for @roomTypeOneToOne. + /// + /// In en, this message translates to: + /// **'Private'** + String get roomTypeOneToOne; + + /// No description provided for @roomTypeGroup. + /// + /// In en, this message translates to: + /// **'Group'** + String get roomTypeGroup; + + /// No description provided for @roomTypePublic. + /// + /// In en, this message translates to: + /// **'Public'** + String get roomTypePublic; + + /// No description provided for @callStart. + /// + /// In en, this message translates to: + /// **'Start call'** + String get callStart; + + /// No description provided for @callJoin. + /// + /// In en, this message translates to: + /// **'Join call'** + String get callJoin; + + /// No description provided for @callLeave. + /// + /// In en, this message translates to: + /// **'Leave call'** + String get callLeave; + + /// No description provided for @screenSharingSelectScreen. + /// + /// In en, this message translates to: + /// **'Select screen'** + String get screenSharingSelectScreen; + + /// No description provided for @screenSharingSelectScreenScreens. + /// + /// In en, this message translates to: + /// **'Screens'** + String get screenSharingSelectScreenScreens; + + /// No description provided for @screenSharingSelectScreenWindows. + /// + /// In en, this message translates to: + /// **'Windows'** + String get screenSharingSelectScreenWindows; } class _TalkLocalizationsDelegate extends LocalizationsDelegate { diff --git a/packages/neon/neon_talk/lib/l10n/localizations_en.dart b/packages/neon/neon_talk/lib/l10n/localizations_en.dart index 4a01892dddb..92fae33546f 100644 --- a/packages/neon/neon_talk/lib/l10n/localizations_en.dart +++ b/packages/neon/neon_talk/lib/l10n/localizations_en.dart @@ -9,4 +9,43 @@ class TalkLocalizationsEn extends TalkLocalizations { @override String get actorGuest => 'Guest'; + + @override + String get roomCreate => 'Create room'; + + @override + String get roomCreateUserName => 'User name'; + + @override + String get roomCreateGroupName => 'Group name'; + + @override + String get roomCreateRoomName => 'Room name'; + + @override + String get roomTypeOneToOne => 'Private'; + + @override + String get roomTypeGroup => 'Group'; + + @override + String get roomTypePublic => 'Public'; + + @override + String get callStart => 'Start call'; + + @override + String get callJoin => 'Join call'; + + @override + String get callLeave => 'Leave call'; + + @override + String get screenSharingSelectScreen => 'Select screen'; + + @override + String get screenSharingSelectScreenScreens => 'Screens'; + + @override + String get screenSharingSelectScreenWindows => 'Windows'; } diff --git a/packages/neon/neon_talk/lib/src/blocs/call.dart b/packages/neon/neon_talk/lib/src/blocs/call.dart new file mode 100644 index 00000000000..8a919e1e92f --- /dev/null +++ b/packages/neon/neon_talk/lib/src/blocs/call.dart @@ -0,0 +1,491 @@ +import 'dart:async'; + +import 'package:built_collection/built_collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:logging/logging.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_talk/src/utils/participants.dart'; +import 'package:nextcloud/nextcloud.dart'; +import 'package:nextcloud/spreed.dart' as spreed; +import 'package:rxdart/rxdart.dart'; + +sealed class TalkCallBloc implements InteractiveBloc { + factory TalkCallBloc( + spreed.SignalingSettings settings, + NextcloudClient client, + String roomToken, + String sessionID, + ) => + _TalkCallBloc( + settings, + client, + roomToken, + sessionID, + ); + + Future leaveCall(); + + // ignore: avoid_positional_boolean_parameters + void changeAudio(bool enabled); + + // ignore: avoid_positional_boolean_parameters + void changeVideo(bool enabled); + + // ignore: avoid_positional_boolean_parameters + void changeScreen(bool enabled); + + BehaviorSubject> get remoteParticipants; + + BehaviorSubject get audioEnabled; + + BehaviorSubject get videoEnabled; + + BehaviorSubject get screenEnabled; + + TalkLocalCallParticipant get localParticipant; +} + +class _TalkCallBloc extends InteractiveBloc implements TalkCallBloc { + _TalkCallBloc( + this.settings, + this.client, + this.roomToken, + this.sessionID, + ) { + unawaited(_setupLocalParticipant().then((_) => refresh())); + + listenForSignalingMessagesSubscription = _pullSignalingMessages().listen(onSignalingMessages); + } + + @override + final log = Logger('TalkCallBloc'); + + final spreed.SignalingSettings settings; + final NextcloudClient client; + final String roomToken; + final String sessionID; + late final StreamSubscription> listenForSignalingMessagesSubscription; + + @override + late TalkLocalCallParticipant localParticipant; + + @override + void dispose() { + unawaited(listenForSignalingMessagesSubscription.cancel()); + for (final participant in remoteParticipants.value) { + participant.dispose(); + } + unawaited(remoteParticipants.close()); + unawaited(audioEnabled.close()); + unawaited(videoEnabled.close()); + unawaited(screenEnabled.close()); + super.dispose(); + } + + @override + final remoteParticipants = BehaviorSubject.seeded(BuiltList()); + + @override + final audioEnabled = BehaviorSubject.seeded(false); + + @override + final videoEnabled = BehaviorSubject.seeded(false); + + @override + final screenEnabled = BehaviorSubject.seeded(false); + + @override + Future refresh() async { + await wrapAction(() async { + await client.spreed.call.joinCall(token: roomToken); + }); + } + + @override + Future leaveCall() async { + await wrapAction(() async { + await client.spreed.call.leaveCall(token: roomToken); + }); + } + + @override + Future changeAudio(bool enabled) async { + audioEnabled.add(enabled); + await _updateLocalParticipant(); + } + + @override + Future changeVideo(bool enabled) async { + videoEnabled.add(enabled); + await _updateLocalParticipant(); + } + + @override + void changeScreen(bool enabled) { + screenEnabled.add(enabled); + } + + Future _setupLocalParticipant() async { + final stream = await navigator.mediaDevices.getUserMedia({ + 'audio': true, + 'video': true, + }); + for (final track in stream.getTracks()) { + track.enabled = false; + } + final renderer = await _getInitializedRenderer(); + renderer.srcObject = stream; + localParticipant = TalkLocalCallParticipant( + settings.userId!, + sessionID, + renderer, + stream, + ); + } + + Future _sendSignalingMessages(BuiltList messages) async { + for (final message in messages) { + // TODO: Send all messages at once, needs to send it over the body and not the URL, because that gets too long + await wrapAction(() async { + await client.spreed.signaling.sendMessages( + token: roomToken, + messages: ContentString( + (b) => b + ..content = BuiltList([ + spreed.SignalingSendMessagesMessages( + (b) => b + ..fn.update( + (b) => b..content = message, + ) + ..sessionId = sessionID, + ), + ]), + ), + ); + }); + } + } + + TalkRemoteCallParticipant? _getRemoteParticipant(String sessionID) { + final remoteParticipantMatches = + remoteParticipants.value.where((participant) => participant.sessionID == sessionID); + if (remoteParticipantMatches.length == 1) { + return remoteParticipantMatches.single; + } + return null; + } + + Future _updateRemoteParticipant( + String sessionID, + Future Function(TalkRemoteCallParticipant) call, + ) async { + final updatedRemoteParticipants = ListBuilder(); + for (final remoteParticipant in remoteParticipants.value) { + if (remoteParticipant.sessionID == sessionID) { + updatedRemoteParticipants.add(await call(remoteParticipant)); + } else { + updatedRemoteParticipants.add(remoteParticipant); + } + } + remoteParticipants.add(updatedRemoteParticipants.build()); + } + + Stream> _pullSignalingMessages() async* { + // TODO: Cancel the loop + while (true) { + try { + yield (await client.spreed.signaling.pullMessages(token: roomToken)).body.ocs.data; + } on Exception catch (e, s) { + if (e is DynamiteStatusCodeException && e.statusCode >= 500) { + continue; + } + debugPrint(e.toString()); + debugPrint(s.toString()); + addError(e); + } + } + } + + Future _updateLocalParticipant() async { + if (localParticipant.stream != null) { + for (final track in localParticipant.stream!.getTracks()) { + switch (track.kind) { + case 'video': + track.enabled = videoEnabled.value; + case 'audio': + track.enabled = audioEnabled.value; + default: + debugPrint('Unknown track kind ${track.kind}'); + } + } + } + + await _sendSignalingMessages(_generateMuteMessages(remoteParticipants.value)); + } + + BuiltList _generateMuteMessages(BuiltList participants) => + BuiltList.build((b) { + for (final remoteParticipant in participants) { + for (final entry in { + spreed.SignalingMuteMessage_Payload_Name.audio: audioEnabled.value, + spreed.SignalingMuteMessage_Payload_Name.video: videoEnabled.value, + }.entries) { + b.add( + ( + signalingICECandidateMessage: null, + signalingMuteMessage: spreed.SignalingMuteMessage( + (b) => b + ..from = sessionID + ..to = remoteParticipant.sessionID + ..type = entry.value ? spreed.SignalingMessageType.unmute : spreed.SignalingMessageType.mute + ..payload.update( + (b) => b.name = entry.key, + ), + ), + signalingSessionDescriptionMessage: null, + ), + ); + } + } + }); + + bool _isWeakerParticipant(TalkRemoteCallParticipant remoteParticipant) => + sessionID.compareTo(remoteParticipant.sessionID) > 0; + + Future _sendOffer(TalkRemoteCallParticipant remoteParticipant) async { + debugPrint('Sending offer to ${remoteParticipant.userID} ${remoteParticipant.sessionID}'); + final connection = await _setupConnection(remoteParticipant); + final localSDP = await connection.createOffer(); + await connection.setLocalDescription(localSDP); + await _sendSignalingMessages( + BuiltList.build((b) { + b + ..add( + ( + signalingICECandidateMessage: null, + signalingMuteMessage: null, + signalingSessionDescriptionMessage: spreed.SignalingSessionDescriptionMessage( + (b) => b + ..from = sessionID + ..to = remoteParticipant.sessionID + ..type = spreed.SignalingMessageType.offer + ..payload.update( + (b) => b + ..type = spreed.SignalingSessionDescriptionMessage_Payload_Type.offer + ..sdp = localSDP.sdp + ..nick = '', + ), + ), + ), + ) + ..addAll(_generateMuteMessages(BuiltList.from([remoteParticipant]))); + }), + ); + } + + Future _setupConnection(TalkRemoteCallParticipant remoteParticipant) async { + final connection = await createPeerConnection( + { + 'sdpSemantics': 'unified-plan', + 'iceServers': [ + ...settings.stunservers.map((s) => s.toJson()), + ...settings.turnservers.map((s) => s.toJson()), + ], + }, + ); + connection + ..onTrack = (event) async { + if (event.track.kind == 'video') { + final stream = event.streams.first; + final renderer = await _getInitializedRenderer(); + renderer.srcObject = stream; + await _updateRemoteParticipant( + remoteParticipant.sessionID, + (remoteParticipant) async => remoteParticipant + ..renderer = renderer + ..stream = stream, + ); + } + } + ..onIceCandidate = (candidate) async { + await _sendSignalingMessages( + BuiltList.build((b) { + b.add( + ( + signalingICECandidateMessage: spreed.SignalingICECandidateMessage( + (b) => b + ..from = sessionID + ..to = remoteParticipant.sessionID + ..type = spreed.SignalingMessageType.answer + ..payload.update( + (b) => b + ..candidate.update( + (b) => b + ..candidate = candidate.candidate + ..sdpMid = candidate.sdpMid + ..sdpMLineIndex = candidate.sdpMLineIndex, + ), + ), + ), + signalingMuteMessage: null, + signalingSessionDescriptionMessage: null, + ), + ); + }), + ); + } + ..onIceGatheringState = print + ..onIceConnectionState = print + ..onConnectionState = print; + await remoteParticipant.acceptNewConnection(connection); + await remoteParticipant.acceptNewLocalStream(localParticipant.stream); + + return connection; + } + + Future onSignalingMessages(BuiltList messages) async { + for (final message in messages) { + if (message.signalingSessions != null) { + final users = message.signalingSessions!.data.where( + (user) => + spreed.ParticipantInCallFlag.values.byBinary(user.inCall).contains(spreed.ParticipantInCallFlag.inCall), + ); + + final currentParticipants = remoteParticipants.value; + final updatedParticipants = ListBuilder(); + + for (final currentParticipant in currentParticipants) { + if (users.where((user) => user.userId == currentParticipant.userID).isNotEmpty) { + updatedParticipants.add(currentParticipant); + } else { + currentParticipant.dispose(); + } + } + + for (final user in users) { + if (currentParticipants.where((currentParticipant) => user.userId == currentParticipant.userID).isEmpty && + user.sessionId != sessionID) { + final remoteParticipant = TalkRemoteCallParticipant( + user.userId, + user.sessionId, + null, + null, + null, + null, + ); + if (_isWeakerParticipant(remoteParticipant)) { + await _sendOffer(remoteParticipant); + } + updatedParticipants.add(remoteParticipant); + } + } + remoteParticipants.add(updatedParticipants.build()); + + continue; + } + + if (message.signalingMessageWrapper != null) { + final signalingMessage = message.signalingMessageWrapper!.data.content; + + if (signalingMessage.signalingSessionDescriptionMessage != null) { + final remoteSDP = signalingMessage.signalingSessionDescriptionMessage!; + + await _updateRemoteParticipant(remoteSDP.from, (remoteParticipant) async { + switch (remoteSDP.payload.type) { + case spreed.SignalingSessionDescriptionMessage_Payload_Type.offer: + debugPrint('Received offer from ${remoteParticipant.userID} ${remoteParticipant.sessionID}'); + final connection = await _setupConnection(remoteParticipant); + await connection.setRemoteDescription( + RTCSessionDescription( + remoteSDP.payload.sdp, + 'offer', + ), + ); + final localSDP = await connection.createAnswer(); + await connection.setLocalDescription(localSDP); + await _sendSignalingMessages( + BuiltList.build((b) { + b + ..add( + ( + signalingICECandidateMessage: null, + signalingMuteMessage: null, + signalingSessionDescriptionMessage: spreed.SignalingSessionDescriptionMessage( + (b) => b + ..from = sessionID + ..to = remoteParticipant.sessionID + ..type = spreed.SignalingMessageType.answer + ..payload.update( + (b) => b + ..type = spreed.SignalingSessionDescriptionMessage_Payload_Type.answer + ..sdp = localSDP.sdp + ..nick = '', + ), + ), + ), + ) + ..addAll(_generateMuteMessages(BuiltList.from([remoteParticipant]))); + }), + ); + case spreed.SignalingSessionDescriptionMessage_Payload_Type.answer: + debugPrint('Received answer from ${remoteParticipant.userID} ${remoteParticipant.sessionID}'); + } + + return remoteParticipant; + }); + + continue; + } + + if (signalingMessage.signalingICECandidateMessage != null) { + final iceCandidateMessage = signalingMessage.signalingICECandidateMessage!; + final remoteParticipant = _getRemoteParticipant(iceCandidateMessage.from); + if (remoteParticipant == null) { + continue; + } + + if (iceCandidateMessage.payload.candidate.candidate.isEmpty) { + // TODO: Handle end-of-candidates properly + continue; + } + + await remoteParticipant.addCandidate( + RTCIceCandidate( + iceCandidateMessage.payload.candidate.candidate, + iceCandidateMessage.payload.candidate.sdpMid, + iceCandidateMessage.payload.candidate.sdpMLineIndex, + ), + ); + + continue; + } + + if (signalingMessage.signalingMuteMessage != null) { + final muteMessage = signalingMessage.signalingMuteMessage!; + + await _updateRemoteParticipant(muteMessage.from, (remoteParticipant) async { + final isUnmute = muteMessage.type == spreed.SignalingMessageType.unmute; + switch (muteMessage.payload.name) { + case spreed.SignalingMuteMessage_Payload_Name.audio: + remoteParticipant.audioEnabled = isUnmute; + case spreed.SignalingMuteMessage_Payload_Name.video: + remoteParticipant.videoEnabled = isUnmute; + } + return remoteParticipant; + }); + + continue; + } + } + + debugPrint('Unknown signaling message ${message.toJson()}'); + } + } +} + +Future _getInitializedRenderer() async { + final renderer = RTCVideoRenderer(); + await renderer.initialize(); + return renderer; +} diff --git a/packages/neon/neon_talk/lib/src/blocs/room.dart b/packages/neon/neon_talk/lib/src/blocs/room.dart new file mode 100644 index 00000000000..fd260299ac6 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/blocs/room.dart @@ -0,0 +1,195 @@ +import 'dart:async'; + +import 'package:built_collection/built_collection.dart'; +import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_framework/models.dart'; +import 'package:neon_framework/utils.dart'; +import 'package:nextcloud/spreed.dart' as spreed; +import 'package:rxdart/rxdart.dart'; + +sealed class TalkRoomBloc implements InteractiveBloc { + factory TalkRoomBloc( + Account account, + spreed.Room room, + ) => + _TalkRoomBloc( + account, + room, + ); + + Future loadMoreMessages(); + + void sendMessage(String message); + + Future leaveRoom(); + + BehaviorSubject> get room; + + BehaviorSubject>> get messages; + + BehaviorSubject get allLoaded; + + BehaviorSubject get sendingMessage; + + BehaviorSubject get lastCommonReadMessageId; + + String get roomToken; + + String get actorId; +} + +class _TalkRoomBloc extends InteractiveBloc implements TalkRoomBloc { + _TalkRoomBloc( + this.account, + spreed.Room r, + ) { + roomToken = r.token; + room.add(Result.success(r)); + + unawaited(refresh()); + } + + @override + final log = Logger('TalkCallBloc'); + + final Account account; + @override + late final String roomToken; + final _limit = 100; + + int? _lastKnownMessageId; + + @override + String get actorId => account.username; + + @override + void dispose() { + unawaited(room.close()); + unawaited(messages.close()); + unawaited(allLoaded.close()); + unawaited(sendingMessage.close()); + unawaited(lastCommonReadMessageId.close()); + super.dispose(); + } + + @override + final allLoaded = BehaviorSubject.seeded(false); + + @override + final lastCommonReadMessageId = BehaviorSubject.seeded(null); + + @override + final messages = BehaviorSubject(); + + @override + final room = BehaviorSubject(); + + @override + final sendingMessage = BehaviorSubject.seeded(null); + + @override + Future refresh() async { + await RequestManager.instance.wrapNextcloud( + account: account, + cacheKey: 'spreed-room-$roomToken', + subject: room, + rawResponse: account.client.spreed.room.joinRoomRaw(token: roomToken), + unwrap: (response) => response.body.ocs.data, + ); + await _loadMessages(force: true); + } + + @override + Future sendMessage(String message) async { + sendingMessage.add(message); + await wrapAction( + () async { + await account.client.spreed.chat.sendMessage( + token: roomToken, + message: message, + ); + }, + refresh: () async { + await _loadMessages(force: true); + }, + ); + sendingMessage.add(null); + } + + @override + Future loadMoreMessages() async { + await _loadMessages(force: false); + } + + @override + Future leaveRoom() async { + await wrapAction(() async { + await account.client.spreed.room.leaveRoom(token: roomToken); + }); + } + + Future _loadMessages({required bool force}) async { + if (!force && (allLoaded.valueOrNull ?? false)) { + return; + } + + final previousData = messages.valueOrNull?.data; + try { + messages.add( + Result( + previousData, + null, + isLoading: true, + isCached: true, + ), + ); + final data = await account.client.spreed.chat.receiveMessages( + token: roomToken, + lookIntoFuture: spreed.ChatReceiveMessagesLookIntoFuture.$0, + includeLastKnown: spreed.ChatReceiveMessagesIncludeLastKnown.$1, + limit: _limit, + lastKnownMessageId: (force ? null : _lastKnownMessageId) ?? 0, + lastCommonReadId: lastCommonReadMessageId.valueOrNull ?? 0, + ); + + _lastKnownMessageId = data.headers.xChatLastGiven != null ? int.parse(data.headers.xChatLastGiven!) : null; + lastCommonReadMessageId.add( + data.headers.xChatLastCommonRead != null ? int.parse(data.headers.xChatLastCommonRead!) : null, + ); + + if (data.body.ocs.data.length < _limit) { + allLoaded.add(true); + } + + messages.add( + Result.success( + BuiltList.from( + BuiltMap.build((b) { + if (previousData != null) { + for (final message in previousData) { + b[message.id] = message; + } + } + for (final message in data.body.ocs.data) { + b[message.id] = message; + } + }).values, + ), + ), + ); + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + messages.add( + Result( + previousData, + e, + isLoading: false, + isCached: true, + ), + ); + } + } +} diff --git a/packages/neon/neon_talk/lib/src/blocs/talk.dart b/packages/neon/neon_talk/lib/src/blocs/talk.dart index 4616129f218..8764792631f 100644 --- a/packages/neon/neon_talk/lib/src/blocs/talk.dart +++ b/packages/neon/neon_talk/lib/src/blocs/talk.dart @@ -6,6 +6,7 @@ import 'package:meta/meta.dart'; import 'package:neon_framework/blocs.dart'; import 'package:neon_framework/models.dart'; import 'package:neon_framework/utils.dart'; +import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/spreed.dart' as spreed; import 'package:rxdart/rxdart.dart'; @@ -20,6 +21,12 @@ sealed class TalkBloc implements InteractiveBloc { /// The total number of unread messages. BehaviorSubject get unreadCounter; + + void createRoom( + spreed.RoomType type, + String? roomName, + core.AutocompleteResult? invite, + ); } class _TalkBloc extends InteractiveBloc implements TalkBloc { @@ -72,4 +79,20 @@ class _TalkBloc extends InteractiveBloc implements TalkBloc { ), ); } + + @override + Future createRoom( + spreed.RoomType type, + String? roomName, + core.AutocompleteResult? invite, + ) async { + await wrapAction( + () async => account.client.spreed.room.createRoom( + roomType: type.value, + roomName: roomName ?? '', + invite: invite?.id ?? '', + source: invite?.source ?? '', + ), + ); + } } diff --git a/packages/neon/neon_talk/lib/src/dialogs/create_room.dart b/packages/neon/neon_talk/lib/src/dialogs/create_room.dart new file mode 100644 index 00000000000..6b855f8d367 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/dialogs/create_room.dart @@ -0,0 +1,149 @@ +import 'package:flutter/material.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_framework/utils.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/l10n/localizations.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:nextcloud/spreed.dart' as spreed; + +class TalkCreateRoomDialog extends StatefulWidget { + const TalkCreateRoomDialog({ + super.key, + }); + + @override + State createState() => _TalkCreateRoomDialogState(); +} + +class _TalkCreateRoomDialogState extends State { + late final values = { + spreed.RoomType.oneToOne: TalkLocalizations.of(context).roomTypeOneToOne, + spreed.RoomType.group: TalkLocalizations.of(context).roomTypeGroup, + spreed.RoomType.public: TalkLocalizations.of(context).roomTypePublic, + }; + + final formKey = GlobalKey(); + final controller = TextEditingController(); + final focusNode = FocusNode(); + + spreed.RoomType? selectedType; + core.AutocompleteResult? selectedAutocompleteEntry; + + void changeType(spreed.RoomType? type) { + controller.clear(); + setState(() { + selectedType = type; + }); + } + + void submit() { + if (formKey.currentState!.validate()) { + Navigator.of(context).pop( + TalkCreateRoomDetails( + selectedType!, + selectedType! == spreed.RoomType.public ? controller.text : null, + selectedType! != spreed.RoomType.public ? selectedAutocompleteEntry : null, + ), + ); + } + } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) => NeonDialog( + title: Text(TalkLocalizations.of(context).roomCreate), + content: Form( + key: formKey, + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + mainAxisSize: MainAxisSize.min, + children: [ + for (final type in values.keys) + ListTile( + title: Text(values[type]!), + leading: Icon( + type == spreed.RoomType.oneToOne + ? Icons.person + : type == spreed.RoomType.group + ? Icons.group + : Icons.public, + ), + trailing: Radio( + value: type, + groupValue: selectedType, + onChanged: changeType, + ), + onTap: () { + changeType(type); + }, + ), + const Divider(), + if (selectedType == spreed.RoomType.oneToOne || selectedType == spreed.RoomType.group) + NeonAutocomplete( + key: Key(selectedType!.index.toString()), + account: NeonProvider.of(context).activeAccount.value!, + itemType: 'call', + itemId: 'new', + shareTypes: [ + if (selectedType == spreed.RoomType.oneToOne) + core.ShareType.user + else if (selectedType == spreed.RoomType.group) + core.ShareType.group, + ], + validator: (input) => validateNotEmpty(context, input), + decoration: InputDecoration( + hintText: selectedType == spreed.RoomType.oneToOne + ? TalkLocalizations.of(context).roomCreateUserName + : TalkLocalizations.of(context).roomCreateGroupName, + ), + onSelected: (entry) { + setState(() { + selectedAutocompleteEntry = entry; + }); + }, + onFieldSubmitted: (_) { + submit(); + }, + ), + if (selectedType == spreed.RoomType.public) + TextFormField( + controller: controller, + focusNode: focusNode, + validator: (input) => validateNotEmpty(context, input), + decoration: InputDecoration( + hintText: TalkLocalizations.of(context).roomCreateRoomName, + ), + onFieldSubmitted: (_) { + submit(); + }, + ), + ], + ), + ), + actions: [ + NeonDialogAction( + onPressed: selectedType == null ? null : submit, + child: Text(TalkLocalizations.of(context).roomCreate), + ), + ], + ); +} + +class TalkCreateRoomDetails { + TalkCreateRoomDetails( + this.type, + this.roomName, + this.invite, + ); + + final spreed.RoomType type; + + final String? roomName; + + final core.AutocompleteResult? invite; +} diff --git a/packages/neon/neon_talk/lib/src/dialogs/select_screen.dart b/packages/neon/neon_talk/lib/src/dialogs/select_screen.dart new file mode 100644 index 00000000000..cb853c1f437 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/dialogs/select_screen.dart @@ -0,0 +1,111 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/l10n/localizations.dart'; +import 'package:neon_talk/src/widgets/screen_preview.dart'; + +class TalkSelectScreenDialog extends StatefulWidget { + const TalkSelectScreenDialog({ + super.key, + }); + + @override + State createState() => _TalkSelectScreenDialogState(); +} + +class _TalkSelectScreenDialogState extends State { + List? sources; + DesktopCapturerSource? selectedSource; + late Timer timer; + + @override + void initState() { + super.initState(); + + unawaited( + desktopCapturer.getSources(types: SourceType.values).then((sources) { + setState(() { + this.sources = sources; + }); + }), + ); + timer = Timer.periodic(const Duration(seconds: 1), (_) async { + await desktopCapturer.updateSources(types: SourceType.values); + }); + } + + @override + void dispose() { + timer.cancel(); + + super.dispose(); + } + + @override + Widget build(BuildContext context) => NeonDialog( + title: Text(TalkLocalizations.of(context).screenSharingSelectScreen), + content: sources == null + ? const SizedBox.shrink() + : ListView( + children: _buildSources().toList(), + ), + actions: [ + NeonDialogAction( + onPressed: selectedSource == null + ? null + : () { + Navigator.of(context).pop(selectedSource); + }, + child: Text(TalkLocalizations.of(context).screenSharingSelectScreen), + ), + ], + ); + + Iterable _buildSources() sync* { + for (var i = 0; i < SourceType.values.length; i++) { + final sourceType = SourceType.values.reversed.elementAt(i); + + if (sourceType == SourceType.Screen) { + yield Text(TalkLocalizations.of(context).screenSharingSelectScreenScreens); + } else { + yield Text(TalkLocalizations.of(context).screenSharingSelectScreenWindows); + } + + yield Wrap( + spacing: 10, + runSpacing: 10, + children: [ + for (final source in sources!.where((source) => source.type == sourceType)) + InkWell( + onTap: () { + setState(() { + selectedSource = source; + }); + }, + child: Container( + padding: const EdgeInsets.all(5), + decoration: BoxDecoration( + border: Border.all( + // Transparent to prevent the image from jumping around when changing the selected source + color: selectedSource == source ? Theme.of(context).colorScheme.primary : Colors.transparent, + width: 2, + ), + ), + width: max(MediaQuery.of(context).size.width, MediaQuery.of(context).size.height) / 5, + child: TalkScreenPreview( + source: source, + ), + ), + ), + ], + ); + + if (i != SourceType.values.length - 1) { + yield const Divider(); + } + } + } +} diff --git a/packages/neon/neon_talk/lib/src/pages/call.dart b/packages/neon/neon_talk/lib/src/pages/call.dart new file mode 100644 index 00000000000..f45a19a0872 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/pages/call.dart @@ -0,0 +1,153 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:intersperse/intersperse.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/src/blocs/call.dart'; +import 'package:neon_talk/src/dialogs/select_screen.dart'; +import 'package:neon_talk/src/utils/view_size.dart'; +import 'package:neon_talk/src/widgets/call_button.dart'; +import 'package:neon_talk/src/widgets/call_participant_view.dart'; + +class TalkCallPage extends StatefulWidget { + const TalkCallPage({ + required this.bloc, + super.key, + }); + + final TalkCallBloc bloc; + + @override + State createState() => _TalkCallPageState(); +} + +class _TalkCallPageState extends State { + @override + void initState() { + widget.bloc.errors.listen((error) { + if (!mounted) { + return; + } + NeonError.showSnackbar(context, error); + }); + + super.initState(); + } + + @override + Widget build(BuildContext context) => StreamBuilder( + stream: widget.bloc.audioEnabled, + builder: (context, audioEnabledSnapshot) => StreamBuilder( + stream: widget.bloc.videoEnabled, + builder: (context, videoEnabledSnapshot) => StreamBuilder( + stream: widget.bloc.screenEnabled, + builder: (context, screenEnabledSnapshot) { + final audioEnabled = audioEnabledSnapshot.data ?? false; + final videoEnabled = videoEnabledSnapshot.data ?? false; + final screenEnabled = screenEnabledSnapshot.data ?? false; + return Scaffold( + appBar: AppBar( + automaticallyImplyLeading: false, + actions: [ + IconButton( + icon: Icon( + audioEnabled ? MdiIcons.microphone : MdiIcons.microphoneOff, + color: !audioEnabled ? Colors.red : null, + ), + onPressed: () { + widget.bloc.changeAudio(!audioEnabled); + }, + ), + IconButton( + icon: Icon( + videoEnabled ? MdiIcons.video : MdiIcons.videoOff, + color: !videoEnabled ? Colors.red : null, + ), + onPressed: () { + widget.bloc.changeVideo(!videoEnabled); + }, + ), + IconButton( + icon: Icon( + screenEnabled ? MdiIcons.monitorShare : MdiIcons.monitorOff, + color: !screenEnabled ? Colors.red : null, + ), + onPressed: () async { + if (!screenEnabled) { + final result = await showDialog( + context: context, + builder: (context) => const TalkSelectScreenDialog(), + ); + if (result == null) { + return; + } + } + widget.bloc.changeScreen(!screenEnabled); + }, + ), + TalkCallButton( + type: TalkCallButtonType.leaveCall, + onPressed: () { + Navigator.of(context).pop(); + }, + ), + ] + .intersperse( + const SizedBox( + width: 20, + ), + ) + .toList(), + ), + body: StreamBuilder( + stream: widget.bloc.remoteParticipants, + builder: (context, remoteParticipantsSnapshot) { + if (remoteParticipantsSnapshot.data == null) { + return Center( + child: LayoutBuilder( + builder: (context, constraints) => SizedBox( + width: constraints.maxWidth / 2, + child: const NeonLinearProgressIndicator(), + ), + ), + ); + } + + final participants = [ + ...remoteParticipantsSnapshot.requireData, + widget.bloc.localParticipant, + ]; + + return Center( + child: LayoutBuilder( + builder: (context, constraints) { + final viewSize = calculateViewSize(participants.length, constraints.biggest); + return Wrap( + alignment: WrapAlignment.center, + children: [ + for (final participant in participants) + Container( + constraints: BoxConstraints( + maxWidth: viewSize.width, + maxHeight: viewSize.height, + ), + child: TalkCallParticipantView( + participant: participant, + localAudioEnabled: audioEnabled, + localVideoEnabled: videoEnabled, + localScreenEnabled: screenEnabled, + ), + ), + ], + ); + }, + ), + ); + }, + ), + ); + }, + ), + ), + ); +} diff --git a/packages/neon/neon_talk/lib/src/pages/main.dart b/packages/neon/neon_talk/lib/src/pages/main.dart index e1009777bc2..098acc244b1 100644 --- a/packages/neon/neon_talk/lib/src/pages/main.dart +++ b/packages/neon/neon_talk/lib/src/pages/main.dart @@ -4,7 +4,10 @@ import 'package:flutter/material.dart'; import 'package:neon_framework/blocs.dart'; import 'package:neon_framework/utils.dart'; import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/src/blocs/room.dart'; import 'package:neon_talk/src/blocs/talk.dart'; +import 'package:neon_talk/src/dialogs/create_room.dart'; +import 'package:neon_talk/src/pages/room.dart'; import 'package:neon_talk/src/widgets/message.dart'; import 'package:neon_talk/src/widgets/room_avatar.dart'; import 'package:neon_talk/src/widgets/unread_indicator.dart'; @@ -40,15 +43,34 @@ class _TalkMainPageState extends State { } @override - Widget build(BuildContext context) => ResultBuilder.behaviorSubject( - subject: bloc.rooms, - builder: (context, rooms) => NeonListView( - scrollKey: 'talk-rooms', - isLoading: rooms.isLoading, - error: rooms.error, - onRefresh: bloc.refresh, - itemCount: rooms.data?.length ?? 0, - itemBuilder: (context, index) => buildRoom(rooms.requireData[index]), + Widget build(BuildContext context) => Scaffold( + body: ResultBuilder.behaviorSubject( + subject: bloc.rooms, + builder: (context, rooms) => NeonListView( + scrollKey: 'talk-rooms', + isLoading: rooms.isLoading, + error: rooms.error, + onRefresh: bloc.refresh, + itemCount: rooms.data?.length ?? 0, + itemBuilder: (context, index) => buildRoom(rooms.requireData[index]), + ), + ), + floatingActionButton: FloatingActionButton( + child: const Icon(Icons.add), + onPressed: () async { + final result = await showDialog( + context: context, + builder: (context) => const TalkCreateRoomDialog(), + ); + if (result == null) { + return; + } + bloc.createRoom( + result.type, + result.roomName, + result.invite, + ); + }, ), ); @@ -78,6 +100,21 @@ class _TalkMainPageState extends State { title: Text(room.displayName), subtitle: subtitle, trailing: trailing, + onTap: () async { + final roomBloc = TalkRoomBloc( + NeonProvider.of(context).activeAccount.value!, + room, + ); + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => TalkRoomPage( + bloc: roomBloc, + ), + ), + ); + await roomBloc.leaveRoom(); + bloc.dispose(); + }, ); } } diff --git a/packages/neon/neon_talk/lib/src/pages/room.dart b/packages/neon/neon_talk/lib/src/pages/room.dart new file mode 100644 index 00000000000..46ba602d8a6 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/pages/room.dart @@ -0,0 +1,408 @@ +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/json_object.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_chat_types/flutter_chat_types.dart' as chat_types; +import 'package:flutter_chat_ui/flutter_chat_ui.dart' as chat_ui; +import 'package:go_router/go_router.dart'; +import 'package:neon_framework/blocs.dart'; +import 'package:neon_framework/utils.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/src/blocs/call.dart'; +import 'package:neon_talk/src/blocs/room.dart'; +import 'package:neon_talk/src/pages/call.dart'; +import 'package:neon_talk/src/utils/text_matchers.dart'; +import 'package:neon_talk/src/widgets/call_button.dart'; +import 'package:neon_talk/src/widgets/room_avatar.dart'; +import 'package:nextcloud/core.dart'; +import 'package:nextcloud/spreed.dart' as spreed; + +class TalkRoomPage extends StatefulWidget { + const TalkRoomPage({ + required this.bloc, + super.key, + }); + + final TalkRoomBloc bloc; + + @override + State createState() => _TalkRoomPageState(); +} + +class _TalkRoomPageState extends State { + final defaultChatTheme = const chat_ui.DefaultChatTheme(); + + late final chatTheme = chat_ui.DefaultChatTheme( + backgroundColor: Theme.of(context).colorScheme.background, + primaryColor: Theme.of(context).colorScheme.primary, + secondaryColor: Theme.of(context).colorScheme.secondary, + inputBackgroundColor: Theme.of(context).colorScheme.primary, + inputTextColor: Theme.of(context).colorScheme.onPrimary, + inputTextCursorColor: Theme.of(context).colorScheme.onPrimary, + receivedMessageBodyTextStyle: defaultChatTheme.receivedMessageBodyTextStyle.copyWith( + color: Theme.of(context).colorScheme.onSecondary, + ), + sentMessageBodyTextStyle: defaultChatTheme.sentMessageBodyTextStyle.copyWith( + color: Theme.of(context).colorScheme.onPrimary, + ), + unreadHeaderTheme: chat_ui.UnreadHeaderTheme( + color: Theme.of(context).colorScheme.background, + textStyle: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + ); + + final inputOptions = const chat_ui.InputOptions( + sendButtonVisibilityMode: chat_ui.SendButtonVisibilityMode.always, + ); + + late final user = chat_types.User( + id: widget.bloc.actorId, + ); + + void onSendPressed(chat_types.PartialText partialText) { + widget.bloc.sendMessage(partialText.text); + } + + Future openCall(spreed.Room room) async { + try { + final client = NeonProvider.of(context).activeAccount.value!.client; + final settings = (await client.spreed.signaling.getSettings(token: widget.bloc.roomToken)).body.ocs.data; + final bloc = TalkCallBloc( + settings, + client, + widget.bloc.roomToken, + room.sessionId, + ); + if (!mounted) { + return; + } + await Navigator.of(context).push( + MaterialPageRoute( + builder: (context) => TalkCallPage( + bloc: bloc, + ), + ), + ); + await bloc.leaveCall(); + bloc.dispose(); + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + if (mounted) { + NeonError.showSnackbar(context, e); + } + } + } + + @override + void initState() { + super.initState(); + + widget.bloc.errors.listen((error) { + NeonError.showSnackbar(context, error); + }); + } + + Iterable _buildTitle(Result room) sync* { + if (room.hasData) { + final roomType = spreed.RoomType.fromValue(room.requireData.type); + + if (roomType.isSingleUser) { + yield TalkRoomAvatar( + room: room.requireData, + ); + + yield const SizedBox( + width: 8, + ); + } + + yield Flexible( + child: Text(room.requireData.displayName), + ); + } + + if (room.hasError) { + yield const SizedBox( + width: 8, + ); + + yield Icon( + Icons.error_outline, + size: 30, + color: Theme.of(context).colorScheme.onPrimary, + ); + } + + if (room.isLoading) { + yield const SizedBox( + width: 8, + ); + + yield Expanded( + child: NeonLinearProgressIndicator( + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ); + } + } + + @override + Widget build(BuildContext context) => StreamBuilder( + stream: widget.bloc.allLoaded, + builder: (context, allLoadedSnapshot) => ResultBuilder( + stream: widget.bloc.room, + builder: (context, room) => StreamBuilder( + stream: widget.bloc.lastCommonReadMessageId, + builder: (context, lastCommonReadMessageIdSnapshot) => StreamBuilder( + stream: widget.bloc.sendingMessage, + builder: (context, sendingMessageSnapshot) => ResultBuilder( + stream: widget.bloc.messages, + builder: (context, messages) { + final roomType = room.hasData ? spreed.RoomType.fromValue(room.requireData.type) : null; + return Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + titleSpacing: 0, + title: Row( + children: _buildTitle(room).toList(), + ), + actions: [ + if (room.hasData && room.requireData.readOnly == 0) + if (room.requireData.hasCall) + TalkCallButton( + type: TalkCallButtonType.joinCall, + onPressed: () async { + await openCall(room.requireData); + }, + ) + else if (room.requireData.canStartCall) + TalkCallButton( + type: TalkCallButtonType.startCall, + onPressed: () async { + await openCall(room.requireData); + }, + ), + ], + ), + body: Center( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 1120, + ), + child: chat_ui.Chat( + useTopSafeAreaInset: false, + showUserNames: true, + showUserAvatars: !(roomType?.isSingleUser ?? true), + theme: chatTheme, + inputOptions: inputOptions, + scrollToUnreadOptions: chat_ui.ScrollToUnreadOptions( + lastReadMessageId: room.data?.lastReadMessage.toString(), + scrollOnOpen: true, + scrollDelay: Duration.zero, + ), + avatarBuilder: (user) => NeonUserAvatar( + username: user.id, + showStatus: false, + ), + textMessageBuilder: ( + message, { + required messageWidth, + required showName, + }) => + chat_ui.TextMessage( + emojiEnlargementBehavior: chat_ui.EmojiEnlargementBehavior.multi, + hideBackgroundOnEmojiMessages: true, + message: message, + showName: showName, + usePreviewData: true, + options: chat_ui.TextMessageOptions( + matchers: getTextMatchers( + messageParameters: message.metadata, + style: chatTheme.sentMessageBodyTextStyle, + fullContent: true, + ), + ), + ), + systemMessageBuilder: (message) => chat_ui.SystemMessage( + message: message.text, + options: chat_ui.TextMessageOptions( + matchers: getTextMatchers( + messageParameters: message.metadata, + style: chatTheme.sentMessageBodyTextStyle, + fullContent: true, + ), + ), + ), + fileMessageBuilder: (message, {required messageWidth}) => InkWell( + onTap: () { + final account = NeonProvider.of(context).activeAccount.value!; + context.go(account.completeUri(Uri.parse(message.uri)).toString()); + }, + child: chat_ui.FileMessage( + message: message, + ), + ), + imageMessageBuilder: (message, {required messageWidth}) { + final file = message.metadata!['file'] as BuiltMap; + final id = int.parse((file['id']! as StringJsonObject).value); + final path = (file['path']! as StringJsonObject).value; + final etag = (file['etag']! as StringJsonObject).value; + final width = (file['width']! as NumJsonObject).value.toInt(); + final height = (file['height']! as NumJsonObject).value.toInt(); + + return InkWell( + onTap: () { + final account = NeonProvider.of(context).activeAccount.value!; + context.go(account.completeUri(Uri.parse(message.uri)).toString()); + }, + child: NeonApiImage( + getImage: (client) => client.core.preview.getPreviewByFileIdRaw( + fileId: id, + x: width, + y: height, + ), + cacheKey: 'preview-$path-$width-$height', + etag: etag, + expires: null, + ), + ); + }, + customBottomWidget: Column( + children: [ + NeonError( + messages.error, + onRetry: widget.bloc.refresh, + ), + if (messages.isLoading) + const NeonLinearProgressIndicator( + margin: EdgeInsets.symmetric(horizontal: 10, vertical: 5), + ), + if ((room.data?.readOnly ?? 0) == 0) + chat_ui.Input( + onSendPressed: onSendPressed, + options: inputOptions, + ), + ], + ), + user: user, + onEndReached: widget.bloc.loadMoreMessages, + onSendPressed: onSendPressed, + isLastPage: allLoadedSnapshot.data ?? false, + messages: [ + if (sendingMessageSnapshot.hasData) + chat_types.TextMessage( + id: 'sending', + author: user, + text: sendingMessageSnapshot.data!, + showStatus: true, + status: chat_types.Status.sending, + ), + if (messages.hasData) + ...messages.requireData + .map( + (message) => _talkMessageToChatMessage( + message, + lastCommonReadMessageId: lastCommonReadMessageIdSnapshot.data, + ), + ) + .whereNotNull(), + ], + ), + ), + ), + ); + }, + ), + ), + ), + ), + ); + + chat_types.Message? _talkMessageToChatMessage( + spreed.$ChatMessageInterface message, { + int? lastCommonReadMessageId, + }) { + final id = message.id.toString(); + final author = chat_types.User( + id: message.actorId, + firstName: message.actorDisplayName, + imageUrl: message.actorId, + ); + final createdAt = message.timestamp * 1000; + // TODO: Doesn't work yet in the UI. See https://github.com/flyerhq/flutter_chat_ui/pull/256 + final repliedMessage = message is spreed.ChatMessageWithParent && message.parent != null + ? _talkMessageToChatMessage(message.parent!) + : null; + final status = lastCommonReadMessageId != null && lastCommonReadMessageId >= message.id + ? chat_types.Status.seen + : chat_types.Status.sent; + final metadata = message.messageParameters.toMap(); + + switch (message.messageType) { + case spreed.MessageType.comment: + final file = metadata['file']; + if (file != null) { + final name = (file['name']! as StringJsonObject).value; + final size = (file['size']! as NumJsonObject).value.toInt(); + final link = (file['link']! as StringJsonObject).value; + final mimetype = (file['mimetype']! as StringJsonObject).value; + final previewAvailable = (file['preview-available']! as StringJsonObject).value == 'yes'; + + // TODO: Handle file images with text + if (previewAvailable) { + return chat_types.ImageMessage( + id: id, + author: author, + createdAt: createdAt, + repliedMessage: repliedMessage, + showStatus: true, + status: status, + metadata: metadata, + name: name, + size: size, + uri: link, + ); + } else { + return chat_types.FileMessage( + id: id, + author: author, + createdAt: createdAt, + repliedMessage: repliedMessage, + showStatus: true, + status: status, + metadata: metadata, + name: name, + size: size, + uri: link, + mimeType: mimetype, + ); + } + } + + return chat_types.TextMessage( + id: id, + author: author, + createdAt: createdAt, + repliedMessage: repliedMessage, + showStatus: true, + status: status, + metadata: metadata, + text: message.message, + ); + case spreed.MessageType.command: + case spreed.MessageType.system: + return chat_types.SystemMessage( + id: id, + createdAt: createdAt, + text: message.message, + metadata: metadata, + ); + default: + return null; + } + } +} diff --git a/packages/neon/neon_talk/lib/src/utils/participants.dart b/packages/neon/neon_talk/lib/src/utils/participants.dart new file mode 100644 index 00000000000..e20a2002d68 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/utils/participants.dart @@ -0,0 +1,113 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; + +abstract class TalkCallParticipant { + TalkCallParticipant( + this.userID, + this.sessionID, + this.renderer, + this.stream, + ); + + final String userID; + final String sessionID; + RTCVideoRenderer? renderer; + MediaStream? stream; + + void dispose() { + stream?.getTracks().forEach((track) => unawaited(track.stop())); + unawaited(stream?.dispose()); + renderer?.srcObject = null; + unawaited(renderer?.dispose()); + } +} + +class TalkLocalCallParticipant extends TalkCallParticipant { + TalkLocalCallParticipant( + super.userID, + super.sessionID, + super.renderer, + super.stream, + ); +} + +class TalkRemoteCallParticipant extends TalkCallParticipant { + TalkRemoteCallParticipant( + super.userID, + super.sessionID, + super.renderer, + super.stream, + this._connection, + this._senders, { + this.audioEnabled = false, + this.videoEnabled = false, + }); + + RTCPeerConnection? _connection; + List? _senders; + final List _candidates = []; + bool audioEnabled; + bool videoEnabled; + + RTCPeerConnection? get connection => _connection; + List? get senders => _senders; + + Future _clearSenders() async { + if (_senders != null && _connection != null) { + for (final sender in _senders!) { + await _connection!.removeTrack(sender); + } + } + if (_senders != null) { + for (final sender in _senders!) { + try { + await sender.dispose(); + } catch (_) { + // TODO: Somehow peerConnection is null when calling this on disposing the participant + } + } + _senders = null; + } + } + + Future acceptNewConnection(RTCPeerConnection? connection) async { + await _clearSenders(); + await _connection?.close(); + _connection = connection; + if (_connection != null) { + for (final candidate in _candidates) { + debugPrint('Loading candidate'); + await _connection!.addCandidate(candidate); + } + _candidates.clear(); + } + } + + Future acceptNewLocalStream(MediaStream? stream) async { + await _clearSenders(); + if (_connection != null && stream != null) { + _senders = []; + for (final track in stream.getTracks()) { + _senders!.add(await _connection!.addTrack(track, stream)); + } + } + } + + Future addCandidate(RTCIceCandidate candidate) async { + if (connection != null) { + await connection!.addCandidate(candidate); + } else { + _candidates.add(candidate); + debugPrint('Storing candidate for later use'); + } + } + + @override + void dispose() { + unawaited(_clearSenders()); + unawaited(_connection?.close()); + super.dispose(); + } +} diff --git a/packages/neon/neon_talk/lib/src/utils/text_matchers.dart b/packages/neon/neon_talk/lib/src/utils/text_matchers.dart new file mode 100644 index 00000000000..b6f3e6a4532 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/utils/text_matchers.dart @@ -0,0 +1,38 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter_chat_ui/flutter_chat_ui.dart' as chat_ui; +import 'package:flutter_parsed_text/flutter_parsed_text.dart'; +import 'package:neon_framework/widgets.dart'; + +List getTextMatchers({ + required Map? messageParameters, + required bool fullContent, + required TextStyle style, +}) => + [ + chat_ui.mailToMatcher( + style: style.copyWith( + decoration: TextDecoration.underline, + decorationColor: style.color, + ), + ), + chat_ui.urlMatcher( + style: style.copyWith( + decoration: TextDecoration.underline, + decorationColor: style.color, + ), + ), + chat_ui.boldMatcher(style: style.merge(chat_ui.PatternStyle.bold.textStyle)), + chat_ui.italicMatcher(style: style.merge(chat_ui.PatternStyle.italic.textStyle)), + chat_ui.lineThroughMatcher( + style: style.merge(chat_ui.PatternStyle.lineThrough.textStyle).copyWith( + decorationColor: style.color, + ), + ), + chat_ui.codeMatcher(style: style.merge(chat_ui.PatternStyle.code.textStyle)), + if (messageParameters != null) + NeonRichObject( + parameters: messageParameters, + style: style, + fullContent: fullContent, + ), + ]; diff --git a/packages/neon/neon_talk/lib/src/utils/view_size.dart b/packages/neon/neon_talk/lib/src/utils/view_size.dart new file mode 100644 index 00000000000..f2fe6ab3414 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/utils/view_size.dart @@ -0,0 +1,22 @@ +import 'dart:math'; +import 'dart:ui'; + +Size calculateViewSize(int count, Size constraints) { + const aspectRatio = 2 / 3; + Size? bestSize; + + for (var i = 1.0; i < min(constraints.width, constraints.height / aspectRatio) + 1; i++) { + final width = i; + final height = i * aspectRatio; + if ((constraints.width ~/ width) * (constraints.height ~/ height) >= count) { + bestSize = Size( + width, + height, + ); + } else { + break; + } + } + + return bestSize ?? Size.zero; +} diff --git a/packages/neon/neon_talk/lib/src/widgets/call_button.dart b/packages/neon/neon_talk/lib/src/widgets/call_button.dart new file mode 100644 index 00000000000..c03a3773e43 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/widgets/call_button.dart @@ -0,0 +1,55 @@ +import 'package:flutter/material.dart'; +import 'package:neon_talk/l10n/localizations.dart'; + +class TalkCallButton extends StatelessWidget { + const TalkCallButton({ + required this.type, + required this.onPressed, + super.key, + }); + + final TalkCallButtonType type; + + final VoidCallback? onPressed; + + @override + Widget build(BuildContext context) { + late final String label; + late final IconData icon; + late final Color? backgroundColor; + switch (type) { + case TalkCallButtonType.startCall: + icon = Icons.videocam; + label = TalkLocalizations.of(context).callStart; + backgroundColor = null; + case TalkCallButtonType.joinCall: + icon = Icons.phone; + label = TalkLocalizations.of(context).callJoin; + backgroundColor = Colors.green; + case TalkCallButtonType.leaveCall: + icon = Icons.videocam_off; + label = TalkLocalizations.of(context).callLeave; + backgroundColor = Colors.red; + } + return Container( + margin: const EdgeInsets.all(5), + child: ElevatedButton.icon( + onPressed: onPressed, + icon: Icon(icon), + label: Text(label), + style: backgroundColor != null + ? ElevatedButton.styleFrom( + backgroundColor: backgroundColor, + foregroundColor: Theme.of(context).colorScheme.background, + ) + : null, + ), + ); + } +} + +enum TalkCallButtonType { + startCall, + joinCall, + leaveCall, +} diff --git a/packages/neon/neon_talk/lib/src/widgets/call_participant_view.dart b/packages/neon/neon_talk/lib/src/widgets/call_participant_view.dart new file mode 100644 index 00000000000..7f7775647b6 --- /dev/null +++ b/packages/neon/neon_talk/lib/src/widgets/call_participant_view.dart @@ -0,0 +1,73 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; +import 'package:neon_framework/widgets.dart'; +import 'package:neon_talk/src/utils/participants.dart'; + +class TalkCallParticipantView extends StatelessWidget { + const TalkCallParticipantView({ + required this.participant, + required this.localAudioEnabled, + required this.localVideoEnabled, + required this.localScreenEnabled, + super.key, + }); + + final TalkCallParticipant participant; + final bool localAudioEnabled; + final bool localVideoEnabled; + final bool localScreenEnabled; + + @override + Widget build(BuildContext context) { + final hasEnabledVideoTracks = + participant.renderer?.srcObject?.getVideoTracks().where((track) => track.enabled).isNotEmpty ?? false; + final audioEnabled = participant is TalkLocalCallParticipant + ? localAudioEnabled + : (participant as TalkRemoteCallParticipant).audioEnabled; + final videoEnabled = participant is TalkLocalCallParticipant + ? localVideoEnabled + : (participant as TalkRemoteCallParticipant).videoEnabled; + return LayoutBuilder( + builder: (context, constraints) => Container( + margin: const EdgeInsets.all(5), + child: ClipRRect( + borderRadius: BorderRadius.circular(10), + child: ColoredBox( + color: Theme.of(context).colorScheme.primary, + child: Stack( + children: [ + Center( + child: hasEnabledVideoTracks && videoEnabled + ? RTCVideoView( + participant.renderer!, + objectFit: RTCVideoViewObjectFit.RTCVideoViewObjectFitCover, + ) + : NeonUserAvatar( + username: participant.userID, + showStatus: false, + size: min(constraints.maxHeight, constraints.maxWidth) / 2, + ), + ), + if (!audioEnabled) + Align( + alignment: Alignment.bottomRight, + child: Container( + margin: const EdgeInsets.all(5), + child: Icon( + MdiIcons.microphoneOff, + size: 28, + color: Theme.of(context).colorScheme.onPrimary, + ), + ), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/packages/neon/neon_talk/lib/src/widgets/screen_preview.dart b/packages/neon/neon_talk/lib/src/widgets/screen_preview.dart new file mode 100644 index 00000000000..bf30a915e4f --- /dev/null +++ b/packages/neon/neon_talk/lib/src/widgets/screen_preview.dart @@ -0,0 +1,62 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter_webrtc/flutter_webrtc.dart'; + +class TalkScreenPreview extends StatefulWidget { + const TalkScreenPreview({ + required this.source, + super.key, + }); + + final DesktopCapturerSource source; + + @override + State createState() => _TalkScreenPreviewState(); +} + +class _TalkScreenPreviewState extends State { + late final List> subscriptions = []; + + @override + void initState() { + super.initState(); + subscriptions.addAll([ + widget.source.onThumbnailChanged.stream.listen((_) => setState(() {})), + widget.source.onNameChanged.stream.listen((_) => setState(() {})), + ]); + } + + @override + void dispose() { + for (final subscription in subscriptions) { + unawaited(subscription.cancel()); + } + + super.dispose(); + } + + @override + Widget build(BuildContext context) => Column( + children: [ + if (widget.source.thumbnail != null) + AspectRatio( + aspectRatio: 3 / 2, + child: Image.memory( + widget.source.thumbnail!, + gaplessPlayback: true, + fit: BoxFit.contain, + ), + ), + Text( + widget.source.name, + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: const TextStyle( + fontWeight: FontWeight.bold, + ), + ), + ], + ); +} diff --git a/packages/neon/neon_talk/pubspec.yaml b/packages/neon/neon_talk/pubspec.yaml index 658ed817975..28e6fc96c33 100644 --- a/packages/neon/neon_talk/pubspec.yaml +++ b/packages/neon/neon_talk/pubspec.yaml @@ -8,10 +8,17 @@ environment: dependencies: built_collection: ^5.0.0 + built_value: ^8.9.0 + collection: ^1.0.0 flutter: sdk: flutter + flutter_chat_types: ^3.6.2 + flutter_chat_ui: ^1.6.12 flutter_localizations: sdk: flutter + flutter_material_design_icons: ^1.0.0 + flutter_parsed_text: ^2.1.0 + flutter_webrtc: ^0.9.48+hotfix.1 go_router: ^13.0.0 intersperse: ^2.0.0 logging: ^1.0.0 @@ -22,6 +29,7 @@ dependencies: path: packages/neon_framework nextcloud: ^5.0.2 rxdart: ^0.27.0 + vector_graphics: ^1.0.0 dev_dependencies: build_runner: ^2.4.8 diff --git a/packages/neon_framework/lib/src/widgets/autocomplete.dart b/packages/neon_framework/lib/src/widgets/autocomplete.dart new file mode 100644 index 00000000000..50ef301124a --- /dev/null +++ b/packages/neon_framework/lib/src/widgets/autocomplete.dart @@ -0,0 +1,95 @@ +// ignore_for_file: public_member_api_docs + +import 'package:built_collection/built_collection.dart'; +import 'package:flutter/material.dart'; +import 'package:neon_framework/src/models/account.dart'; +import 'package:neon_framework/src/widgets/group_avatar.dart'; +import 'package:neon_framework/src/widgets/user_avatar.dart'; +import 'package:nextcloud/core.dart' as core; + +class NeonAutocomplete extends StatelessWidget { + const NeonAutocomplete({ + required this.account, + required this.itemType, + required this.itemId, + required this.shareTypes, + required this.onSelected, + this.sorter, + this.limit = 10, + this.validator, + this.decoration, + this.onFieldSubmitted, + super.key, + }); + + final Account account; + + final String itemType; + final String itemId; + final List shareTypes; + final void Function(core.AutocompleteResult entry) onSelected; + final String? sorter; + final int limit; + final FormFieldValidator? validator; + final InputDecoration? decoration; + final ValueChanged? onFieldSubmitted; + + @override + Widget build(BuildContext context) => Autocomplete( + fieldViewBuilder: ( + context, + controller, + focusNode, + onFieldSubmitted, + ) => + TextFormField( + controller: controller, + focusNode: focusNode, + validator: validator, + decoration: decoration, + onFieldSubmitted: (value) { + onFieldSubmitted(); + this.onFieldSubmitted?.call(value); + }, + ), + optionsBuilder: (text) async { + final result = await account.client.core.autoComplete.$get( + search: text.text, + itemType: itemType, + itemId: itemId, + shareTypes: BuiltList(shareTypes.map((s) => s.index)), + ); + return result.body.ocs.data; + }, + displayStringForOption: (option) => option.id, + optionsViewBuilder: (context, onSelected, options) => Material( + elevation: 4, + child: ListView.builder( + padding: EdgeInsets.zero, + shrinkWrap: true, + itemCount: options.length, + itemBuilder: (context, index) { + final option = options.elementAt(index); + Widget? icon; + switch (option.source) { + case 'users': + icon = NeonUserAvatar( + username: option.id, + ); + case 'groups': + icon = const NeonGroupAvatar(); + } + return ListTile( + title: Text(option.label), + subtitle: Text(option.id), + leading: icon, + onTap: () { + onSelected(option); + }, + ); + }, + ), + ), + onSelected: onSelected, + ); +} diff --git a/packages/neon_framework/lib/src/widgets/group_avatar.dart b/packages/neon_framework/lib/src/widgets/group_avatar.dart new file mode 100644 index 00000000000..134af0c2cbc --- /dev/null +++ b/packages/neon_framework/lib/src/widgets/group_avatar.dart @@ -0,0 +1,30 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:neon_framework/src/theme/sizes.dart'; + +class NeonGroupAvatar extends StatelessWidget { + const NeonGroupAvatar({ + this.icon = Icons.group, + this.backgroundColor, + this.foregroundColor, + this.size = smallIconSize, + super.key, + }); + + final IconData icon; + final Color? backgroundColor; + final Color? foregroundColor; + final double size; + + @override + Widget build(BuildContext context) => CircleAvatar( + radius: size, + backgroundColor: backgroundColor, + child: Icon( + icon, + color: foregroundColor, + size: size, + ), + ); +} diff --git a/packages/neon_framework/lib/src/widgets/rich_object.dart b/packages/neon_framework/lib/src/widgets/rich_object.dart new file mode 100644 index 00000000000..f41bf443663 --- /dev/null +++ b/packages/neon_framework/lib/src/widgets/rich_object.dart @@ -0,0 +1,133 @@ +// ignore_for_file: public_member_api_docs + +import 'package:built_collection/built_collection.dart'; +import 'package:built_value/json_object.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_parsed_text/flutter_parsed_text.dart'; +import 'package:go_router/go_router.dart'; +import 'package:neon_framework/src/blocs/accounts.dart'; +import 'package:neon_framework/src/utils/provider.dart'; +import 'package:neon_framework/src/widgets/group_avatar.dart'; +import 'package:neon_framework/src/widgets/image.dart'; +import 'package:neon_framework/src/widgets/user_avatar.dart'; +import 'package:nextcloud/core.dart'; + +class NeonRichObject extends MatchText { + NeonRichObject({ + required Map parameters, + required TextStyle style, + required bool fullContent, + }) : super( + onTap: (_) {}, + pattern: '{(${parameters.keys.join('|')})}', + renderWidget: ({ + required pattern, + required text, + }) => + Builder( + builder: (context) { + final richObject = parameters[text.substring(1, text.length - 1)] as BuiltMap; + final type = (richObject['type']! as StringJsonObject).value; + + switch (type) { + case 'user': + final account = NeonProvider.of(context).activeAccount.value!; + final id = (richObject['id']! as StringJsonObject).value; + + final highlight = id == account.username; + return _buildChip( + context: context, + avatar: NeonUserAvatar.withAccount( + account: account, + username: id, + showStatus: false, + size: 20, + ), + label: Text( + (richObject['name']! as StringJsonObject).value, + style: style.copyWith( + color: highlight + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onBackground, + ), + ), + highlight: highlight, + ); + case 'group': + case 'call': + return _buildChip( + context: context, + avatar: const NeonGroupAvatar( + size: 10, + ), + label: Text( + (richObject['name']! as StringJsonObject).value, + style: style, + ), + highlight: true, + ); + case 'file': + void onTap() { + final link = Uri.parse((richObject['link']! as StringJsonObject).value); + final account = NeonProvider.of(context).activeAccount.value!; + context.go(account.completeUri(link).toString()); + } + final previewAvailable = (richObject['preview-available']! as StringJsonObject).value == 'yes'; + if (previewAvailable && fullContent) { + final id = int.parse((richObject['id']! as StringJsonObject).value); + final path = (richObject['path']! as StringJsonObject).value; + final etag = (richObject['etag']! as StringJsonObject).value; + final width = (richObject['width']! as NumJsonObject).value.toInt(); + final height = (richObject['height']! as NumJsonObject).value.toInt(); + return InkWell( + onTap: onTap, + child: NeonApiImage( + cacheKey: 'preview-$path-$width-$height', + etag: etag, + expires: null, + getImage: (client) => client.core.preview.getPreviewByFileIdRaw( + fileId: id, + x: width, + y: height, + ), + ), + ); + } else { + final name = (richObject['name']! as StringJsonObject).value; + return InkWell( + onTap: onTap, + child: Text( + name, + style: style.copyWith( + decoration: TextDecoration.underline, + decorationColor: style.color, + ), + ), + ); + } + default: + debugPrint('Rich message type $richObject not implemented yet'); + return Text( + text, + style: style, + ); + } + }, + ), + ); + + static Widget _buildChip({ + required BuildContext context, + required Widget avatar, + required Widget label, + required bool highlight, + }) => + Chip( + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(50))), + avatar: avatar, + label: label, + backgroundColor: highlight ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.background, + padding: EdgeInsets.zero, + labelPadding: const EdgeInsets.only(right: 7.5), + ); +} diff --git a/packages/neon_framework/lib/widgets.dart b/packages/neon_framework/lib/widgets.dart index fac6ab9cf27..e0893866430 100644 --- a/packages/neon_framework/lib/widgets.dart +++ b/packages/neon_framework/lib/widgets.dart @@ -1,11 +1,14 @@ +export 'package:neon_framework/src/widgets/autocomplete.dart'; export 'package:neon_framework/src/widgets/custom_background.dart'; export 'package:neon_framework/src/widgets/dialog.dart' hide AccountDeletion, NeonAccountDeletionDialog, NeonAccountSelectionDialog, NeonUnifiedPushDialog; export 'package:neon_framework/src/widgets/error.dart'; +export 'package:neon_framework/src/widgets/group_avatar.dart'; export 'package:neon_framework/src/widgets/image.dart' hide NeonImage; export 'package:neon_framework/src/widgets/linear_progress_indicator.dart'; export 'package:neon_framework/src/widgets/list_view.dart'; export 'package:neon_framework/src/widgets/relative_time.dart'; +export 'package:neon_framework/src/widgets/rich_object.dart'; export 'package:neon_framework/src/widgets/server_icon.dart'; export 'package:neon_framework/src/widgets/user_avatar.dart' hide NeonUserStatusIndicator; export 'package:neon_framework/src/widgets/user_status_icon.dart'; diff --git a/packages/neon_framework/pubspec.yaml b/packages/neon_framework/pubspec.yaml index 286b7ed927a..efc4437cca3 100644 --- a/packages/neon_framework/pubspec.yaml +++ b/packages/neon_framework/pubspec.yaml @@ -27,6 +27,7 @@ dependencies: sdk: flutter flutter_material_design_icons: ^1.0.0 flutter_native_splash: ^2.0.0 + flutter_parsed_text: ^2.1.0 flutter_svg: ^2.0.0 flutter_zxing: ^1.0.0 go_router: ^13.0.0 diff --git a/packages/nextcloud/lib/src/api/spreed.openapi.dart b/packages/nextcloud/lib/src/api/spreed.openapi.dart index d5c8c70d3f0..41472363bc9 100644 --- a/packages/nextcloud/lib/src/api/spreed.openapi.dart +++ b/packages/nextcloud/lib/src/api/spreed.openapi.dart @@ -13586,7 +13586,7 @@ class $SignalingClient { /// See: /// * [sendMessagesRaw] for an experimental operation that returns a `DynamiteRawResponse` that can be serialized. Future<_i1.DynamiteResponse> sendMessages({ - required String messages, + required ContentString> messages, required String token, SignalingSendMessagesApiVersion? apiVersion, bool? oCSAPIRequest, @@ -13622,7 +13622,7 @@ class $SignalingClient { /// * [sendMessages] for an operation that returns a `DynamiteResponse` with a stable API. @_i2.experimental Future<_i1.DynamiteRawResponse> sendMessagesRaw({ - required String messages, + required ContentString> messages, required String token, SignalingSendMessagesApiVersion? apiVersion, bool? oCSAPIRequest, @@ -13645,7 +13645,12 @@ class $SignalingClient { } // coverage:ignore-end - final $messages = _$jsonSerializers.serialize(messages, specifiedType: const FullType(String)); + final $messages = _$jsonSerializers.serialize( + messages, + specifiedType: const FullType(ContentString, [ + FullType(BuiltList, [FullType(SignalingSendMessagesMessages)]), + ]), + ); _parameters['messages'] = $messages; final $token = _$jsonSerializers.serialize(token, specifiedType: const FullType(String)); @@ -38655,36 +38660,296 @@ abstract class SignalingSession static Serializer get serializer => _$signalingSessionSerializer; } -typedef SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data = ({ - BuiltList? builtListSignalingSession, - String? string -}); - @BuiltValue(instantiable: false) -abstract interface class $SignalingPullMessagesResponseApplicationJson_Ocs_DataInterface { +abstract interface class $SignalingSessionsInterface { String get type; - SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data get data; + BuiltList get data; +} + +abstract class SignalingSessions + implements $SignalingSessionsInterface, Built { + /// Creates a new SignalingSessions object using the builder pattern. + factory SignalingSessions([void Function(SignalingSessionsBuilder)? b]) = _$SignalingSessions; + + // coverage:ignore-start + const SignalingSessions._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingSessions.fromJson(Map json) => _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingSessions. + static Serializer get serializer => _$signalingSessionsSerializer; +} + +class SignalingMessageType extends EnumClass { + const SignalingMessageType._(super.name); + + /// `offer` + static const SignalingMessageType offer = _$signalingMessageTypeOffer; + + /// `answer` + static const SignalingMessageType answer = _$signalingMessageTypeAnswer; + + /// `candidate` + static const SignalingMessageType candidate = _$signalingMessageTypeCandidate; + + /// `unshareScreen` + static const SignalingMessageType unshareScreen = _$signalingMessageTypeUnshareScreen; + + /// `remove-candidates` + @BuiltValueEnumConst(wireName: 'remove-candidates') + static const SignalingMessageType removeCandidates = _$signalingMessageTypeRemoveCandidates; + + /// `control` + static const SignalingMessageType control = _$signalingMessageTypeControl; + + /// `forceMute` + static const SignalingMessageType forceMute = _$signalingMessageTypeForceMute; + + /// `mute` + static const SignalingMessageType mute = _$signalingMessageTypeMute; + + /// `unmute` + static const SignalingMessageType unmute = _$signalingMessageTypeUnmute; + + /// `nickChanged` + static const SignalingMessageType nickChanged = _$signalingMessageTypeNickChanged; + + /// Returns a set with all values this enum contains. + // coverage:ignore-start + static BuiltSet get values => _$signalingMessageTypeValues; + // coverage:ignore-end + + /// Returns the enum value associated to the [name]. + static SignalingMessageType valueOf(String name) => _$valueOfSignalingMessageType(name); + + /// Returns the serialized value of this enum value. + String get value => _$jsonSerializers.serializeWith(serializer, this)! as String; + + /// Serializer for SignalingMessageType. + @BuiltValueSerializer(custom: true) + static Serializer get serializer => const _$SignalingMessageTypeSerializer(); +} + +class _$SignalingMessageTypeSerializer implements PrimitiveSerializer { + const _$SignalingMessageTypeSerializer(); + + static const Map _toWire = { + SignalingMessageType.offer: 'offer', + SignalingMessageType.answer: 'answer', + SignalingMessageType.candidate: 'candidate', + SignalingMessageType.unshareScreen: 'unshareScreen', + SignalingMessageType.removeCandidates: 'remove-candidates', + SignalingMessageType.control: 'control', + SignalingMessageType.forceMute: 'forceMute', + SignalingMessageType.mute: 'mute', + SignalingMessageType.unmute: 'unmute', + SignalingMessageType.nickChanged: 'nickChanged', + }; + + static const Map _fromWire = { + 'offer': SignalingMessageType.offer, + 'answer': SignalingMessageType.answer, + 'candidate': SignalingMessageType.candidate, + 'unshareScreen': SignalingMessageType.unshareScreen, + 'remove-candidates': SignalingMessageType.removeCandidates, + 'control': SignalingMessageType.control, + 'forceMute': SignalingMessageType.forceMute, + 'mute': SignalingMessageType.mute, + 'unmute': SignalingMessageType.unmute, + 'nickChanged': SignalingMessageType.nickChanged, + }; + + @override + Iterable get types => const [SignalingMessageType]; + + @override + String get wireName => 'SignalingMessageType'; + + @override + Object serialize( + Serializers serializers, + SignalingMessageType object, { + FullType specifiedType = FullType.unspecified, + }) => + _toWire[object]!; + + @override + SignalingMessageType deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => + _fromWire[serialized]!; +} + +class SignalingRoomType extends EnumClass { + const SignalingRoomType._(super.name); + + /// `video` + static const SignalingRoomType video = _$signalingRoomTypeVideo; + + /// `screen` + static const SignalingRoomType screen = _$signalingRoomTypeScreen; + + /// Returns a set with all values this enum contains. + // coverage:ignore-start + static BuiltSet get values => _$signalingRoomTypeValues; + // coverage:ignore-end + + /// Returns the enum value associated to the [name]. + static SignalingRoomType valueOf(String name) => _$valueOfSignalingRoomType(name); + + /// Returns the serialized value of this enum value. + String get value => _$jsonSerializers.serializeWith(serializer, this)! as String; + + /// Serializer for SignalingRoomType. + @BuiltValueSerializer(custom: true) + static Serializer get serializer => const _$SignalingRoomTypeSerializer(); +} + +class _$SignalingRoomTypeSerializer implements PrimitiveSerializer { + const _$SignalingRoomTypeSerializer(); + + static const Map _toWire = { + SignalingRoomType.video: 'video', + SignalingRoomType.screen: 'screen', + }; + + static const Map _fromWire = { + 'video': SignalingRoomType.video, + 'screen': SignalingRoomType.screen, + }; + + @override + Iterable get types => const [SignalingRoomType]; + + @override + String get wireName => 'SignalingRoomType'; + + @override + Object serialize( + Serializers serializers, + SignalingRoomType object, { + FullType specifiedType = FullType.unspecified, + }) => + _toWire[object]!; + + @override + SignalingRoomType deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => + _fromWire[serialized]!; +} + +class SignalingSessionDescriptionMessage_Payload_Type extends EnumClass { + const SignalingSessionDescriptionMessage_Payload_Type._(super.name); + + /// `offer` + static const SignalingSessionDescriptionMessage_Payload_Type offer = + _$signalingSessionDescriptionMessagePayloadTypeOffer; + + /// `answer` + static const SignalingSessionDescriptionMessage_Payload_Type answer = + _$signalingSessionDescriptionMessagePayloadTypeAnswer; + + /// Returns a set with all values this enum contains. + // coverage:ignore-start + static BuiltSet get values => + _$signalingSessionDescriptionMessagePayloadTypeValues; + // coverage:ignore-end + + /// Returns the enum value associated to the [name]. + static SignalingSessionDescriptionMessage_Payload_Type valueOf(String name) => + _$valueOfSignalingSessionDescriptionMessage_Payload_Type(name); + + /// Returns the serialized value of this enum value. + String get value => _$jsonSerializers.serializeWith(serializer, this)! as String; + + /// Serializer for SignalingSessionDescriptionMessage_Payload_Type. + @BuiltValueSerializer(custom: true) + static Serializer get serializer => + const _$SignalingSessionDescriptionMessage_Payload_TypeSerializer(); +} + +class _$SignalingSessionDescriptionMessage_Payload_TypeSerializer + implements PrimitiveSerializer { + const _$SignalingSessionDescriptionMessage_Payload_TypeSerializer(); + + static const Map _toWire = + { + SignalingSessionDescriptionMessage_Payload_Type.offer: 'offer', + SignalingSessionDescriptionMessage_Payload_Type.answer: 'answer', + }; + + static const Map _fromWire = + { + 'offer': SignalingSessionDescriptionMessage_Payload_Type.offer, + 'answer': SignalingSessionDescriptionMessage_Payload_Type.answer, + }; + + @override + Iterable get types => const [SignalingSessionDescriptionMessage_Payload_Type]; + + @override + String get wireName => 'SignalingSessionDescriptionMessage_Payload_Type'; + + @override + Object serialize( + Serializers serializers, + SignalingSessionDescriptionMessage_Payload_Type object, { + FullType specifiedType = FullType.unspecified, + }) => + _toWire[object]!; + + @override + SignalingSessionDescriptionMessage_Payload_Type deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => + _fromWire[serialized]!; +} + +@BuiltValue(instantiable: false) +abstract interface class $SignalingSessionDescriptionMessage_PayloadInterface { + SignalingSessionDescriptionMessage_Payload_Type get type; + String get sdp; + String get nick; } -abstract class SignalingPullMessagesResponseApplicationJson_Ocs_Data +abstract class SignalingSessionDescriptionMessage_Payload implements - $SignalingPullMessagesResponseApplicationJson_Ocs_DataInterface, - Built { - /// Creates a new SignalingPullMessagesResponseApplicationJson_Ocs_Data object using the builder pattern. - factory SignalingPullMessagesResponseApplicationJson_Ocs_Data([ - void Function(SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder)? b, - ]) = _$SignalingPullMessagesResponseApplicationJson_Ocs_Data; + $SignalingSessionDescriptionMessage_PayloadInterface, + Built { + /// Creates a new SignalingSessionDescriptionMessage_Payload object using the builder pattern. + factory SignalingSessionDescriptionMessage_Payload([ + void Function(SignalingSessionDescriptionMessage_PayloadBuilder)? b, + ]) = _$SignalingSessionDescriptionMessage_Payload; // coverage:ignore-start - const SignalingPullMessagesResponseApplicationJson_Ocs_Data._(); + const SignalingSessionDescriptionMessage_Payload._(); // coverage:ignore-end /// Creates a new object from the given [json] data. /// /// Use [toJson] to serialize it back into json. // coverage:ignore-start - factory SignalingPullMessagesResponseApplicationJson_Ocs_Data.fromJson(Map json) => + factory SignalingSessionDescriptionMessage_Payload.fromJson(Map json) => _$jsonSerializers.deserializeWith(serializer, json)!; // coverage:ignore-end @@ -38695,20 +38960,353 @@ abstract class SignalingPullMessagesResponseApplicationJson_Ocs_Data Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; // coverage:ignore-end - /// Serializer for SignalingPullMessagesResponseApplicationJson_Ocs_Data. - static Serializer get serializer => - _$signalingPullMessagesResponseApplicationJsonOcsDataSerializer; + /// Serializer for SignalingSessionDescriptionMessage_Payload. + static Serializer get serializer => + _$signalingSessionDescriptionMessagePayloadSerializer; +} - @BuiltValueHook(finalizeBuilder: true) - static void _validate(SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder b) { - b.data?.validateOneOf(); - } +@BuiltValue(instantiable: false) +abstract interface class $SignalingSessionDescriptionMessageInterface { + String get from; + String get to; + SignalingMessageType get type; + SignalingRoomType? get roomType; + String? get sid; + SignalingSessionDescriptionMessage_Payload get payload; +} + +abstract class SignalingSessionDescriptionMessage + implements + $SignalingSessionDescriptionMessageInterface, + Built { + /// Creates a new SignalingSessionDescriptionMessage object using the builder pattern. + factory SignalingSessionDescriptionMessage([void Function(SignalingSessionDescriptionMessageBuilder)? b]) = + _$SignalingSessionDescriptionMessage; + + // coverage:ignore-start + const SignalingSessionDescriptionMessage._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingSessionDescriptionMessage.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingSessionDescriptionMessage. + static Serializer get serializer => + _$signalingSessionDescriptionMessageSerializer; +} + +@BuiltValue(instantiable: false) +abstract interface class $SignalingICECandidateMessage_Payload_CandidateInterface { + int get sdpMLineIndex; + String get sdpMid; + String get candidate; +} + +abstract class SignalingICECandidateMessage_Payload_Candidate + implements + $SignalingICECandidateMessage_Payload_CandidateInterface, + Built { + /// Creates a new SignalingICECandidateMessage_Payload_Candidate object using the builder pattern. + factory SignalingICECandidateMessage_Payload_Candidate([ + void Function(SignalingICECandidateMessage_Payload_CandidateBuilder)? b, + ]) = _$SignalingICECandidateMessage_Payload_Candidate; + + // coverage:ignore-start + const SignalingICECandidateMessage_Payload_Candidate._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingICECandidateMessage_Payload_Candidate.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingICECandidateMessage_Payload_Candidate. + static Serializer get serializer => + _$signalingICECandidateMessagePayloadCandidateSerializer; +} + +@BuiltValue(instantiable: false) +abstract interface class $SignalingICECandidateMessage_PayloadInterface { + SignalingICECandidateMessage_Payload_Candidate get candidate; +} + +abstract class SignalingICECandidateMessage_Payload + implements + $SignalingICECandidateMessage_PayloadInterface, + Built { + /// Creates a new SignalingICECandidateMessage_Payload object using the builder pattern. + factory SignalingICECandidateMessage_Payload([void Function(SignalingICECandidateMessage_PayloadBuilder)? b]) = + _$SignalingICECandidateMessage_Payload; + + // coverage:ignore-start + const SignalingICECandidateMessage_Payload._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingICECandidateMessage_Payload.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingICECandidateMessage_Payload. + static Serializer get serializer => + _$signalingICECandidateMessagePayloadSerializer; } +@BuiltValue(instantiable: false) +abstract interface class $SignalingICECandidateMessageInterface { + String get from; + String get to; + SignalingMessageType get type; + SignalingRoomType? get roomType; + String? get sid; + SignalingICECandidateMessage_Payload get payload; +} + +abstract class SignalingICECandidateMessage + implements + $SignalingICECandidateMessageInterface, + Built { + /// Creates a new SignalingICECandidateMessage object using the builder pattern. + factory SignalingICECandidateMessage([void Function(SignalingICECandidateMessageBuilder)? b]) = + _$SignalingICECandidateMessage; + + // coverage:ignore-start + const SignalingICECandidateMessage._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingICECandidateMessage.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingICECandidateMessage. + static Serializer get serializer => _$signalingICECandidateMessageSerializer; +} + +class SignalingMuteMessage_Payload_Name extends EnumClass { + const SignalingMuteMessage_Payload_Name._(super.name); + + /// `audio` + static const SignalingMuteMessage_Payload_Name audio = _$signalingMuteMessagePayloadNameAudio; + + /// `video` + static const SignalingMuteMessage_Payload_Name video = _$signalingMuteMessagePayloadNameVideo; + + /// Returns a set with all values this enum contains. + // coverage:ignore-start + static BuiltSet get values => _$signalingMuteMessagePayloadNameValues; + // coverage:ignore-end + + /// Returns the enum value associated to the [name]. + static SignalingMuteMessage_Payload_Name valueOf(String name) => _$valueOfSignalingMuteMessage_Payload_Name(name); + + /// Returns the serialized value of this enum value. + String get value => _$jsonSerializers.serializeWith(serializer, this)! as String; + + /// Serializer for SignalingMuteMessage_Payload_Name. + @BuiltValueSerializer(custom: true) + static Serializer get serializer => + const _$SignalingMuteMessage_Payload_NameSerializer(); +} + +class _$SignalingMuteMessage_Payload_NameSerializer implements PrimitiveSerializer { + const _$SignalingMuteMessage_Payload_NameSerializer(); + + static const Map _toWire = { + SignalingMuteMessage_Payload_Name.audio: 'audio', + SignalingMuteMessage_Payload_Name.video: 'video', + }; + + static const Map _fromWire = { + 'audio': SignalingMuteMessage_Payload_Name.audio, + 'video': SignalingMuteMessage_Payload_Name.video, + }; + + @override + Iterable get types => const [SignalingMuteMessage_Payload_Name]; + + @override + String get wireName => 'SignalingMuteMessage_Payload_Name'; + + @override + Object serialize( + Serializers serializers, + SignalingMuteMessage_Payload_Name object, { + FullType specifiedType = FullType.unspecified, + }) => + _toWire[object]!; + + @override + SignalingMuteMessage_Payload_Name deserialize( + Serializers serializers, + Object serialized, { + FullType specifiedType = FullType.unspecified, + }) => + _fromWire[serialized]!; +} + +@BuiltValue(instantiable: false) +abstract interface class $SignalingMuteMessage_PayloadInterface { + SignalingMuteMessage_Payload_Name get name; +} + +abstract class SignalingMuteMessage_Payload + implements + $SignalingMuteMessage_PayloadInterface, + Built { + /// Creates a new SignalingMuteMessage_Payload object using the builder pattern. + factory SignalingMuteMessage_Payload([void Function(SignalingMuteMessage_PayloadBuilder)? b]) = + _$SignalingMuteMessage_Payload; + + // coverage:ignore-start + const SignalingMuteMessage_Payload._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingMuteMessage_Payload.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingMuteMessage_Payload. + static Serializer get serializer => _$signalingMuteMessagePayloadSerializer; +} + +@BuiltValue(instantiable: false) +abstract interface class $SignalingMuteMessageInterface { + String get from; + String get to; + SignalingMessageType get type; + SignalingRoomType? get roomType; + String? get sid; + SignalingMuteMessage_Payload get payload; +} + +abstract class SignalingMuteMessage + implements $SignalingMuteMessageInterface, Built { + /// Creates a new SignalingMuteMessage object using the builder pattern. + factory SignalingMuteMessage([void Function(SignalingMuteMessageBuilder)? b]) = _$SignalingMuteMessage; + + // coverage:ignore-start + const SignalingMuteMessage._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingMuteMessage.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingMuteMessage. + static Serializer get serializer => _$signalingMuteMessageSerializer; +} + +typedef SignalingMessage = ({ + SignalingICECandidateMessage? signalingICECandidateMessage, + SignalingMuteMessage? signalingMuteMessage, + SignalingSessionDescriptionMessage? signalingSessionDescriptionMessage +}); + +@BuiltValue(instantiable: false) +abstract interface class $SignalingMessageWrapperInterface { + String get type; + ContentString get data; +} + +abstract class SignalingMessageWrapper + implements $SignalingMessageWrapperInterface, Built { + /// Creates a new SignalingMessageWrapper object using the builder pattern. + factory SignalingMessageWrapper([void Function(SignalingMessageWrapperBuilder)? b]) = _$SignalingMessageWrapper; + + // coverage:ignore-start + const SignalingMessageWrapper._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingMessageWrapper.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingMessageWrapper. + static Serializer get serializer => _$signalingMessageWrapperSerializer; +} + +typedef SignalingData = ({SignalingMessageWrapper? signalingMessageWrapper, SignalingSessions? signalingSessions}); + @BuiltValue(instantiable: false) abstract interface class $SignalingPullMessagesResponseApplicationJson_OcsInterface { OCSMeta get meta; - BuiltList get data; + BuiltList get data; } abstract class SignalingPullMessagesResponseApplicationJson_Ocs @@ -38783,6 +39381,47 @@ abstract class SignalingPullMessagesResponseApplicationJson _$signalingPullMessagesResponseApplicationJsonSerializer; } +@BuiltValue(instantiable: false) +abstract interface class $SignalingSendMessagesMessagesInterface { + String get ev; + ContentString get fn; + String get sessionId; +} + +abstract class SignalingSendMessagesMessages + implements + $SignalingSendMessagesMessagesInterface, + Built { + /// Creates a new SignalingSendMessagesMessages object using the builder pattern. + factory SignalingSendMessagesMessages([void Function(SignalingSendMessagesMessagesBuilder)? b]) = + _$SignalingSendMessagesMessages; + + // coverage:ignore-start + const SignalingSendMessagesMessages._(); + // coverage:ignore-end + + /// Creates a new object from the given [json] data. + /// + /// Use [toJson] to serialize it back into json. + // coverage:ignore-start + factory SignalingSendMessagesMessages.fromJson(Map json) => + _$jsonSerializers.deserializeWith(serializer, json)!; + // coverage:ignore-end + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + // coverage:ignore-start + Map toJson() => _$jsonSerializers.serializeWith(serializer, this)! as Map; + // coverage:ignore-end + + /// Serializer for SignalingSendMessagesMessages. + static Serializer get serializer => _$signalingSendMessagesMessagesSerializer; + + @BuiltValueHook(initializeBuilder: true) + static void _defaults(SignalingSendMessagesMessagesBuilder b) => b..ev = 'message'; +} + class SignalingSendMessagesApiVersion extends EnumClass { const SignalingSendMessagesApiVersion._(super.name); @@ -39702,19 +40341,28 @@ extension $SettingsSetUserSettingValueExtension on SettingsSetUserSettingValue { $b2c4857c0136baea42828d89c87c757dExtension._fromJson(json); } -/// Serialization extension for `SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data`. -extension $SignalingPullMessagesResponseApplicationJson_Ocs_Data_DataExtension - on SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data { - /// Serializer for SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data. +/// Serialization extension for `SignalingMessage`. +extension $SignalingMessageExtension on SignalingMessage { + /// Serializer for SignalingMessage. + @BuiltValueSerializer(custom: true) + static Serializer get serializer => $aaa26ed867b68e19101e8f929ac3f6f2Extension._serializer; + + /// Creates a new object from the given [json] data. + /// + /// Use `toJson` to serialize it back into json. + static SignalingMessage fromJson(Object? json) => $aaa26ed867b68e19101e8f929ac3f6f2Extension._fromJson(json); +} + +/// Serialization extension for `SignalingData`. +extension $SignalingDataExtension on SignalingData { + /// Serializer for SignalingData. @BuiltValueSerializer(custom: true) - static Serializer get serializer => - $1df642f5035aea3b22543ab331c3fb01Extension._serializer; + static Serializer get serializer => $6fcb7f6535673c24974b8bafbdb3c329Extension._serializer; /// Creates a new object from the given [json] data. /// /// Use `toJson` to serialize it back into json. - static SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data fromJson(Object? json) => - $1df642f5035aea3b22543ab331c3fb01Extension._fromJson(json); + static SignalingData fromJson(Object? json) => $6fcb7f6535673c24974b8bafbdb3c329Extension._fromJson(json); } /// Serialization extension for `PublicCapabilities`. @@ -39971,13 +40619,18 @@ class _$b2c4857c0136baea42828d89c87c757dSerializer implements PrimitiveSerialize } } -typedef _$1df642f5035aea3b22543ab331c3fb01 = ({BuiltList? builtListSignalingSession, String? string}); +typedef _$aaa26ed867b68e19101e8f929ac3f6f2 = ({ + SignalingICECandidateMessage? signalingICECandidateMessage, + SignalingMuteMessage? signalingMuteMessage, + SignalingSessionDescriptionMessage? signalingSessionDescriptionMessage +}); /// @nodoc // ignore: library_private_types_in_public_api -extension $1df642f5035aea3b22543ab331c3fb01Extension on _$1df642f5035aea3b22543ab331c3fb01 { - List get _values => [builtListSignalingSession, string]; - List get _names => const ['builtListSignalingSession', 'string']; +extension $aaa26ed867b68e19101e8f929ac3f6f2Extension on _$aaa26ed867b68e19101e8f929ac3f6f2 { + List get _values => [signalingICECandidateMessage, signalingMuteMessage, signalingSessionDescriptionMessage]; + List get _names => + const ['signalingICECandidateMessage', 'signalingMuteMessage', 'signalingSessionDescriptionMessage']; /// {@macro Dynamite.validateOneOf} void validateOneOf() => _i3.validateOneOf( @@ -39990,9 +40643,9 @@ extension $1df642f5035aea3b22543ab331c3fb01Extension on _$1df642f5035aea3b22543a _values, _names, ); - static Serializer<_$1df642f5035aea3b22543ab331c3fb01> get _serializer => - const _$1df642f5035aea3b22543ab331c3fb01Serializer(); - static _$1df642f5035aea3b22543ab331c3fb01 _fromJson(Object? json) => + static Serializer<_$aaa26ed867b68e19101e8f929ac3f6f2> get _serializer => + const _$aaa26ed867b68e19101e8f929ac3f6f2Serializer(); + static _$aaa26ed867b68e19101e8f929ac3f6f2 _fromJson(Object? json) => _$jsonSerializers.deserializeWith(_serializer, json)!; /// Parses this object into a json like map. @@ -40001,52 +40654,149 @@ extension $1df642f5035aea3b22543ab331c3fb01Extension on _$1df642f5035aea3b22543a Object? toJson() => _$jsonSerializers.serializeWith(_serializer, this); } -class _$1df642f5035aea3b22543ab331c3fb01Serializer implements PrimitiveSerializer<_$1df642f5035aea3b22543ab331c3fb01> { - const _$1df642f5035aea3b22543ab331c3fb01Serializer(); +class _$aaa26ed867b68e19101e8f929ac3f6f2Serializer implements PrimitiveSerializer<_$aaa26ed867b68e19101e8f929ac3f6f2> { + const _$aaa26ed867b68e19101e8f929ac3f6f2Serializer(); @override - Iterable get types => const [_$1df642f5035aea3b22543ab331c3fb01]; + Iterable get types => const [_$aaa26ed867b68e19101e8f929ac3f6f2]; @override - String get wireName => r'_$1df642f5035aea3b22543ab331c3fb01'; + String get wireName => r'_$aaa26ed867b68e19101e8f929ac3f6f2'; @override Object serialize( Serializers serializers, - _$1df642f5035aea3b22543ab331c3fb01 object, { + _$aaa26ed867b68e19101e8f929ac3f6f2 object, { FullType specifiedType = FullType.unspecified, }) { dynamic value; - value = object.builtListSignalingSession; + value = object.signalingICECandidateMessage; if (value != null) { - return serializers.serialize(value, specifiedType: const FullType(BuiltList, [FullType(SignalingSession)]))!; + return serializers.serialize(value, specifiedType: const FullType(SignalingICECandidateMessage))!; } - value = object.string; + value = object.signalingMuteMessage; if (value != null) { - return serializers.serialize(value, specifiedType: const FullType(String))!; + return serializers.serialize(value, specifiedType: const FullType(SignalingMuteMessage))!; + } + value = object.signalingSessionDescriptionMessage; + if (value != null) { + return serializers.serialize(value, specifiedType: const FullType(SignalingSessionDescriptionMessage))!; } // Should not be possible after validation. throw StateError('Tried to serialize without any value.'); } @override - _$1df642f5035aea3b22543ab331c3fb01 deserialize( + _$aaa26ed867b68e19101e8f929ac3f6f2 deserialize( Serializers serializers, Object data, { FullType specifiedType = FullType.unspecified, }) { - BuiltList? builtListSignalingSession; + SignalingICECandidateMessage? signalingICECandidateMessage; try { - builtListSignalingSession = serializers.deserialize( + signalingICECandidateMessage = serializers.deserialize( data, - specifiedType: const FullType(BuiltList, [FullType(SignalingSession)]), - )! as BuiltList; + specifiedType: const FullType(SignalingICECandidateMessage), + )! as SignalingICECandidateMessage; } catch (_) {} - String? string; + SignalingMuteMessage? signalingMuteMessage; try { - string = serializers.deserialize(data, specifiedType: const FullType(String))! as String; + signalingMuteMessage = + serializers.deserialize(data, specifiedType: const FullType(SignalingMuteMessage))! as SignalingMuteMessage; + } catch (_) {} + SignalingSessionDescriptionMessage? signalingSessionDescriptionMessage; + try { + signalingSessionDescriptionMessage = serializers.deserialize( + data, + specifiedType: const FullType(SignalingSessionDescriptionMessage), + )! as SignalingSessionDescriptionMessage; } catch (_) {} - return (builtListSignalingSession: builtListSignalingSession, string: string); + return ( + signalingICECandidateMessage: signalingICECandidateMessage, + signalingMuteMessage: signalingMuteMessage, + signalingSessionDescriptionMessage: signalingSessionDescriptionMessage + ); + } +} + +typedef _$6fcb7f6535673c24974b8bafbdb3c329 = ({ + SignalingMessageWrapper? signalingMessageWrapper, + SignalingSessions? signalingSessions +}); + +/// @nodoc +// ignore: library_private_types_in_public_api +extension $6fcb7f6535673c24974b8bafbdb3c329Extension on _$6fcb7f6535673c24974b8bafbdb3c329 { + List get _values => [signalingMessageWrapper, signalingSessions]; + List get _names => const ['signalingMessageWrapper', 'signalingSessions']; + + /// {@macro Dynamite.validateOneOf} + void validateOneOf() => _i3.validateOneOf( + _values, + _names, + ); + + /// {@macro Dynamite.validateAnyOf} + void validateAnyOf() => _i3.validateAnyOf( + _values, + _names, + ); + static Serializer<_$6fcb7f6535673c24974b8bafbdb3c329> get _serializer => + const _$6fcb7f6535673c24974b8bafbdb3c329Serializer(); + static _$6fcb7f6535673c24974b8bafbdb3c329 _fromJson(Object? json) => + _$jsonSerializers.deserializeWith(_serializer, json)!; + + /// Parses this object into a json like map. + /// + /// Use the fromJson factory to revive it again. + Object? toJson() => _$jsonSerializers.serializeWith(_serializer, this); +} + +class _$6fcb7f6535673c24974b8bafbdb3c329Serializer implements PrimitiveSerializer<_$6fcb7f6535673c24974b8bafbdb3c329> { + const _$6fcb7f6535673c24974b8bafbdb3c329Serializer(); + + @override + Iterable get types => const [_$6fcb7f6535673c24974b8bafbdb3c329]; + + @override + String get wireName => r'_$6fcb7f6535673c24974b8bafbdb3c329'; + + @override + Object serialize( + Serializers serializers, + _$6fcb7f6535673c24974b8bafbdb3c329 object, { + FullType specifiedType = FullType.unspecified, + }) { + dynamic value; + value = object.signalingMessageWrapper; + if (value != null) { + return serializers.serialize(value, specifiedType: const FullType(SignalingMessageWrapper))!; + } + value = object.signalingSessions; + if (value != null) { + return serializers.serialize(value, specifiedType: const FullType(SignalingSessions))!; + } +// Should not be possible after validation. + throw StateError('Tried to serialize without any value.'); + } + + @override + _$6fcb7f6535673c24974b8bafbdb3c329 deserialize( + Serializers serializers, + Object data, { + FullType specifiedType = FullType.unspecified, + }) { + SignalingMessageWrapper? signalingMessageWrapper; + try { + signalingMessageWrapper = serializers.deserialize(data, specifiedType: const FullType(SignalingMessageWrapper))! + as SignalingMessageWrapper; + } catch (_) {} + SignalingSessions? signalingSessions; + try { + signalingSessions = + serializers.deserialize(data, specifiedType: const FullType(SignalingSessions))! as SignalingSessions; + } catch (_) {} + return (signalingMessageWrapper: signalingMessageWrapper, signalingSessions: signalingSessions); } } @@ -41533,18 +42283,61 @@ final Serializers _$serializers = (Serializers().toBuilder() SignalingPullMessagesResponseApplicationJson_OcsBuilder.new, ) ..add(SignalingPullMessagesResponseApplicationJson_Ocs.serializer) - ..addBuilderFactory( - const FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data), - SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder.new, - ) - ..add(SignalingPullMessagesResponseApplicationJson_Ocs_Data.serializer) + ..addBuilderFactory(const FullType(SignalingSessions), SignalingSessionsBuilder.new) + ..add(SignalingSessions.serializer) ..addBuilderFactory(const FullType(SignalingSession), SignalingSessionBuilder.new) ..add(SignalingSession.serializer) ..addBuilderFactory(const FullType(BuiltList, [FullType(SignalingSession)]), ListBuilder.new) - ..add($1df642f5035aea3b22543ab331c3fb01Extension._serializer) + ..addBuilderFactory(const FullType(SignalingMessageWrapper), SignalingMessageWrapperBuilder.new) + ..add(SignalingMessageWrapper.serializer) ..addBuilderFactory( - const FullType(BuiltList, [FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data)]), - ListBuilder.new, + const FullType(SignalingSessionDescriptionMessage), + SignalingSessionDescriptionMessageBuilder.new, + ) + ..add(SignalingSessionDescriptionMessage.serializer) + ..add(SignalingMessageType.serializer) + ..add(SignalingRoomType.serializer) + ..addBuilderFactory( + const FullType(SignalingSessionDescriptionMessage_Payload), + SignalingSessionDescriptionMessage_PayloadBuilder.new, + ) + ..add(SignalingSessionDescriptionMessage_Payload.serializer) + ..add(SignalingSessionDescriptionMessage_Payload_Type.serializer) + ..addBuilderFactory(const FullType(SignalingICECandidateMessage), SignalingICECandidateMessageBuilder.new) + ..add(SignalingICECandidateMessage.serializer) + ..addBuilderFactory( + const FullType(SignalingICECandidateMessage_Payload), + SignalingICECandidateMessage_PayloadBuilder.new, + ) + ..add(SignalingICECandidateMessage_Payload.serializer) + ..addBuilderFactory( + const FullType(SignalingICECandidateMessage_Payload_Candidate), + SignalingICECandidateMessage_Payload_CandidateBuilder.new, + ) + ..add(SignalingICECandidateMessage_Payload_Candidate.serializer) + ..addBuilderFactory(const FullType(SignalingMuteMessage), SignalingMuteMessageBuilder.new) + ..add(SignalingMuteMessage.serializer) + ..addBuilderFactory(const FullType(SignalingMuteMessage_Payload), SignalingMuteMessage_PayloadBuilder.new) + ..add(SignalingMuteMessage_Payload.serializer) + ..add(SignalingMuteMessage_Payload_Name.serializer) + ..add($aaa26ed867b68e19101e8f929ac3f6f2Extension._serializer) + ..addBuilderFactory( + const FullType(ContentString, [FullType(SignalingMessage)]), + ContentStringBuilder.new, + ) + ..add($6fcb7f6535673c24974b8bafbdb3c329Extension._serializer) + ..addBuilderFactory(const FullType(BuiltList, [FullType(SignalingData)]), ListBuilder.new) + ..addBuilderFactory(const FullType(SignalingSendMessagesMessages), SignalingSendMessagesMessagesBuilder.new) + ..add(SignalingSendMessagesMessages.serializer) + ..addBuilderFactory( + const FullType(BuiltList, [FullType(SignalingSendMessagesMessages)]), + ListBuilder.new, + ) + ..addBuilderFactory( + const FullType(ContentString, [ + FullType(BuiltList, [FullType(SignalingSendMessagesMessages)]), + ]), + ContentStringBuilder>.new, ) ..add(SignalingSendMessagesApiVersion.serializer) ..addBuilderFactory( @@ -41648,7 +42441,8 @@ final Serializers _$jsonSerializers = (_$serializers.toBuilder() _$e620970959f428e934829e52f32b7089, _$bd993fb3f40af33e8594d0d698208560, _$b2c4857c0136baea42828d89c87c757d, - _$1df642f5035aea3b22543ab331c3fb01, + _$aaa26ed867b68e19101e8f929ac3f6f2, + _$6fcb7f6535673c24974b8bafbdb3c329, _$bc4aac45771b11649d372f39a92b1cf3, }, ), diff --git a/packages/nextcloud/lib/src/api/spreed.openapi.g.dart b/packages/nextcloud/lib/src/api/spreed.openapi.g.dart index 948b61129c7..1adf20a9939 100644 --- a/packages/nextcloud/lib/src/api/spreed.openapi.g.dart +++ b/packages/nextcloud/lib/src/api/spreed.openapi.g.dart @@ -7255,6 +7255,121 @@ final BuiltSet _$signalingPullMessagesApiVersio _$signalingPullMessagesApiVersionV3, ]); +const SignalingMessageType _$signalingMessageTypeOffer = SignalingMessageType._('offer'); +const SignalingMessageType _$signalingMessageTypeAnswer = SignalingMessageType._('answer'); +const SignalingMessageType _$signalingMessageTypeCandidate = SignalingMessageType._('candidate'); +const SignalingMessageType _$signalingMessageTypeUnshareScreen = SignalingMessageType._('unshareScreen'); +const SignalingMessageType _$signalingMessageTypeRemoveCandidates = SignalingMessageType._('removeCandidates'); +const SignalingMessageType _$signalingMessageTypeControl = SignalingMessageType._('control'); +const SignalingMessageType _$signalingMessageTypeForceMute = SignalingMessageType._('forceMute'); +const SignalingMessageType _$signalingMessageTypeMute = SignalingMessageType._('mute'); +const SignalingMessageType _$signalingMessageTypeUnmute = SignalingMessageType._('unmute'); +const SignalingMessageType _$signalingMessageTypeNickChanged = SignalingMessageType._('nickChanged'); + +SignalingMessageType _$valueOfSignalingMessageType(String name) { + switch (name) { + case 'offer': + return _$signalingMessageTypeOffer; + case 'answer': + return _$signalingMessageTypeAnswer; + case 'candidate': + return _$signalingMessageTypeCandidate; + case 'unshareScreen': + return _$signalingMessageTypeUnshareScreen; + case 'removeCandidates': + return _$signalingMessageTypeRemoveCandidates; + case 'control': + return _$signalingMessageTypeControl; + case 'forceMute': + return _$signalingMessageTypeForceMute; + case 'mute': + return _$signalingMessageTypeMute; + case 'unmute': + return _$signalingMessageTypeUnmute; + case 'nickChanged': + return _$signalingMessageTypeNickChanged; + default: + throw ArgumentError(name); + } +} + +final BuiltSet _$signalingMessageTypeValues = + BuiltSet(const [ + _$signalingMessageTypeOffer, + _$signalingMessageTypeAnswer, + _$signalingMessageTypeCandidate, + _$signalingMessageTypeUnshareScreen, + _$signalingMessageTypeRemoveCandidates, + _$signalingMessageTypeControl, + _$signalingMessageTypeForceMute, + _$signalingMessageTypeMute, + _$signalingMessageTypeUnmute, + _$signalingMessageTypeNickChanged, +]); + +const SignalingRoomType _$signalingRoomTypeVideo = SignalingRoomType._('video'); +const SignalingRoomType _$signalingRoomTypeScreen = SignalingRoomType._('screen'); + +SignalingRoomType _$valueOfSignalingRoomType(String name) { + switch (name) { + case 'video': + return _$signalingRoomTypeVideo; + case 'screen': + return _$signalingRoomTypeScreen; + default: + throw ArgumentError(name); + } +} + +final BuiltSet _$signalingRoomTypeValues = BuiltSet(const [ + _$signalingRoomTypeVideo, + _$signalingRoomTypeScreen, +]); + +const SignalingSessionDescriptionMessage_Payload_Type _$signalingSessionDescriptionMessagePayloadTypeOffer = + SignalingSessionDescriptionMessage_Payload_Type._('offer'); +const SignalingSessionDescriptionMessage_Payload_Type _$signalingSessionDescriptionMessagePayloadTypeAnswer = + SignalingSessionDescriptionMessage_Payload_Type._('answer'); + +SignalingSessionDescriptionMessage_Payload_Type _$valueOfSignalingSessionDescriptionMessage_Payload_Type(String name) { + switch (name) { + case 'offer': + return _$signalingSessionDescriptionMessagePayloadTypeOffer; + case 'answer': + return _$signalingSessionDescriptionMessagePayloadTypeAnswer; + default: + throw ArgumentError(name); + } +} + +final BuiltSet _$signalingSessionDescriptionMessagePayloadTypeValues = + BuiltSet(const [ + _$signalingSessionDescriptionMessagePayloadTypeOffer, + _$signalingSessionDescriptionMessagePayloadTypeAnswer, +]); + +const SignalingMuteMessage_Payload_Name _$signalingMuteMessagePayloadNameAudio = + SignalingMuteMessage_Payload_Name._('audio'); +const SignalingMuteMessage_Payload_Name _$signalingMuteMessagePayloadNameVideo = + SignalingMuteMessage_Payload_Name._('video'); + +SignalingMuteMessage_Payload_Name _$valueOfSignalingMuteMessage_Payload_Name(String name) { + switch (name) { + case 'audio': + return _$signalingMuteMessagePayloadNameAudio; + case 'video': + return _$signalingMuteMessagePayloadNameVideo; + default: + throw ArgumentError(name); + } +} + +final BuiltSet _$signalingMuteMessagePayloadNameValues = + BuiltSet(const [ + _$signalingMuteMessagePayloadNameAudio, + _$signalingMuteMessagePayloadNameVideo, +]); + const SignalingSendMessagesApiVersion _$signalingSendMessagesApiVersionV3 = SignalingSendMessagesApiVersion._('v3'); SignalingSendMessagesApiVersion _$valueOfSignalingSendMessagesApiVersion(String name) { @@ -7858,14 +7973,28 @@ Serializer _$signalingGetSettin Serializer _$signalingGetSettingsResponseApplicationJsonSerializer = _$SignalingGetSettingsResponseApplicationJsonSerializer(); Serializer _$signalingSessionSerializer = _$SignalingSessionSerializer(); -Serializer - _$signalingPullMessagesResponseApplicationJsonOcsDataSerializer = - _$SignalingPullMessagesResponseApplicationJson_Ocs_DataSerializer(); +Serializer _$signalingSessionsSerializer = _$SignalingSessionsSerializer(); +Serializer _$signalingSessionDescriptionMessagePayloadSerializer = + _$SignalingSessionDescriptionMessage_PayloadSerializer(); +Serializer _$signalingSessionDescriptionMessageSerializer = + _$SignalingSessionDescriptionMessageSerializer(); +Serializer _$signalingICECandidateMessagePayloadCandidateSerializer = + _$SignalingICECandidateMessage_Payload_CandidateSerializer(); +Serializer _$signalingICECandidateMessagePayloadSerializer = + _$SignalingICECandidateMessage_PayloadSerializer(); +Serializer _$signalingICECandidateMessageSerializer = + _$SignalingICECandidateMessageSerializer(); +Serializer _$signalingMuteMessagePayloadSerializer = + _$SignalingMuteMessage_PayloadSerializer(); +Serializer _$signalingMuteMessageSerializer = _$SignalingMuteMessageSerializer(); +Serializer _$signalingMessageWrapperSerializer = _$SignalingMessageWrapperSerializer(); Serializer _$signalingPullMessagesResponseApplicationJsonOcsSerializer = _$SignalingPullMessagesResponseApplicationJson_OcsSerializer(); Serializer _$signalingPullMessagesResponseApplicationJsonSerializer = _$SignalingPullMessagesResponseApplicationJsonSerializer(); +Serializer _$signalingSendMessagesMessagesSerializer = + _$SignalingSendMessagesMessagesSerializer(); Serializer _$signalingSendMessagesResponseApplicationJsonOcsSerializer = _$SignalingSendMessagesResponseApplicationJson_OcsSerializer(); @@ -20394,35 +20523,486 @@ class _$SignalingSessionSerializer implements StructuredSerializer { +class _$SignalingSessionsSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingSessions, _$SignalingSessions]; + @override + final String wireName = 'SignalingSessions'; + + @override + Iterable serialize(Serializers serializers, SignalingSessions object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'type', + serializers.serialize(object.type, specifiedType: const FullType(String)), + 'data', + serializers.serialize(object.data, specifiedType: const FullType(BuiltList, [FullType(SignalingSession)])), + ]; + + return result; + } + + @override + SignalingSessions deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingSessionsBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'type': + result.type = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'data': + result.data.replace(serializers.deserialize(value, + specifiedType: const FullType(BuiltList, [FullType(SignalingSession)]))! as BuiltList); + break; + } + } + + return result.build(); + } +} + +class _$SignalingSessionDescriptionMessage_PayloadSerializer + implements StructuredSerializer { @override final Iterable types = const [ - SignalingPullMessagesResponseApplicationJson_Ocs_Data, - _$SignalingPullMessagesResponseApplicationJson_Ocs_Data + SignalingSessionDescriptionMessage_Payload, + _$SignalingSessionDescriptionMessage_Payload ]; @override - final String wireName = 'SignalingPullMessagesResponseApplicationJson_Ocs_Data'; + final String wireName = 'SignalingSessionDescriptionMessage_Payload'; + + @override + Iterable serialize(Serializers serializers, SignalingSessionDescriptionMessage_Payload object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'type', + serializers.serialize(object.type, + specifiedType: const FullType(SignalingSessionDescriptionMessage_Payload_Type)), + 'sdp', + serializers.serialize(object.sdp, specifiedType: const FullType(String)), + 'nick', + serializers.serialize(object.nick, specifiedType: const FullType(String)), + ]; + + return result; + } + + @override + SignalingSessionDescriptionMessage_Payload deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingSessionDescriptionMessage_PayloadBuilder(); + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'type': + result.type = serializers.deserialize(value, + specifiedType: const FullType(SignalingSessionDescriptionMessage_Payload_Type))! + as SignalingSessionDescriptionMessage_Payload_Type; + break; + case 'sdp': + result.sdp = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'nick': + result.nick = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + } + } + + return result.build(); + } +} + +class _$SignalingSessionDescriptionMessageSerializer + implements StructuredSerializer { @override - Iterable serialize(Serializers serializers, SignalingPullMessagesResponseApplicationJson_Ocs_Data object, + final Iterable types = const [SignalingSessionDescriptionMessage, _$SignalingSessionDescriptionMessage]; + @override + final String wireName = 'SignalingSessionDescriptionMessage'; + + @override + Iterable serialize(Serializers serializers, SignalingSessionDescriptionMessage object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'from', + serializers.serialize(object.from, specifiedType: const FullType(String)), + 'to', + serializers.serialize(object.to, specifiedType: const FullType(String)), + 'type', + serializers.serialize(object.type, specifiedType: const FullType(SignalingMessageType)), + 'payload', + serializers.serialize(object.payload, specifiedType: const FullType(SignalingSessionDescriptionMessage_Payload)), + ]; + Object? value; + value = object.roomType; + if (value != null) { + result + ..add('roomType') + ..add(serializers.serialize(value, specifiedType: const FullType(SignalingRoomType))); + } + value = object.sid; + if (value != null) { + result + ..add('sid') + ..add(serializers.serialize(value, specifiedType: const FullType(String))); + } + return result; + } + + @override + SignalingSessionDescriptionMessage deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingSessionDescriptionMessageBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'from': + result.from = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'to': + result.to = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'type': + result.type = serializers.deserialize(value, specifiedType: const FullType(SignalingMessageType))! + as SignalingMessageType; + break; + case 'roomType': + result.roomType = + serializers.deserialize(value, specifiedType: const FullType(SignalingRoomType)) as SignalingRoomType?; + break; + case 'sid': + result.sid = serializers.deserialize(value, specifiedType: const FullType(String)) as String?; + break; + case 'payload': + result.payload.replace( + serializers.deserialize(value, specifiedType: const FullType(SignalingSessionDescriptionMessage_Payload))! + as SignalingSessionDescriptionMessage_Payload); + break; + } + } + + return result.build(); + } +} + +class _$SignalingICECandidateMessage_Payload_CandidateSerializer + implements StructuredSerializer { + @override + final Iterable types = const [ + SignalingICECandidateMessage_Payload_Candidate, + _$SignalingICECandidateMessage_Payload_Candidate + ]; + @override + final String wireName = 'SignalingICECandidateMessage_Payload_Candidate'; + + @override + Iterable serialize(Serializers serializers, SignalingICECandidateMessage_Payload_Candidate object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'sdpMLineIndex', + serializers.serialize(object.sdpMLineIndex, specifiedType: const FullType(int)), + 'sdpMid', + serializers.serialize(object.sdpMid, specifiedType: const FullType(String)), + 'candidate', + serializers.serialize(object.candidate, specifiedType: const FullType(String)), + ]; + + return result; + } + + @override + SignalingICECandidateMessage_Payload_Candidate deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingICECandidateMessage_Payload_CandidateBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'sdpMLineIndex': + result.sdpMLineIndex = serializers.deserialize(value, specifiedType: const FullType(int))! as int; + break; + case 'sdpMid': + result.sdpMid = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'candidate': + result.candidate = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + } + } + + return result.build(); + } +} + +class _$SignalingICECandidateMessage_PayloadSerializer + implements StructuredSerializer { + @override + final Iterable types = const [SignalingICECandidateMessage_Payload, _$SignalingICECandidateMessage_Payload]; + @override + final String wireName = 'SignalingICECandidateMessage_Payload'; + + @override + Iterable serialize(Serializers serializers, SignalingICECandidateMessage_Payload object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'candidate', + serializers.serialize(object.candidate, + specifiedType: const FullType(SignalingICECandidateMessage_Payload_Candidate)), + ]; + + return result; + } + + @override + SignalingICECandidateMessage_Payload deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingICECandidateMessage_PayloadBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'candidate': + result.candidate.replace(serializers.deserialize(value, + specifiedType: const FullType(SignalingICECandidateMessage_Payload_Candidate))! + as SignalingICECandidateMessage_Payload_Candidate); + break; + } + } + + return result.build(); + } +} + +class _$SignalingICECandidateMessageSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingICECandidateMessage, _$SignalingICECandidateMessage]; + @override + final String wireName = 'SignalingICECandidateMessage'; + + @override + Iterable serialize(Serializers serializers, SignalingICECandidateMessage object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'from', + serializers.serialize(object.from, specifiedType: const FullType(String)), + 'to', + serializers.serialize(object.to, specifiedType: const FullType(String)), + 'type', + serializers.serialize(object.type, specifiedType: const FullType(SignalingMessageType)), + 'payload', + serializers.serialize(object.payload, specifiedType: const FullType(SignalingICECandidateMessage_Payload)), + ]; + Object? value; + value = object.roomType; + if (value != null) { + result + ..add('roomType') + ..add(serializers.serialize(value, specifiedType: const FullType(SignalingRoomType))); + } + value = object.sid; + if (value != null) { + result + ..add('sid') + ..add(serializers.serialize(value, specifiedType: const FullType(String))); + } + return result; + } + + @override + SignalingICECandidateMessage deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingICECandidateMessageBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'from': + result.from = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'to': + result.to = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'type': + result.type = serializers.deserialize(value, specifiedType: const FullType(SignalingMessageType))! + as SignalingMessageType; + break; + case 'roomType': + result.roomType = + serializers.deserialize(value, specifiedType: const FullType(SignalingRoomType)) as SignalingRoomType?; + break; + case 'sid': + result.sid = serializers.deserialize(value, specifiedType: const FullType(String)) as String?; + break; + case 'payload': + result.payload.replace( + serializers.deserialize(value, specifiedType: const FullType(SignalingICECandidateMessage_Payload))! + as SignalingICECandidateMessage_Payload); + break; + } + } + + return result.build(); + } +} + +class _$SignalingMuteMessage_PayloadSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingMuteMessage_Payload, _$SignalingMuteMessage_Payload]; + @override + final String wireName = 'SignalingMuteMessage_Payload'; + + @override + Iterable serialize(Serializers serializers, SignalingMuteMessage_Payload object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'name', + serializers.serialize(object.name, specifiedType: const FullType(SignalingMuteMessage_Payload_Name)), + ]; + + return result; + } + + @override + SignalingMuteMessage_Payload deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingMuteMessage_PayloadBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'name': + result.name = serializers.deserialize(value, + specifiedType: const FullType(SignalingMuteMessage_Payload_Name))! as SignalingMuteMessage_Payload_Name; + break; + } + } + + return result.build(); + } +} + +class _$SignalingMuteMessageSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingMuteMessage, _$SignalingMuteMessage]; + @override + final String wireName = 'SignalingMuteMessage'; + + @override + Iterable serialize(Serializers serializers, SignalingMuteMessage object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'from', + serializers.serialize(object.from, specifiedType: const FullType(String)), + 'to', + serializers.serialize(object.to, specifiedType: const FullType(String)), + 'type', + serializers.serialize(object.type, specifiedType: const FullType(SignalingMessageType)), + 'payload', + serializers.serialize(object.payload, specifiedType: const FullType(SignalingMuteMessage_Payload)), + ]; + Object? value; + value = object.roomType; + if (value != null) { + result + ..add('roomType') + ..add(serializers.serialize(value, specifiedType: const FullType(SignalingRoomType))); + } + value = object.sid; + if (value != null) { + result + ..add('sid') + ..add(serializers.serialize(value, specifiedType: const FullType(String))); + } + return result; + } + + @override + SignalingMuteMessage deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingMuteMessageBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'from': + result.from = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'to': + result.to = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'type': + result.type = serializers.deserialize(value, specifiedType: const FullType(SignalingMessageType))! + as SignalingMessageType; + break; + case 'roomType': + result.roomType = + serializers.deserialize(value, specifiedType: const FullType(SignalingRoomType)) as SignalingRoomType?; + break; + case 'sid': + result.sid = serializers.deserialize(value, specifiedType: const FullType(String)) as String?; + break; + case 'payload': + result.payload.replace(serializers.deserialize(value, + specifiedType: const FullType(SignalingMuteMessage_Payload))! as SignalingMuteMessage_Payload); + break; + } + } + + return result.build(); + } +} + +class _$SignalingMessageWrapperSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingMessageWrapper, _$SignalingMessageWrapper]; + @override + final String wireName = 'SignalingMessageWrapper'; + + @override + Iterable serialize(Serializers serializers, SignalingMessageWrapper object, {FullType specifiedType = FullType.unspecified}) { final result = [ 'type', serializers.serialize(object.type, specifiedType: const FullType(String)), 'data', - serializers.serialize(object.data, - specifiedType: const FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data)), + serializers.serialize(object.data, specifiedType: const FullType(ContentString, [FullType(SignalingMessage)])), ]; return result; } @override - SignalingPullMessagesResponseApplicationJson_Ocs_Data deserialize( - Serializers serializers, Iterable serialized, + SignalingMessageWrapper deserialize(Serializers serializers, Iterable serialized, {FullType specifiedType = FullType.unspecified}) { - final result = SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder(); + final result = SignalingMessageWrapperBuilder(); final iterator = serialized.iterator; while (iterator.moveNext()) { @@ -20434,9 +21014,9 @@ class _$SignalingPullMessagesResponseApplicationJson_Ocs_DataSerializer result.type = serializers.deserialize(value, specifiedType: const FullType(String))! as String; break; case 'data': - result.data = serializers.deserialize(value, - specifiedType: const FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data))! - as SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data; + result.data.replace(serializers.deserialize(value, + specifiedType: const FullType(ContentString, [FullType(SignalingMessage)]))! + as ContentString); break; } } @@ -20462,8 +21042,7 @@ class _$SignalingPullMessagesResponseApplicationJson_OcsSerializer 'meta', serializers.serialize(object.meta, specifiedType: const FullType(OCSMeta)), 'data', - serializers.serialize(object.data, - specifiedType: const FullType(BuiltList, [FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data)])), + serializers.serialize(object.data, specifiedType: const FullType(BuiltList, [FullType(SignalingData)])), ]; return result; @@ -20485,9 +21064,7 @@ class _$SignalingPullMessagesResponseApplicationJson_OcsSerializer break; case 'data': result.data.replace(serializers.deserialize(value, - specifiedType: - const FullType(BuiltList, [FullType(SignalingPullMessagesResponseApplicationJson_Ocs_Data)]))! - as BuiltList); + specifiedType: const FullType(BuiltList, [FullType(SignalingData)]))! as BuiltList); break; } } @@ -20541,6 +21118,56 @@ class _$SignalingPullMessagesResponseApplicationJsonSerializer } } +class _$SignalingSendMessagesMessagesSerializer implements StructuredSerializer { + @override + final Iterable types = const [SignalingSendMessagesMessages, _$SignalingSendMessagesMessages]; + @override + final String wireName = 'SignalingSendMessagesMessages'; + + @override + Iterable serialize(Serializers serializers, SignalingSendMessagesMessages object, + {FullType specifiedType = FullType.unspecified}) { + final result = [ + 'ev', + serializers.serialize(object.ev, specifiedType: const FullType(String)), + 'fn', + serializers.serialize(object.fn, specifiedType: const FullType(ContentString, [FullType(SignalingMessage)])), + 'sessionId', + serializers.serialize(object.sessionId, specifiedType: const FullType(String)), + ]; + + return result; + } + + @override + SignalingSendMessagesMessages deserialize(Serializers serializers, Iterable serialized, + {FullType specifiedType = FullType.unspecified}) { + final result = SignalingSendMessagesMessagesBuilder(); + + final iterator = serialized.iterator; + while (iterator.moveNext()) { + final key = iterator.current! as String; + iterator.moveNext(); + final Object? value = iterator.current; + switch (key) { + case 'ev': + result.ev = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + case 'fn': + result.fn.replace(serializers.deserialize(value, + specifiedType: const FullType(ContentString, [FullType(SignalingMessage)]))! + as ContentString); + break; + case 'sessionId': + result.sessionId = serializers.deserialize(value, specifiedType: const FullType(String))! as String; + break; + } + } + + return result.build(); + } +} + class _$SignalingSendMessagesResponseApplicationJson_OcsSerializer implements StructuredSerializer { @override @@ -52206,48 +52833,1142 @@ class SignalingSessionBuilder } } -abstract mixin class $SignalingPullMessagesResponseApplicationJson_Ocs_DataInterfaceBuilder { - void replace($SignalingPullMessagesResponseApplicationJson_Ocs_DataInterface other); - void update(void Function($SignalingPullMessagesResponseApplicationJson_Ocs_DataInterfaceBuilder) updates); +abstract mixin class $SignalingSessionsInterfaceBuilder { + void replace($SignalingSessionsInterface other); + void update(void Function($SignalingSessionsInterfaceBuilder) updates); String? get type; set type(String? type); - SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data? get data; - set data(SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data? data); + ListBuilder get data; + set data(ListBuilder? data); } -class _$SignalingPullMessagesResponseApplicationJson_Ocs_Data - extends SignalingPullMessagesResponseApplicationJson_Ocs_Data { +class _$SignalingSessions extends SignalingSessions { @override final String type; @override - final SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data data; + final BuiltList data; + + factory _$SignalingSessions([void Function(SignalingSessionsBuilder)? updates]) => + (SignalingSessionsBuilder()..update(updates))._build(); + + _$SignalingSessions._({required this.type, required this.data}) : super._() { + BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessions', 'type'); + BuiltValueNullFieldError.checkNotNull(data, r'SignalingSessions', 'data'); + } + + @override + SignalingSessions rebuild(void Function(SignalingSessionsBuilder) updates) => (toBuilder()..update(updates)).build(); + + @override + SignalingSessionsBuilder toBuilder() => SignalingSessionsBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingSessions && type == other.type && data == other.data; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, data.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingSessions') + ..add('type', type) + ..add('data', data)) + .toString(); + } +} + +class SignalingSessionsBuilder + implements Builder, $SignalingSessionsInterfaceBuilder { + _$SignalingSessions? _$v; + + String? _type; + String? get type => _$this._type; + set type(covariant String? type) => _$this._type = type; + + ListBuilder? _data; + ListBuilder get data => _$this._data ??= ListBuilder(); + set data(covariant ListBuilder? data) => _$this._data = data; + + SignalingSessionsBuilder(); + + SignalingSessionsBuilder get _$this { + final $v = _$v; + if ($v != null) { + _type = $v.type; + _data = $v.data.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingSessions other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingSessions; + } + + @override + void update(void Function(SignalingSessionsBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingSessions build() => _build(); + + _$SignalingSessions _build() { + _$SignalingSessions _$result; + try { + _$result = _$v ?? + _$SignalingSessions._( + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessions', 'type'), data: data.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'data'; + data.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingSessions', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingSessionDescriptionMessage_PayloadInterfaceBuilder { + void replace($SignalingSessionDescriptionMessage_PayloadInterface other); + void update(void Function($SignalingSessionDescriptionMessage_PayloadInterfaceBuilder) updates); + SignalingSessionDescriptionMessage_Payload_Type? get type; + set type(SignalingSessionDescriptionMessage_Payload_Type? type); + + String? get sdp; + set sdp(String? sdp); + + String? get nick; + set nick(String? nick); +} + +class _$SignalingSessionDescriptionMessage_Payload extends SignalingSessionDescriptionMessage_Payload { + @override + final SignalingSessionDescriptionMessage_Payload_Type type; + @override + final String sdp; + @override + final String nick; - factory _$SignalingPullMessagesResponseApplicationJson_Ocs_Data( - [void Function(SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder)? updates]) => - (SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder()..update(updates))._build(); + factory _$SignalingSessionDescriptionMessage_Payload( + [void Function(SignalingSessionDescriptionMessage_PayloadBuilder)? updates]) => + (SignalingSessionDescriptionMessage_PayloadBuilder()..update(updates))._build(); - _$SignalingPullMessagesResponseApplicationJson_Ocs_Data._({required this.type, required this.data}) : super._() { - BuiltValueNullFieldError.checkNotNull(type, r'SignalingPullMessagesResponseApplicationJson_Ocs_Data', 'type'); - BuiltValueNullFieldError.checkNotNull(data, r'SignalingPullMessagesResponseApplicationJson_Ocs_Data', 'data'); + _$SignalingSessionDescriptionMessage_Payload._({required this.type, required this.sdp, required this.nick}) + : super._() { + BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessionDescriptionMessage_Payload', 'type'); + BuiltValueNullFieldError.checkNotNull(sdp, r'SignalingSessionDescriptionMessage_Payload', 'sdp'); + BuiltValueNullFieldError.checkNotNull(nick, r'SignalingSessionDescriptionMessage_Payload', 'nick'); } @override - SignalingPullMessagesResponseApplicationJson_Ocs_Data rebuild( - void Function(SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder) updates) => + SignalingSessionDescriptionMessage_Payload rebuild( + void Function(SignalingSessionDescriptionMessage_PayloadBuilder) updates) => (toBuilder()..update(updates)).build(); @override - SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder toBuilder() => - SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder()..replace(this); + SignalingSessionDescriptionMessage_PayloadBuilder toBuilder() => + SignalingSessionDescriptionMessage_PayloadBuilder()..replace(this); @override bool operator ==(Object other) { if (identical(other, this)) return true; - final dynamic _$dynamicOther = other; - return other is SignalingPullMessagesResponseApplicationJson_Ocs_Data && + return other is SignalingSessionDescriptionMessage_Payload && type == other.type && - data == _$dynamicOther.data; + sdp == other.sdp && + nick == other.nick; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, sdp.hashCode); + _$hash = $jc(_$hash, nick.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingSessionDescriptionMessage_Payload') + ..add('type', type) + ..add('sdp', sdp) + ..add('nick', nick)) + .toString(); + } +} + +class SignalingSessionDescriptionMessage_PayloadBuilder + implements + Builder, + $SignalingSessionDescriptionMessage_PayloadInterfaceBuilder { + _$SignalingSessionDescriptionMessage_Payload? _$v; + + SignalingSessionDescriptionMessage_Payload_Type? _type; + SignalingSessionDescriptionMessage_Payload_Type? get type => _$this._type; + set type(covariant SignalingSessionDescriptionMessage_Payload_Type? type) => _$this._type = type; + + String? _sdp; + String? get sdp => _$this._sdp; + set sdp(covariant String? sdp) => _$this._sdp = sdp; + + String? _nick; + String? get nick => _$this._nick; + set nick(covariant String? nick) => _$this._nick = nick; + + SignalingSessionDescriptionMessage_PayloadBuilder(); + + SignalingSessionDescriptionMessage_PayloadBuilder get _$this { + final $v = _$v; + if ($v != null) { + _type = $v.type; + _sdp = $v.sdp; + _nick = $v.nick; + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingSessionDescriptionMessage_Payload other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingSessionDescriptionMessage_Payload; + } + + @override + void update(void Function(SignalingSessionDescriptionMessage_PayloadBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingSessionDescriptionMessage_Payload build() => _build(); + + _$SignalingSessionDescriptionMessage_Payload _build() { + final _$result = _$v ?? + _$SignalingSessionDescriptionMessage_Payload._( + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessionDescriptionMessage_Payload', 'type'), + sdp: BuiltValueNullFieldError.checkNotNull(sdp, r'SignalingSessionDescriptionMessage_Payload', 'sdp'), + nick: BuiltValueNullFieldError.checkNotNull(nick, r'SignalingSessionDescriptionMessage_Payload', 'nick')); + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingSessionDescriptionMessageInterfaceBuilder { + void replace($SignalingSessionDescriptionMessageInterface other); + void update(void Function($SignalingSessionDescriptionMessageInterfaceBuilder) updates); + String? get from; + set from(String? from); + + String? get to; + set to(String? to); + + SignalingMessageType? get type; + set type(SignalingMessageType? type); + + SignalingRoomType? get roomType; + set roomType(SignalingRoomType? roomType); + + String? get sid; + set sid(String? sid); + + SignalingSessionDescriptionMessage_PayloadBuilder get payload; + set payload(SignalingSessionDescriptionMessage_PayloadBuilder? payload); +} + +class _$SignalingSessionDescriptionMessage extends SignalingSessionDescriptionMessage { + @override + final String from; + @override + final String to; + @override + final SignalingMessageType type; + @override + final SignalingRoomType? roomType; + @override + final String? sid; + @override + final SignalingSessionDescriptionMessage_Payload payload; + + factory _$SignalingSessionDescriptionMessage([void Function(SignalingSessionDescriptionMessageBuilder)? updates]) => + (SignalingSessionDescriptionMessageBuilder()..update(updates))._build(); + + _$SignalingSessionDescriptionMessage._( + {required this.from, required this.to, required this.type, this.roomType, this.sid, required this.payload}) + : super._() { + BuiltValueNullFieldError.checkNotNull(from, r'SignalingSessionDescriptionMessage', 'from'); + BuiltValueNullFieldError.checkNotNull(to, r'SignalingSessionDescriptionMessage', 'to'); + BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessionDescriptionMessage', 'type'); + BuiltValueNullFieldError.checkNotNull(payload, r'SignalingSessionDescriptionMessage', 'payload'); + } + + @override + SignalingSessionDescriptionMessage rebuild(void Function(SignalingSessionDescriptionMessageBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingSessionDescriptionMessageBuilder toBuilder() => SignalingSessionDescriptionMessageBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingSessionDescriptionMessage && + from == other.from && + to == other.to && + type == other.type && + roomType == other.roomType && + sid == other.sid && + payload == other.payload; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, from.hashCode); + _$hash = $jc(_$hash, to.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, roomType.hashCode); + _$hash = $jc(_$hash, sid.hashCode); + _$hash = $jc(_$hash, payload.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingSessionDescriptionMessage') + ..add('from', from) + ..add('to', to) + ..add('type', type) + ..add('roomType', roomType) + ..add('sid', sid) + ..add('payload', payload)) + .toString(); + } +} + +class SignalingSessionDescriptionMessageBuilder + implements + Builder, + $SignalingSessionDescriptionMessageInterfaceBuilder { + _$SignalingSessionDescriptionMessage? _$v; + + String? _from; + String? get from => _$this._from; + set from(covariant String? from) => _$this._from = from; + + String? _to; + String? get to => _$this._to; + set to(covariant String? to) => _$this._to = to; + + SignalingMessageType? _type; + SignalingMessageType? get type => _$this._type; + set type(covariant SignalingMessageType? type) => _$this._type = type; + + SignalingRoomType? _roomType; + SignalingRoomType? get roomType => _$this._roomType; + set roomType(covariant SignalingRoomType? roomType) => _$this._roomType = roomType; + + String? _sid; + String? get sid => _$this._sid; + set sid(covariant String? sid) => _$this._sid = sid; + + SignalingSessionDescriptionMessage_PayloadBuilder? _payload; + SignalingSessionDescriptionMessage_PayloadBuilder get payload => + _$this._payload ??= SignalingSessionDescriptionMessage_PayloadBuilder(); + set payload(covariant SignalingSessionDescriptionMessage_PayloadBuilder? payload) => _$this._payload = payload; + + SignalingSessionDescriptionMessageBuilder(); + + SignalingSessionDescriptionMessageBuilder get _$this { + final $v = _$v; + if ($v != null) { + _from = $v.from; + _to = $v.to; + _type = $v.type; + _roomType = $v.roomType; + _sid = $v.sid; + _payload = $v.payload.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingSessionDescriptionMessage other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingSessionDescriptionMessage; + } + + @override + void update(void Function(SignalingSessionDescriptionMessageBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingSessionDescriptionMessage build() => _build(); + + _$SignalingSessionDescriptionMessage _build() { + _$SignalingSessionDescriptionMessage _$result; + try { + _$result = _$v ?? + _$SignalingSessionDescriptionMessage._( + from: BuiltValueNullFieldError.checkNotNull(from, r'SignalingSessionDescriptionMessage', 'from'), + to: BuiltValueNullFieldError.checkNotNull(to, r'SignalingSessionDescriptionMessage', 'to'), + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingSessionDescriptionMessage', 'type'), + roomType: roomType, + sid: sid, + payload: payload.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'payload'; + payload.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingSessionDescriptionMessage', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingICECandidateMessage_Payload_CandidateInterfaceBuilder { + void replace($SignalingICECandidateMessage_Payload_CandidateInterface other); + void update(void Function($SignalingICECandidateMessage_Payload_CandidateInterfaceBuilder) updates); + int? get sdpMLineIndex; + set sdpMLineIndex(int? sdpMLineIndex); + + String? get sdpMid; + set sdpMid(String? sdpMid); + + String? get candidate; + set candidate(String? candidate); +} + +class _$SignalingICECandidateMessage_Payload_Candidate extends SignalingICECandidateMessage_Payload_Candidate { + @override + final int sdpMLineIndex; + @override + final String sdpMid; + @override + final String candidate; + + factory _$SignalingICECandidateMessage_Payload_Candidate( + [void Function(SignalingICECandidateMessage_Payload_CandidateBuilder)? updates]) => + (SignalingICECandidateMessage_Payload_CandidateBuilder()..update(updates))._build(); + + _$SignalingICECandidateMessage_Payload_Candidate._( + {required this.sdpMLineIndex, required this.sdpMid, required this.candidate}) + : super._() { + BuiltValueNullFieldError.checkNotNull( + sdpMLineIndex, r'SignalingICECandidateMessage_Payload_Candidate', 'sdpMLineIndex'); + BuiltValueNullFieldError.checkNotNull(sdpMid, r'SignalingICECandidateMessage_Payload_Candidate', 'sdpMid'); + BuiltValueNullFieldError.checkNotNull(candidate, r'SignalingICECandidateMessage_Payload_Candidate', 'candidate'); + } + + @override + SignalingICECandidateMessage_Payload_Candidate rebuild( + void Function(SignalingICECandidateMessage_Payload_CandidateBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingICECandidateMessage_Payload_CandidateBuilder toBuilder() => + SignalingICECandidateMessage_Payload_CandidateBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingICECandidateMessage_Payload_Candidate && + sdpMLineIndex == other.sdpMLineIndex && + sdpMid == other.sdpMid && + candidate == other.candidate; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, sdpMLineIndex.hashCode); + _$hash = $jc(_$hash, sdpMid.hashCode); + _$hash = $jc(_$hash, candidate.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingICECandidateMessage_Payload_Candidate') + ..add('sdpMLineIndex', sdpMLineIndex) + ..add('sdpMid', sdpMid) + ..add('candidate', candidate)) + .toString(); + } +} + +class SignalingICECandidateMessage_Payload_CandidateBuilder + implements + Builder, + $SignalingICECandidateMessage_Payload_CandidateInterfaceBuilder { + _$SignalingICECandidateMessage_Payload_Candidate? _$v; + + int? _sdpMLineIndex; + int? get sdpMLineIndex => _$this._sdpMLineIndex; + set sdpMLineIndex(covariant int? sdpMLineIndex) => _$this._sdpMLineIndex = sdpMLineIndex; + + String? _sdpMid; + String? get sdpMid => _$this._sdpMid; + set sdpMid(covariant String? sdpMid) => _$this._sdpMid = sdpMid; + + String? _candidate; + String? get candidate => _$this._candidate; + set candidate(covariant String? candidate) => _$this._candidate = candidate; + + SignalingICECandidateMessage_Payload_CandidateBuilder(); + + SignalingICECandidateMessage_Payload_CandidateBuilder get _$this { + final $v = _$v; + if ($v != null) { + _sdpMLineIndex = $v.sdpMLineIndex; + _sdpMid = $v.sdpMid; + _candidate = $v.candidate; + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingICECandidateMessage_Payload_Candidate other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingICECandidateMessage_Payload_Candidate; + } + + @override + void update(void Function(SignalingICECandidateMessage_Payload_CandidateBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingICECandidateMessage_Payload_Candidate build() => _build(); + + _$SignalingICECandidateMessage_Payload_Candidate _build() { + final _$result = _$v ?? + _$SignalingICECandidateMessage_Payload_Candidate._( + sdpMLineIndex: BuiltValueNullFieldError.checkNotNull( + sdpMLineIndex, r'SignalingICECandidateMessage_Payload_Candidate', 'sdpMLineIndex'), + sdpMid: BuiltValueNullFieldError.checkNotNull( + sdpMid, r'SignalingICECandidateMessage_Payload_Candidate', 'sdpMid'), + candidate: BuiltValueNullFieldError.checkNotNull( + candidate, r'SignalingICECandidateMessage_Payload_Candidate', 'candidate')); + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingICECandidateMessage_PayloadInterfaceBuilder { + void replace($SignalingICECandidateMessage_PayloadInterface other); + void update(void Function($SignalingICECandidateMessage_PayloadInterfaceBuilder) updates); + SignalingICECandidateMessage_Payload_CandidateBuilder get candidate; + set candidate(SignalingICECandidateMessage_Payload_CandidateBuilder? candidate); +} + +class _$SignalingICECandidateMessage_Payload extends SignalingICECandidateMessage_Payload { + @override + final SignalingICECandidateMessage_Payload_Candidate candidate; + + factory _$SignalingICECandidateMessage_Payload( + [void Function(SignalingICECandidateMessage_PayloadBuilder)? updates]) => + (SignalingICECandidateMessage_PayloadBuilder()..update(updates))._build(); + + _$SignalingICECandidateMessage_Payload._({required this.candidate}) : super._() { + BuiltValueNullFieldError.checkNotNull(candidate, r'SignalingICECandidateMessage_Payload', 'candidate'); + } + + @override + SignalingICECandidateMessage_Payload rebuild(void Function(SignalingICECandidateMessage_PayloadBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingICECandidateMessage_PayloadBuilder toBuilder() => + SignalingICECandidateMessage_PayloadBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingICECandidateMessage_Payload && candidate == other.candidate; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, candidate.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingICECandidateMessage_Payload')..add('candidate', candidate)) + .toString(); + } +} + +class SignalingICECandidateMessage_PayloadBuilder + implements + Builder, + $SignalingICECandidateMessage_PayloadInterfaceBuilder { + _$SignalingICECandidateMessage_Payload? _$v; + + SignalingICECandidateMessage_Payload_CandidateBuilder? _candidate; + SignalingICECandidateMessage_Payload_CandidateBuilder get candidate => + _$this._candidate ??= SignalingICECandidateMessage_Payload_CandidateBuilder(); + set candidate(covariant SignalingICECandidateMessage_Payload_CandidateBuilder? candidate) => + _$this._candidate = candidate; + + SignalingICECandidateMessage_PayloadBuilder(); + + SignalingICECandidateMessage_PayloadBuilder get _$this { + final $v = _$v; + if ($v != null) { + _candidate = $v.candidate.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingICECandidateMessage_Payload other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingICECandidateMessage_Payload; + } + + @override + void update(void Function(SignalingICECandidateMessage_PayloadBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingICECandidateMessage_Payload build() => _build(); + + _$SignalingICECandidateMessage_Payload _build() { + _$SignalingICECandidateMessage_Payload _$result; + try { + _$result = _$v ?? _$SignalingICECandidateMessage_Payload._(candidate: candidate.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'candidate'; + candidate.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingICECandidateMessage_Payload', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingICECandidateMessageInterfaceBuilder { + void replace($SignalingICECandidateMessageInterface other); + void update(void Function($SignalingICECandidateMessageInterfaceBuilder) updates); + String? get from; + set from(String? from); + + String? get to; + set to(String? to); + + SignalingMessageType? get type; + set type(SignalingMessageType? type); + + SignalingRoomType? get roomType; + set roomType(SignalingRoomType? roomType); + + String? get sid; + set sid(String? sid); + + SignalingICECandidateMessage_PayloadBuilder get payload; + set payload(SignalingICECandidateMessage_PayloadBuilder? payload); +} + +class _$SignalingICECandidateMessage extends SignalingICECandidateMessage { + @override + final String from; + @override + final String to; + @override + final SignalingMessageType type; + @override + final SignalingRoomType? roomType; + @override + final String? sid; + @override + final SignalingICECandidateMessage_Payload payload; + + factory _$SignalingICECandidateMessage([void Function(SignalingICECandidateMessageBuilder)? updates]) => + (SignalingICECandidateMessageBuilder()..update(updates))._build(); + + _$SignalingICECandidateMessage._( + {required this.from, required this.to, required this.type, this.roomType, this.sid, required this.payload}) + : super._() { + BuiltValueNullFieldError.checkNotNull(from, r'SignalingICECandidateMessage', 'from'); + BuiltValueNullFieldError.checkNotNull(to, r'SignalingICECandidateMessage', 'to'); + BuiltValueNullFieldError.checkNotNull(type, r'SignalingICECandidateMessage', 'type'); + BuiltValueNullFieldError.checkNotNull(payload, r'SignalingICECandidateMessage', 'payload'); + } + + @override + SignalingICECandidateMessage rebuild(void Function(SignalingICECandidateMessageBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingICECandidateMessageBuilder toBuilder() => SignalingICECandidateMessageBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingICECandidateMessage && + from == other.from && + to == other.to && + type == other.type && + roomType == other.roomType && + sid == other.sid && + payload == other.payload; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, from.hashCode); + _$hash = $jc(_$hash, to.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, roomType.hashCode); + _$hash = $jc(_$hash, sid.hashCode); + _$hash = $jc(_$hash, payload.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingICECandidateMessage') + ..add('from', from) + ..add('to', to) + ..add('type', type) + ..add('roomType', roomType) + ..add('sid', sid) + ..add('payload', payload)) + .toString(); + } +} + +class SignalingICECandidateMessageBuilder + implements + Builder, + $SignalingICECandidateMessageInterfaceBuilder { + _$SignalingICECandidateMessage? _$v; + + String? _from; + String? get from => _$this._from; + set from(covariant String? from) => _$this._from = from; + + String? _to; + String? get to => _$this._to; + set to(covariant String? to) => _$this._to = to; + + SignalingMessageType? _type; + SignalingMessageType? get type => _$this._type; + set type(covariant SignalingMessageType? type) => _$this._type = type; + + SignalingRoomType? _roomType; + SignalingRoomType? get roomType => _$this._roomType; + set roomType(covariant SignalingRoomType? roomType) => _$this._roomType = roomType; + + String? _sid; + String? get sid => _$this._sid; + set sid(covariant String? sid) => _$this._sid = sid; + + SignalingICECandidateMessage_PayloadBuilder? _payload; + SignalingICECandidateMessage_PayloadBuilder get payload => + _$this._payload ??= SignalingICECandidateMessage_PayloadBuilder(); + set payload(covariant SignalingICECandidateMessage_PayloadBuilder? payload) => _$this._payload = payload; + + SignalingICECandidateMessageBuilder(); + + SignalingICECandidateMessageBuilder get _$this { + final $v = _$v; + if ($v != null) { + _from = $v.from; + _to = $v.to; + _type = $v.type; + _roomType = $v.roomType; + _sid = $v.sid; + _payload = $v.payload.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingICECandidateMessage other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingICECandidateMessage; + } + + @override + void update(void Function(SignalingICECandidateMessageBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingICECandidateMessage build() => _build(); + + _$SignalingICECandidateMessage _build() { + _$SignalingICECandidateMessage _$result; + try { + _$result = _$v ?? + _$SignalingICECandidateMessage._( + from: BuiltValueNullFieldError.checkNotNull(from, r'SignalingICECandidateMessage', 'from'), + to: BuiltValueNullFieldError.checkNotNull(to, r'SignalingICECandidateMessage', 'to'), + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingICECandidateMessage', 'type'), + roomType: roomType, + sid: sid, + payload: payload.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'payload'; + payload.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingICECandidateMessage', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingMuteMessage_PayloadInterfaceBuilder { + void replace($SignalingMuteMessage_PayloadInterface other); + void update(void Function($SignalingMuteMessage_PayloadInterfaceBuilder) updates); + SignalingMuteMessage_Payload_Name? get name; + set name(SignalingMuteMessage_Payload_Name? name); +} + +class _$SignalingMuteMessage_Payload extends SignalingMuteMessage_Payload { + @override + final SignalingMuteMessage_Payload_Name name; + + factory _$SignalingMuteMessage_Payload([void Function(SignalingMuteMessage_PayloadBuilder)? updates]) => + (SignalingMuteMessage_PayloadBuilder()..update(updates))._build(); + + _$SignalingMuteMessage_Payload._({required this.name}) : super._() { + BuiltValueNullFieldError.checkNotNull(name, r'SignalingMuteMessage_Payload', 'name'); + } + + @override + SignalingMuteMessage_Payload rebuild(void Function(SignalingMuteMessage_PayloadBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingMuteMessage_PayloadBuilder toBuilder() => SignalingMuteMessage_PayloadBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingMuteMessage_Payload && name == other.name; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, name.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingMuteMessage_Payload')..add('name', name)).toString(); + } +} + +class SignalingMuteMessage_PayloadBuilder + implements + Builder, + $SignalingMuteMessage_PayloadInterfaceBuilder { + _$SignalingMuteMessage_Payload? _$v; + + SignalingMuteMessage_Payload_Name? _name; + SignalingMuteMessage_Payload_Name? get name => _$this._name; + set name(covariant SignalingMuteMessage_Payload_Name? name) => _$this._name = name; + + SignalingMuteMessage_PayloadBuilder(); + + SignalingMuteMessage_PayloadBuilder get _$this { + final $v = _$v; + if ($v != null) { + _name = $v.name; + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingMuteMessage_Payload other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingMuteMessage_Payload; + } + + @override + void update(void Function(SignalingMuteMessage_PayloadBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingMuteMessage_Payload build() => _build(); + + _$SignalingMuteMessage_Payload _build() { + final _$result = _$v ?? + _$SignalingMuteMessage_Payload._( + name: BuiltValueNullFieldError.checkNotNull(name, r'SignalingMuteMessage_Payload', 'name')); + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingMuteMessageInterfaceBuilder { + void replace($SignalingMuteMessageInterface other); + void update(void Function($SignalingMuteMessageInterfaceBuilder) updates); + String? get from; + set from(String? from); + + String? get to; + set to(String? to); + + SignalingMessageType? get type; + set type(SignalingMessageType? type); + + SignalingRoomType? get roomType; + set roomType(SignalingRoomType? roomType); + + String? get sid; + set sid(String? sid); + + SignalingMuteMessage_PayloadBuilder get payload; + set payload(SignalingMuteMessage_PayloadBuilder? payload); +} + +class _$SignalingMuteMessage extends SignalingMuteMessage { + @override + final String from; + @override + final String to; + @override + final SignalingMessageType type; + @override + final SignalingRoomType? roomType; + @override + final String? sid; + @override + final SignalingMuteMessage_Payload payload; + + factory _$SignalingMuteMessage([void Function(SignalingMuteMessageBuilder)? updates]) => + (SignalingMuteMessageBuilder()..update(updates))._build(); + + _$SignalingMuteMessage._( + {required this.from, required this.to, required this.type, this.roomType, this.sid, required this.payload}) + : super._() { + BuiltValueNullFieldError.checkNotNull(from, r'SignalingMuteMessage', 'from'); + BuiltValueNullFieldError.checkNotNull(to, r'SignalingMuteMessage', 'to'); + BuiltValueNullFieldError.checkNotNull(type, r'SignalingMuteMessage', 'type'); + BuiltValueNullFieldError.checkNotNull(payload, r'SignalingMuteMessage', 'payload'); + } + + @override + SignalingMuteMessage rebuild(void Function(SignalingMuteMessageBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingMuteMessageBuilder toBuilder() => SignalingMuteMessageBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + return other is SignalingMuteMessage && + from == other.from && + to == other.to && + type == other.type && + roomType == other.roomType && + sid == other.sid && + payload == other.payload; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, from.hashCode); + _$hash = $jc(_$hash, to.hashCode); + _$hash = $jc(_$hash, type.hashCode); + _$hash = $jc(_$hash, roomType.hashCode); + _$hash = $jc(_$hash, sid.hashCode); + _$hash = $jc(_$hash, payload.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingMuteMessage') + ..add('from', from) + ..add('to', to) + ..add('type', type) + ..add('roomType', roomType) + ..add('sid', sid) + ..add('payload', payload)) + .toString(); + } +} + +class SignalingMuteMessageBuilder + implements Builder, $SignalingMuteMessageInterfaceBuilder { + _$SignalingMuteMessage? _$v; + + String? _from; + String? get from => _$this._from; + set from(covariant String? from) => _$this._from = from; + + String? _to; + String? get to => _$this._to; + set to(covariant String? to) => _$this._to = to; + + SignalingMessageType? _type; + SignalingMessageType? get type => _$this._type; + set type(covariant SignalingMessageType? type) => _$this._type = type; + + SignalingRoomType? _roomType; + SignalingRoomType? get roomType => _$this._roomType; + set roomType(covariant SignalingRoomType? roomType) => _$this._roomType = roomType; + + String? _sid; + String? get sid => _$this._sid; + set sid(covariant String? sid) => _$this._sid = sid; + + SignalingMuteMessage_PayloadBuilder? _payload; + SignalingMuteMessage_PayloadBuilder get payload => _$this._payload ??= SignalingMuteMessage_PayloadBuilder(); + set payload(covariant SignalingMuteMessage_PayloadBuilder? payload) => _$this._payload = payload; + + SignalingMuteMessageBuilder(); + + SignalingMuteMessageBuilder get _$this { + final $v = _$v; + if ($v != null) { + _from = $v.from; + _to = $v.to; + _type = $v.type; + _roomType = $v.roomType; + _sid = $v.sid; + _payload = $v.payload.toBuilder(); + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingMuteMessage other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingMuteMessage; + } + + @override + void update(void Function(SignalingMuteMessageBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingMuteMessage build() => _build(); + + _$SignalingMuteMessage _build() { + _$SignalingMuteMessage _$result; + try { + _$result = _$v ?? + _$SignalingMuteMessage._( + from: BuiltValueNullFieldError.checkNotNull(from, r'SignalingMuteMessage', 'from'), + to: BuiltValueNullFieldError.checkNotNull(to, r'SignalingMuteMessage', 'to'), + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingMuteMessage', 'type'), + roomType: roomType, + sid: sid, + payload: payload.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'payload'; + payload.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingMuteMessage', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + +abstract mixin class $SignalingMessageWrapperInterfaceBuilder { + void replace($SignalingMessageWrapperInterface other); + void update(void Function($SignalingMessageWrapperInterfaceBuilder) updates); + String? get type; + set type(String? type); + + ContentStringBuilder get data; + set data(ContentStringBuilder? data); +} + +class _$SignalingMessageWrapper extends SignalingMessageWrapper { + @override + final String type; + @override + final ContentString data; + + factory _$SignalingMessageWrapper([void Function(SignalingMessageWrapperBuilder)? updates]) => + (SignalingMessageWrapperBuilder()..update(updates))._build(); + + _$SignalingMessageWrapper._({required this.type, required this.data}) : super._() { + BuiltValueNullFieldError.checkNotNull(type, r'SignalingMessageWrapper', 'type'); + BuiltValueNullFieldError.checkNotNull(data, r'SignalingMessageWrapper', 'data'); + } + + @override + SignalingMessageWrapper rebuild(void Function(SignalingMessageWrapperBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingMessageWrapperBuilder toBuilder() => SignalingMessageWrapperBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + final dynamic _$dynamicOther = other; + return other is SignalingMessageWrapper && type == other.type && data == _$dynamicOther.data; } @override @@ -52261,62 +53982,70 @@ class _$SignalingPullMessagesResponseApplicationJson_Ocs_Data @override String toString() { - return (newBuiltValueToStringHelper(r'SignalingPullMessagesResponseApplicationJson_Ocs_Data') + return (newBuiltValueToStringHelper(r'SignalingMessageWrapper') ..add('type', type) ..add('data', data)) .toString(); } } -class SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder +class SignalingMessageWrapperBuilder implements - Builder, - $SignalingPullMessagesResponseApplicationJson_Ocs_DataInterfaceBuilder { - _$SignalingPullMessagesResponseApplicationJson_Ocs_Data? _$v; + Builder, + $SignalingMessageWrapperInterfaceBuilder { + _$SignalingMessageWrapper? _$v; String? _type; String? get type => _$this._type; set type(covariant String? type) => _$this._type = type; - SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data? _data; - SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data? get data => _$this._data; - set data(covariant SignalingPullMessagesResponseApplicationJson_Ocs_Data_Data? data) => _$this._data = data; + ContentStringBuilder? _data; + ContentStringBuilder get data => _$this._data ??= ContentStringBuilder(); + set data(covariant ContentStringBuilder? data) => _$this._data = data; - SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder(); + SignalingMessageWrapperBuilder(); - SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder get _$this { + SignalingMessageWrapperBuilder get _$this { final $v = _$v; if ($v != null) { _type = $v.type; - _data = $v.data; + _data = $v.data.toBuilder(); _$v = null; } return this; } @override - void replace(covariant SignalingPullMessagesResponseApplicationJson_Ocs_Data other) { + void replace(covariant SignalingMessageWrapper other) { ArgumentError.checkNotNull(other, 'other'); - _$v = other as _$SignalingPullMessagesResponseApplicationJson_Ocs_Data; + _$v = other as _$SignalingMessageWrapper; } @override - void update(void Function(SignalingPullMessagesResponseApplicationJson_Ocs_DataBuilder)? updates) { + void update(void Function(SignalingMessageWrapperBuilder)? updates) { if (updates != null) updates(this); } @override - SignalingPullMessagesResponseApplicationJson_Ocs_Data build() => _build(); + SignalingMessageWrapper build() => _build(); - _$SignalingPullMessagesResponseApplicationJson_Ocs_Data _build() { - SignalingPullMessagesResponseApplicationJson_Ocs_Data._validate(this); - final _$result = _$v ?? - _$SignalingPullMessagesResponseApplicationJson_Ocs_Data._( - type: BuiltValueNullFieldError.checkNotNull( - type, r'SignalingPullMessagesResponseApplicationJson_Ocs_Data', 'type'), - data: BuiltValueNullFieldError.checkNotNull( - data, r'SignalingPullMessagesResponseApplicationJson_Ocs_Data', 'data')); + _$SignalingMessageWrapper _build() { + _$SignalingMessageWrapper _$result; + try { + _$result = _$v ?? + _$SignalingMessageWrapper._( + type: BuiltValueNullFieldError.checkNotNull(type, r'SignalingMessageWrapper', 'type'), + data: data.build()); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'data'; + data.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingMessageWrapper', _$failedField, e.toString()); + } + rethrow; + } replace(_$result); return _$result; } @@ -52328,15 +54057,15 @@ abstract mixin class $SignalingPullMessagesResponseApplicationJson_OcsInterfaceB OCSMetaBuilder get meta; set meta(OCSMetaBuilder? meta); - ListBuilder get data; - set data(ListBuilder? data); + ListBuilder get data; + set data(ListBuilder? data); } class _$SignalingPullMessagesResponseApplicationJson_Ocs extends SignalingPullMessagesResponseApplicationJson_Ocs { @override final OCSMeta meta; @override - final BuiltList data; + final BuiltList data; factory _$SignalingPullMessagesResponseApplicationJson_Ocs( [void Function(SignalingPullMessagesResponseApplicationJson_OcsBuilder)? updates]) => @@ -52359,7 +54088,10 @@ class _$SignalingPullMessagesResponseApplicationJson_Ocs extends SignalingPullMe @override bool operator ==(Object other) { if (identical(other, this)) return true; - return other is SignalingPullMessagesResponseApplicationJson_Ocs && meta == other.meta && data == other.data; + final dynamic _$dynamicOther = other; + return other is SignalingPullMessagesResponseApplicationJson_Ocs && + meta == other.meta && + data == _$dynamicOther.data; } @override @@ -52391,10 +54123,9 @@ class SignalingPullMessagesResponseApplicationJson_OcsBuilder OCSMetaBuilder get meta => _$this._meta ??= OCSMetaBuilder(); set meta(covariant OCSMetaBuilder? meta) => _$this._meta = meta; - ListBuilder? _data; - ListBuilder get data => - _$this._data ??= ListBuilder(); - set data(covariant ListBuilder? data) => _$this._data = data; + ListBuilder? _data; + ListBuilder get data => _$this._data ??= ListBuilder(); + set data(covariant ListBuilder? data) => _$this._data = data; SignalingPullMessagesResponseApplicationJson_OcsBuilder(); @@ -52547,6 +54278,144 @@ class SignalingPullMessagesResponseApplicationJsonBuilder } } +abstract mixin class $SignalingSendMessagesMessagesInterfaceBuilder { + void replace($SignalingSendMessagesMessagesInterface other); + void update(void Function($SignalingSendMessagesMessagesInterfaceBuilder) updates); + String? get ev; + set ev(String? ev); + + ContentStringBuilder get fn; + set fn(ContentStringBuilder? fn); + + String? get sessionId; + set sessionId(String? sessionId); +} + +class _$SignalingSendMessagesMessages extends SignalingSendMessagesMessages { + @override + final String ev; + @override + final ContentString fn; + @override + final String sessionId; + + factory _$SignalingSendMessagesMessages([void Function(SignalingSendMessagesMessagesBuilder)? updates]) => + (SignalingSendMessagesMessagesBuilder()..update(updates))._build(); + + _$SignalingSendMessagesMessages._({required this.ev, required this.fn, required this.sessionId}) : super._() { + BuiltValueNullFieldError.checkNotNull(ev, r'SignalingSendMessagesMessages', 'ev'); + BuiltValueNullFieldError.checkNotNull(fn, r'SignalingSendMessagesMessages', 'fn'); + BuiltValueNullFieldError.checkNotNull(sessionId, r'SignalingSendMessagesMessages', 'sessionId'); + } + + @override + SignalingSendMessagesMessages rebuild(void Function(SignalingSendMessagesMessagesBuilder) updates) => + (toBuilder()..update(updates)).build(); + + @override + SignalingSendMessagesMessagesBuilder toBuilder() => SignalingSendMessagesMessagesBuilder()..replace(this); + + @override + bool operator ==(Object other) { + if (identical(other, this)) return true; + final dynamic _$dynamicOther = other; + return other is SignalingSendMessagesMessages && + ev == other.ev && + fn == _$dynamicOther.fn && + sessionId == other.sessionId; + } + + @override + int get hashCode { + var _$hash = 0; + _$hash = $jc(_$hash, ev.hashCode); + _$hash = $jc(_$hash, fn.hashCode); + _$hash = $jc(_$hash, sessionId.hashCode); + _$hash = $jf(_$hash); + return _$hash; + } + + @override + String toString() { + return (newBuiltValueToStringHelper(r'SignalingSendMessagesMessages') + ..add('ev', ev) + ..add('fn', fn) + ..add('sessionId', sessionId)) + .toString(); + } +} + +class SignalingSendMessagesMessagesBuilder + implements + Builder, + $SignalingSendMessagesMessagesInterfaceBuilder { + _$SignalingSendMessagesMessages? _$v; + + String? _ev; + String? get ev => _$this._ev; + set ev(covariant String? ev) => _$this._ev = ev; + + ContentStringBuilder? _fn; + ContentStringBuilder get fn => _$this._fn ??= ContentStringBuilder(); + set fn(covariant ContentStringBuilder? fn) => _$this._fn = fn; + + String? _sessionId; + String? get sessionId => _$this._sessionId; + set sessionId(covariant String? sessionId) => _$this._sessionId = sessionId; + + SignalingSendMessagesMessagesBuilder() { + SignalingSendMessagesMessages._defaults(this); + } + + SignalingSendMessagesMessagesBuilder get _$this { + final $v = _$v; + if ($v != null) { + _ev = $v.ev; + _fn = $v.fn.toBuilder(); + _sessionId = $v.sessionId; + _$v = null; + } + return this; + } + + @override + void replace(covariant SignalingSendMessagesMessages other) { + ArgumentError.checkNotNull(other, 'other'); + _$v = other as _$SignalingSendMessagesMessages; + } + + @override + void update(void Function(SignalingSendMessagesMessagesBuilder)? updates) { + if (updates != null) updates(this); + } + + @override + SignalingSendMessagesMessages build() => _build(); + + _$SignalingSendMessagesMessages _build() { + _$SignalingSendMessagesMessages _$result; + try { + _$result = _$v ?? + _$SignalingSendMessagesMessages._( + ev: BuiltValueNullFieldError.checkNotNull(ev, r'SignalingSendMessagesMessages', 'ev'), + fn: fn.build(), + sessionId: + BuiltValueNullFieldError.checkNotNull(sessionId, r'SignalingSendMessagesMessages', 'sessionId')); + } catch (_) { + late String _$failedField; + try { + _$failedField = 'fn'; + fn.build(); + } catch (e) { + throw BuiltValueNestedFieldError(r'SignalingSendMessagesMessages', _$failedField, e.toString()); + } + rethrow; + } + replace(_$result); + return _$result; + } +} + abstract mixin class $SignalingSendMessagesResponseApplicationJson_OcsInterfaceBuilder { void replace($SignalingSendMessagesResponseApplicationJson_OcsInterface other); void update(void Function($SignalingSendMessagesResponseApplicationJson_OcsInterfaceBuilder) updates); diff --git a/packages/nextcloud/lib/src/api/spreed.openapi.json b/packages/nextcloud/lib/src/api/spreed.openapi.json index d98c4fb5e25..a9ac41f1264 100644 --- a/packages/nextcloud/lib/src/api/spreed.openapi.json +++ b/packages/nextcloud/lib/src/api/spreed.openapi.json @@ -1239,6 +1239,248 @@ "federated_users", "phones" ] + }, + "SignalingSessions": { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SignalingSession" + } + } + } + }, + "SignalingData": { + "discriminator": { + "propertyName": "type", + "mapping": { + "usersInRoom": "#/components/schemas/SignalingSessions", + "message": "#/components/schemas/SignalingMessageWrapper" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/SignalingSessions" + }, + { + "$ref": "#/components/schemas/SignalingMessageWrapper" + } + ] + }, + "SignalingMessageWrapper": { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string" + }, + "data": { + "type": "string", + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/components/schemas/SignalingMessage" + } + } + } + }, + "SignalingMessageType": { + "type": "string", + "enum": [ + "offer", + "answer", + "candidate", + "unshareScreen", + "remove-candidates", + "control", + "forceMute", + "mute", + "unmute", + "nickChanged" + ] + }, + "SignalingMessage": { + "discriminator": { + "propertyName": "type", + "mapping": { + "offer": "#/components/schemas/SignalingSessionDescriptionMessage", + "answer": "#/components/schemas/SignalingSessionDescriptionMessage", + "candidate": "#/components/schemas/SignalingICECandidateMessage", + "mute": "#/components/schemas/SignalingMuteMessage", + "unmute": "#/components/schemas/SignalingMuteMessage" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/SignalingSessionDescriptionMessage" + }, + { + "$ref": "#/components/schemas/SignalingICECandidateMessage" + }, + { + "$ref": "#/components/schemas/SignalingMuteMessage" + } + ] + }, + "SignalingRoomType": { + "type": "string", + "enum": [ + "video", + "screen" + ] + }, + "SignalingSessionDescriptionMessage": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "type", + "sdp", + "nick" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "offer", + "answer" + ] + }, + "sdp": { + "type": "string" + }, + "nick": { + "type": "string" + } + } + } + } + }, + "SignalingICECandidateMessage": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "candidate" + ], + "properties": { + "candidate": { + "type": "object", + "required": [ + "sdpMLineIndex", + "sdpMid", + "candidate" + ], + "properties": { + "sdpMLineIndex": { + "type": "integer" + }, + "sdpMid": { + "type": "string" + }, + "candidate": { + "type": "string" + } + } + } + } + } + } + }, + "SignalingMuteMessage": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "enum": [ + "audio", + "video" + ] + } + } + } + } } } }, @@ -16086,8 +16328,36 @@ "in": "query", "description": "JSON encoded messages", "required": true, - "schema": { - "type": "string" + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "required": [ + "ev", + "fn", + "sessionId" + ], + "properties": { + "ev": { + "type": "string", + "default": "message" + }, + "fn": { + "type": "string", + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/components/schemas/SignalingMessage" + } + }, + "sessionId": { + "type": "string" + } + } + } + } + } } }, { @@ -16257,29 +16527,7 @@ "data": { "type": "array", "items": { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string" - }, - "data": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/components/schemas/SignalingSession" - } - }, - { - "type": "string" - } - ] - } - } + "$ref": "#/components/schemas/SignalingData" } } } @@ -16312,29 +16560,7 @@ "data": { "type": "array", "items": { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string" - }, - "data": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/components/schemas/SignalingSession" - } - }, - { - "type": "string" - } - ] - } - } + "$ref": "#/components/schemas/SignalingData" } } } @@ -16367,29 +16593,7 @@ "data": { "type": "array", "items": { - "type": "object", - "required": [ - "type", - "data" - ], - "properties": { - "type": { - "type": "string" - }, - "data": { - "oneOf": [ - { - "type": "array", - "items": { - "$ref": "#/components/schemas/SignalingSession" - } - }, - { - "type": "string" - } - ] - } - } + "$ref": "#/components/schemas/SignalingData" } } } diff --git a/packages/nextcloud/lib/src/patches/spreed/4-internal-signaling.json b/packages/nextcloud/lib/src/patches/spreed/4-internal-signaling.json new file mode 100644 index 00000000000..64fe3d8bece --- /dev/null +++ b/packages/nextcloud/lib/src/patches/spreed/4-internal-signaling.json @@ -0,0 +1,342 @@ +[ + { + "op": "replace", + "path": "/paths/~1ocs~1v2.php~1apps~1spreed~1api~1{apiVersion}~1signaling~1{token}/get/responses/200/content/application~1json/schema/properties/ocs/properties/data/items", + "value": { + "$ref": "#/components/schemas/SignalingData" + } + }, + { + "op": "replace", + "path": "/paths/~1ocs~1v2.php~1apps~1spreed~1api~1{apiVersion}~1signaling~1{token}/get/responses/404/content/application~1json/schema/properties/ocs/properties/data/items", + "value": { + "$ref": "#/components/schemas/SignalingData" + } + }, + { + "op": "replace", + "path": "/paths/~1ocs~1v2.php~1apps~1spreed~1api~1{apiVersion}~1signaling~1{token}/get/responses/409/content/application~1json/schema/properties/ocs/properties/data/items", + "value": { + "$ref": "#/components/schemas/SignalingData" + } + }, + { + "op": "replace", + "path": "/paths/~1ocs~1v2.php~1apps~1spreed~1api~1{apiVersion}~1signaling~1{token}/post/parameters/0", + "value": { + "name": "messages", + "in": "query", + "description": "JSON encoded messages", + "required": true, + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "required": [ + "ev", + "fn", + "sessionId" + ], + "properties": { + "ev": { + "type": "string", + "default": "message" + }, + "fn": { + "type": "string", + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/components/schemas/SignalingMessage" + } + }, + "sessionId": { + "type": "string" + } + } + } + } + } + } + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingSessions", + "value": { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string" + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/SignalingSession" + } + } + } + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingData", + "value": { + "discriminator": { + "propertyName": "type", + "mapping": { + "usersInRoom": "#/components/schemas/SignalingSessions", + "message": "#/components/schemas/SignalingMessageWrapper" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/SignalingSessions" + }, + { + "$ref": "#/components/schemas/SignalingMessageWrapper" + } + ] + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingMessageWrapper", + "value": { + "type": "object", + "required": [ + "type", + "data" + ], + "properties": { + "type": { + "type": "string" + }, + "data": { + "type": "string", + "contentMediaType": "application/json", + "contentSchema": { + "$ref": "#/components/schemas/SignalingMessage" + } + } + } + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingMessageType", + "value": { + "type": "string", + "enum": [ + "offer", + "answer", + "candidate", + "unshareScreen", + "remove-candidates", + "control", + "forceMute", + "mute", + "unmute", + "nickChanged" + ] + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingMessage", + "value": { + "discriminator": { + "propertyName": "type", + "mapping": { + "offer": "#/components/schemas/SignalingSessionDescriptionMessage", + "answer": "#/components/schemas/SignalingSessionDescriptionMessage", + "candidate": "#/components/schemas/SignalingICECandidateMessage", + "mute": "#/components/schemas/SignalingMuteMessage", + "unmute": "#/components/schemas/SignalingMuteMessage" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/SignalingSessionDescriptionMessage" + }, + { + "$ref": "#/components/schemas/SignalingICECandidateMessage" + }, + { + "$ref": "#/components/schemas/SignalingMuteMessage" + } + ] + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingRoomType", + "value": { + "type": "string", + "enum": [ + "video", + "screen" + ] + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingSessionDescriptionMessage", + "value": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "type", + "sdp", + "nick" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "offer", + "answer" + ] + }, + "sdp": { + "type": "string" + }, + "nick": { + "type": "string" + } + } + } + } + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingICECandidateMessage", + "value": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "candidate" + ], + "properties": { + "candidate": { + "type": "object", + "required": [ + "sdpMLineIndex", + "sdpMid", + "candidate" + ], + "properties": { + "sdpMLineIndex": { + "type": "integer" + }, + "sdpMid": { + "type": "string" + }, + "candidate": { + "type": "string" + } + } + } + } + } + } + } + }, + { + "op": "add", + "path": "/components/schemas/SignalingMuteMessage", + "value": { + "type": "object", + "required": [ + "from", + "to", + "type", + "payload" + ], + "properties": { + "from": { + "type": "string" + }, + "to": { + "type": "string" + }, + "type": { + "$ref": "#/components/schemas/SignalingMessageType" + }, + "roomType": { + "$ref": "#/components/schemas/SignalingRoomType" + }, + "sid": { + "type": "string" + }, + "payload": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "enum": [ + "audio", + "video" + ] + } + } + } + } + } + } +] diff --git a/packages/nextcloud/test/spreed_test.dart b/packages/nextcloud/test/spreed_test.dart index 86647f97618..33189ebc39f 100644 --- a/packages/nextcloud/test/spreed_test.dart +++ b/packages/nextcloud/test/spreed_test.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'dart:convert'; +import 'package:built_collection/built_collection.dart'; import 'package:built_value/json_object.dart'; import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/nextcloud.dart'; @@ -425,29 +425,46 @@ void main() { final room2 = (await client2.spreed.room.joinRoom(token: room.token)).body.ocs.data; await client2.spreed.call.joinCall(token: room.token); + final muteMessage = spreed.SignalingMuteMessage( + (b) => b + ..from = room1.sessionId + ..to = room2.sessionId + ..type = spreed.SignalingMessageType.mute + ..payload.update( + (b) => b..name = spreed.SignalingMuteMessage_Payload_Name.audio, + ), + ); await client1.spreed.signaling.sendMessages( token: room.token, - messages: json.encode([ - { - 'ev': 'message', - 'sessionId': room1.sessionId, - 'fn': json.encode({ - 'to': room2.sessionId, - }), - }, - ]), + messages: ContentString( + (b) => b + ..content = BuiltList([ + spreed.SignalingSendMessagesMessages( + (b) => b + ..sessionId = room1.sessionId + ..fn.update( + (b) => b + ..content = ( + signalingICECandidateMessage: null, + signalingMuteMessage: muteMessage, + signalingSessionDescriptionMessage: null, + ), + ), + ), + ]), + ), ); await Future.delayed(const Duration(seconds: 1)); final messages = (await client2.spreed.signaling.pullMessages(token: room.token)).body.ocs.data; expect(messages, hasLength(2)); - expect(messages[0].type, 'message'); - expect(json.decode(messages[0].data.string!), {'to': room2.sessionId, 'from': room1.sessionId}); - expect(messages[1].type, 'usersInRoom'); - expect(messages[1].data.builtListSignalingSession, hasLength(2)); - expect(messages[1].data.builtListSignalingSession![0].userId, 'user1'); - expect(messages[1].data.builtListSignalingSession![1].userId, 'user2'); + expect(messages[0].signalingMessageWrapper!.type, 'message'); + expect(messages[0].signalingMessageWrapper!.data.content.signalingMuteMessage, muteMessage); + expect(messages[1].signalingSessions!.type, 'usersInRoom'); + expect(messages[1].signalingSessions!.data, hasLength(2)); + expect(messages[1].signalingSessions!.data[0].userId, 'user1'); + expect(messages[1].signalingSessions!.data[1].userId, 'user2'); }); }); },