Is there a way to programmatically check if there are other connected peers?

Currently, I’m aware of an 'peers' event to which a callback can be attached like this: provider.on('peers', (data) => { ... }) . However, it would be very useful for me to programmatically determine if there is a user at the time of creating a provider.

// Initialize YJS
const ydoc = new Y.Doc();
const provider = new WebrtcProvider(roomName, ydoc, {
  password: process.env.YJS_PASSWORD,
  signaling: [process.env.YJS_WSS_ENDPOINT],
})

// Create the shared type to use in teh monaco editor instance
const monacoText = ydoc.getText("commonEditor");
monacoBindingRef.current = new MonacoBinding(monacoText, editorRef.current.getModel(), new Set([editorRef.current]), provider.awareness)

// This is what I need to do
if (!provider.someMethodThatReturnsConnectedPeersList?.length) {
  monacoText.insert(0, initialContent);
}

Recently, I’ve been reading about this solution, which seems great. However, in my case, it’s not applicable because the content edited in the Monaco Editor (a JSON Schema) is used as-is by other services that depend on it.

Hello again :wave:

I bring a workaround that may also be helpful to others in need of this feature. Since the 'peers' event is triggered very quickly if there are peers connected when creating a provider with a specific room, I decided to simply halt the execution flow until that check is completed. This allows me to know whether there are connected peers in that room or not, and depending on that, execute the required logic.

const peers = useRef({
  checked: false,
  value: [],
});

provider.on('peers', (data) => {
  peers.current = {
    checked: true,
    value: data.webrtcPeers,
  }
})

// Wait until the time of triggering the 'peers' event has elapsed
await new Promise((res, _) => {
  setTimeout(() => {
    peers.current.checked = true;
    res(true);
  }, 500)
})

// This is what I need to do
// Insert content only if the current user is the only one editing.
if (peers.current.checked && !peers.current.value.length) {
  monacoText.insert(0, initialContent);
}

However, as I mentioned, it’s a workaround, and I suspect that the time it takes for the ‘peers’ event to be triggered may not always be the same. Therefore, the ideal solution, I believe, would be a programmatic way to determine if there are connected peers in a room.

Greetings.

I believe the connected peers are on the Awareness object, provider.awareness. So maybe provider.awareness.getStates() (untested).

Keep in mind that clients can always be offline, or connecting at the same time, so it’s entirely possible that two clients will insert initialContent. See a larger discussion of setting initial content here: Initial offline value of a shared document - #25 by joshuafontany.

Hello again @raine, I greatly appreciate the prompt response.

Actually, that was the first post I’ve been reading since yesterday, exploring different solutions presented.
The main advice offered by @dmonad is not to insert anything until the provider has been synchronized, and here I assume that in the meantime, you put the editor in a loading state. The problem with that in my case is that this event usually takes between 6 and 10 seconds if another client connects (which I assume is considered offline until synchronized with another peer). This time delay can be somewhat counterproductive for the UX, which is why I tried to go the route of using the ‘peers’ event, as it tends to be much faster.

On the other hand, regarding the synchronization time, the ‘synced’ event might never occur, so it’s not entirely reliable for this need unless approached similarly to waiting at least the average time it takes for the event to be triggered before executing the initial content insertion logic.

Regarding the approach of provider.awareness.getStates(), at the initial moment of creating the provider, it only returns the state of the current client by the time of the ‘synced’ event.

I’m not sure if there’s a way to have some kind of reference, something like metadata that all clients share, so that at the moment of inserting the initial content by one of them, if there are other already connected clients, they can determine whether a specific update is related to initial content and, in that case, discard or cancel that update. I would like to know what you think about this.

Again, thank you very much :slightly_smiling_face:

Ah, I see. I’m used to y-websocket where the synced event fires as soon as the client is connected to the server, regardless of other clients. That delay isn’t too bad. I haven’t used y-webrtc, so my comment may not have been as relevant your case.

You’re clearly not the only person who has run into this. I’ll keep my eyes out for a proper solution.

So I guess that answers your original question: Technically there are 0 connected peers as the provider is being created.

Unhelpful :see_no_evil:

Kevin has generally advocated for the user who creates the document to be responsible for initializing the document. However, the problem still remains with global, singleton Docs.

It’s not possible to discard or cancel an update in YJS. If you do, the client will halt and wait for the missing update. So you have to avoid creating the update to begin with if it’s not valid.

I had a silly idea of wrapping the YText in a Y.Map that is keyed by guid. Then duplicate initializations won’t conflict and you can merge them. But that is surely a poor solution.

Good luck :saluting_face:

1 Like

@bravoy93 I added a reusable solution for creating initial content here. It might work better in your case than waiting for the provider to sync. Just make sure to heed the dire warnings.