@@ -31,6 +31,28 @@ import '../track/remote/video.dart';
3131import '../types/other.dart' ;
3232import 'participant.dart' ;
3333
34+ /// Result of creating a RemoteParticipant with all its initial data populated.
35+ ///
36+ /// This struct is returned by [RemoteParticipant.createFromInfo] and contains
37+ /// the fully initialized participant along with the list of track publications
38+ /// that were added during creation. The caller is responsible for emitting
39+ /// events in the correct order.
40+ @internal
41+ class ParticipantCreationResult {
42+ /// The fully initialized remote participant with all basic info and tracks populated.
43+ final RemoteParticipant participant;
44+
45+ /// List of new track publications that were added during participant creation.
46+ /// The caller should emit [TrackPublishedEvent] for each of these after
47+ /// emitting [ParticipantConnectedEvent] .
48+ final List <RemoteTrackPublication > newPublications;
49+
50+ const ParticipantCreationResult ({
51+ required this .participant,
52+ required this .newPublications,
53+ });
54+ }
55+
3456/// Represents other participant in the [Room] .
3557class RemoteParticipant extends Participant <RemoteTrackPublication > {
3658 @internal
@@ -46,18 +68,70 @@ class RemoteParticipant extends Participant<RemoteTrackPublication> {
4668 name: name,
4769 );
4870
49- static Future <RemoteParticipant > createFromInfo ({
71+ /// Creates a fully initialized RemoteParticipant without emitting events.
72+ ///
73+ /// Populates the participant with all data from [info] including metadata, permissions,
74+ /// and track publications. No events are emitted, allowing the caller to control event
75+ /// timing and order.
76+ ///
77+ /// Returns [ParticipantCreationResult] with the participant and new track publications.
78+ /// The caller should emit [ParticipantConnectedEvent] first, then [TrackPublishedEvent]
79+ /// for each track, ensuring the participant is fully populated when connected event fires.
80+ ///
81+ /// @internal - Should only be called by [Room] .
82+ @internal
83+ static Future <ParticipantCreationResult > createFromInfo ({
5084 required Room room,
5185 required lk_models.ParticipantInfo info,
5286 }) async {
5387 final participant = RemoteParticipant (
5488 room: room,
55- sid: info.identity ,
89+ sid: info.sid ,
5690 identity: info.identity,
5791 name: info.name,
5892 );
59- await participant.updateFromInfo (info);
60- return participant;
93+ // Update basic participant info (state, metadata, etc.)
94+ await participant._updateBasicInfo (info);
95+ // Add tracks to participant without emitting events
96+ final newPubs = await participant._addTracks (info.tracks);
97+ // Return result for caller to emit events in correct order
98+ return ParticipantCreationResult (
99+ participant: participant,
100+ newPublications: newPubs,
101+ );
102+ }
103+
104+ Future <void > _updateBasicInfo (lk_models.ParticipantInfo info) async {
105+ // Only call superclass updateFromInfo to update basic participant state
106+ await super .updateFromInfo (info);
107+ }
108+
109+ Future <List <RemoteTrackPublication >> _addTracks (List <lk_models.TrackInfo > tracks) async {
110+ final newPubs = < RemoteTrackPublication > [];
111+ for (final trackInfo in tracks) {
112+ final RemoteTrackPublication ? pub = getTrackPublicationBySid (trackInfo.sid);
113+ if (pub == null ) {
114+ final RemoteTrackPublication pub;
115+ if (trackInfo.type == lk_models.TrackType .VIDEO ) {
116+ pub = RemoteTrackPublication <RemoteVideoTrack >(
117+ participant: this ,
118+ info: trackInfo,
119+ );
120+ } else if (trackInfo.type == lk_models.TrackType .AUDIO ) {
121+ pub = RemoteTrackPublication <RemoteAudioTrack >(
122+ participant: this ,
123+ info: trackInfo,
124+ );
125+ } else {
126+ throw UnexpectedStateException ('Unknown track type' );
127+ }
128+ newPubs.add (pub);
129+ addTrackPublication (pub);
130+ } else {
131+ pub.updateFromInfo (trackInfo);
132+ }
133+ }
134+ return newPubs;
61135 }
62136
63137 /// A convenience property to get all video tracks.
@@ -189,34 +263,16 @@ class RemoteParticipant extends Participant<RemoteTrackPublication> {
189263 //return false;
190264 }
191265
192- // figuring out deltas between tracks
193- final newPubs = < RemoteTrackPublication > {};
266+ await _updateTracks (info.tracks);
194267
195- for (final trackInfo in info.tracks) {
196- final RemoteTrackPublication ? pub = getTrackPublicationBySid (trackInfo.sid);
197- if (pub == null ) {
198- final RemoteTrackPublication pub;
199- if (trackInfo.type == lk_models.TrackType .VIDEO ) {
200- pub = RemoteTrackPublication <RemoteVideoTrack >(
201- participant: this ,
202- info: trackInfo,
203- );
204- } else if (trackInfo.type == lk_models.TrackType .AUDIO ) {
205- pub = RemoteTrackPublication <RemoteAudioTrack >(
206- participant: this ,
207- info: trackInfo,
208- );
209- } else {
210- throw UnexpectedStateException ('Unknown track type' );
211- }
212- newPubs.add (pub);
213- addTrackPublication (pub);
214- } else {
215- pub.updateFromInfo (trackInfo);
216- }
217- }
268+ return true ;
269+ }
218270
219- // always emit events for new publications, Room will not forward them unless it's ready
271+ Future <void > _updateTracks (List <lk_models.TrackInfo > tracks) async {
272+ // Add new tracks
273+ final newPubs = await _addTracks (tracks);
274+
275+ // Emit events for new publications
220276 for (final pub in newPubs) {
221277 final event = TrackPublishedEvent (
222278 participant: this ,
@@ -227,14 +283,12 @@ class RemoteParticipant extends Participant<RemoteTrackPublication> {
227283 }
228284 }
229285
230- // remove any published track that is not in the info
231- final validSids = info. tracks.map ((e) => e.sid);
286+ // Remove any published track that is not in the info
287+ final validSids = tracks.map ((e) => e.sid);
232288 final removeSids = trackPublications.keys.where ((e) => ! validSids.contains (e)).toSet ();
233289 for (final sid in removeSids) {
234290 await removePublishedTrack (sid);
235291 }
236-
237- return true ;
238292 }
239293
240294 Future <void > removePublishedTrack (String trackSid, {bool notify = true }) async {
0 commit comments