Repair connectivity correctly v1
This commit is contained in:
@@ -195,7 +195,8 @@ export class RoomStateSyncEffects {
|
||||
UsersActions.userLeft({
|
||||
userId: signalingMessage.oderId,
|
||||
serverId: signalingMessage.serverId,
|
||||
serverIds: remainingServerIds
|
||||
serverIds: remainingServerIds,
|
||||
connectedPeerIds: this.webrtc.getConnectedPeers()
|
||||
})
|
||||
];
|
||||
|
||||
|
||||
@@ -300,6 +300,98 @@ describe('users reducer - status', () => {
|
||||
expect(state.entities['u3']?.cameraState?.isEnabled).toBe(false);
|
||||
expect(state.entities['u3']?.screenShareState?.isSharing).toBe(false);
|
||||
});
|
||||
|
||||
it('preserves voice state on user_left while live peer transport still exists', () => {
|
||||
const remoteUser = createUser({
|
||||
id: 'u4',
|
||||
oderId: 'u4',
|
||||
displayName: 'Transient Peer',
|
||||
presenceServerIds: ['s1'],
|
||||
status: 'online',
|
||||
voiceState: {
|
||||
isConnected: true,
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
isSpeaking: true,
|
||||
roomId: 'voice-1',
|
||||
serverId: 's1'
|
||||
},
|
||||
cameraState: { isEnabled: true }
|
||||
});
|
||||
const withUser = usersReducer(baseState, UsersActions.userJoined({ user: remoteUser }));
|
||||
const state = usersReducer(withUser, UsersActions.userLeft({
|
||||
userId: 'u4',
|
||||
serverId: 's1',
|
||||
connectedPeerIds: ['u4']
|
||||
}));
|
||||
|
||||
expect(state.entities['u4']?.presenceServerIds).toEqual(['s1']);
|
||||
expect(state.entities['u4']?.isOnline).toBe(true);
|
||||
expect(state.entities['u4']?.status).toBe('online');
|
||||
expect(state.entities['u4']?.voiceState?.isConnected).toBe(true);
|
||||
expect(state.entities['u4']?.voiceState?.roomId).toBe('voice-1');
|
||||
expect(state.entities['u4']?.cameraState?.isEnabled).toBe(true);
|
||||
});
|
||||
|
||||
it('matches live user_left transports by oderId and peerId', () => {
|
||||
const remoteUser = createUser({
|
||||
id: 'db-id-5',
|
||||
oderId: 'oder-5',
|
||||
peerId: 'peer-5',
|
||||
displayName: 'Peer Id Match',
|
||||
presenceServerIds: ['s1'],
|
||||
voiceState: {
|
||||
isConnected: true,
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
isSpeaking: false,
|
||||
roomId: 'voice-1',
|
||||
serverId: 's1'
|
||||
}
|
||||
});
|
||||
const withUser = usersReducer(baseState, UsersActions.userJoined({ user: remoteUser }));
|
||||
const byOderId = usersReducer(withUser, UsersActions.userLeft({
|
||||
userId: 'db-id-5',
|
||||
serverId: 's1',
|
||||
connectedPeerIds: ['oder-5']
|
||||
}));
|
||||
const byPeerId = usersReducer(withUser, UsersActions.userLeft({
|
||||
userId: 'db-id-5',
|
||||
serverId: 's1',
|
||||
connectedPeerIds: ['peer-5']
|
||||
}));
|
||||
|
||||
expect(byOderId.entities['db-id-5']?.voiceState?.isConnected).toBe(true);
|
||||
expect(byPeerId.entities['db-id-5']?.voiceState?.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
it('clears voice state on user_left when the peer transport is gone', () => {
|
||||
const remoteUser = createUser({
|
||||
id: 'u6',
|
||||
oderId: 'u6',
|
||||
displayName: 'Gone Peer',
|
||||
presenceServerIds: ['s1'],
|
||||
voiceState: {
|
||||
isConnected: true,
|
||||
isMuted: false,
|
||||
isDeafened: false,
|
||||
isSpeaking: true,
|
||||
roomId: 'voice-1',
|
||||
serverId: 's1'
|
||||
}
|
||||
});
|
||||
const withUser = usersReducer(baseState, UsersActions.userJoined({ user: remoteUser }));
|
||||
const state = usersReducer(withUser, UsersActions.userLeft({
|
||||
userId: 'u6',
|
||||
serverId: 's1',
|
||||
connectedPeerIds: []
|
||||
}));
|
||||
|
||||
expect(state.entities['u6']?.presenceServerIds).toBeUndefined();
|
||||
expect(state.entities['u6']?.isOnline).toBe(false);
|
||||
expect(state.entities['u6']?.voiceState?.isConnected).toBe(false);
|
||||
expect(state.entities['u6']?.voiceState?.roomId).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('manual status overrides auto idle', () => {
|
||||
|
||||
@@ -32,7 +32,7 @@ export const UsersActions = createActionGroup({
|
||||
'Load Room Users Failure': props<{ error: string }>(),
|
||||
|
||||
'User Joined': props<{ user: User }>(),
|
||||
'User Left': props<{ userId: string; serverId?: string; serverIds?: string[] }>(),
|
||||
'User Left': props<{ userId: string; serverId?: string; serverIds?: string[]; connectedPeerIds?: string[] }>(),
|
||||
'Sync Server Presence': props<{ roomId: string; users: User[]; connectedPeerIds?: string[] }>(),
|
||||
|
||||
'Update User': props<{ userId: string; updates: Partial<User> }>(),
|
||||
|
||||
@@ -227,7 +227,8 @@ function buildAvatarUser(existingUser: User | undefined, incomingUser: {
|
||||
|
||||
function buildPresenceRemovalChanges(
|
||||
user: User,
|
||||
update: { serverId?: string; serverIds?: readonly string[] }
|
||||
update: { serverId?: string; serverIds?: readonly string[] },
|
||||
connectedPeerIds: ReadonlySet<string> = new Set<string>()
|
||||
): Partial<User> {
|
||||
const nextPresenceServerIds = update.serverIds !== undefined
|
||||
? normalizePresenceServerIds(update.serverIds)
|
||||
@@ -235,6 +236,24 @@ function buildPresenceRemovalChanges(
|
||||
const isOnline = (nextPresenceServerIds?.length ?? 0) > 0;
|
||||
const shouldClearLiveState = !isOnline
|
||||
|| (!!user.voiceState?.serverId && !nextPresenceServerIds?.includes(user.voiceState.serverId));
|
||||
const hasLiveState = user.voiceState?.isConnected === true
|
||||
|| user.screenShareState?.isSharing === true
|
||||
|| user.cameraState?.isEnabled === true;
|
||||
|
||||
if (shouldClearLiveState && hasLiveState && hasLivePeerTransport(user, connectedPeerIds)) {
|
||||
const preservedPresenceServerIds = user.presenceServerIds
|
||||
?? (user.voiceState?.serverId ? [user.voiceState.serverId] : undefined);
|
||||
|
||||
return {
|
||||
presenceServerIds: preservedPresenceServerIds,
|
||||
isOnline: true,
|
||||
status: user.status && user.status !== 'offline' && user.status !== 'disconnected' ? user.status : 'online',
|
||||
voiceState: user.voiceState,
|
||||
screenShareState: user.screenShareState,
|
||||
cameraState: user.cameraState,
|
||||
gameActivity: user.gameActivity
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
presenceServerIds: nextPresenceServerIds,
|
||||
@@ -390,7 +409,7 @@ export const usersReducer = createReducer(
|
||||
? usersAdapter.updateMany(stalePresenceUpdates, nextState)
|
||||
: nextState;
|
||||
}),
|
||||
on(UsersActions.userLeft, (state, { userId, serverId, serverIds }) => {
|
||||
on(UsersActions.userLeft, (state, { userId, serverId, serverIds, connectedPeerIds }) => {
|
||||
const existingUser = state.entities[userId];
|
||||
|
||||
if (!existingUser) {
|
||||
@@ -409,7 +428,7 @@ export const usersReducer = createReducer(
|
||||
changes: buildPresenceRemovalChanges(existingUser, {
|
||||
serverId,
|
||||
serverIds
|
||||
})
|
||||
}, new Set(connectedPeerIds ?? []))
|
||||
},
|
||||
state
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user