fixed participantState

This commit is contained in:
J Taylor 2021-06-30 12:42:12 +01:00
parent e8fd32ad56
commit aa53966923
3 changed files with 122 additions and 104 deletions

View File

@ -8,6 +8,7 @@
* - A session id for each remote participant * - A session id for each remote participant
* - "<id>-screen" for each shared screen * - "<id>-screen" for each shared screen
*/ */
import fasteq from 'fast-deep-equal';
import { import {
DEVICE_STATE_OFF, DEVICE_STATE_OFF,
DEVICE_STATE_BLOCKED, DEVICE_STATE_BLOCKED,
@ -15,8 +16,9 @@ import {
} from './useDevices'; } from './useDevices';
const initialParticipantsState = { const initialParticipantsState = {
participants: { lastPendingUnknownActiveSpeaker: null,
local: { participants: [
{
camMutedByHost: false, camMutedByHost: false,
hasNameSet: false, hasNameSet: false,
id: 'local', id: 'local',
@ -31,9 +33,9 @@ const initialParticipantsState = {
lastActiveDate: null, lastActiveDate: null,
micMutedByHost: false, micMutedByHost: false,
name: '', name: '',
position: 1,
}, },
}, ],
screens: [],
}; };
// --- Derived data --- // --- Derived data ---
@ -63,36 +65,7 @@ function getMaxPosition(participants) {
); );
} }
function getUpdatedParticipant(participant, participants) { function getNewParticipant(participant) {
const id = getId(participant);
const prevItem = participants[id];
const { local } = participant;
const { audio, video } = participant.tracks;
return {
...prevItem,
camMutedByHost: video?.off?.byRemoteRequest,
hasNameSet: !!participant.user_name,
id,
isCamMuted:
video?.state === DEVICE_STATE_OFF ||
video?.state === DEVICE_STATE_BLOCKED,
isLoading:
audio?.state === DEVICE_STATE_LOADING ||
video?.state === DEVICE_STATE_LOADING,
isLocal: local,
isMicMuted:
audio?.state === DEVICE_STATE_OFF ||
audio?.state === DEVICE_STATE_BLOCKED,
isOwner: !!participant.owner,
isRecording: !!participant.record,
micMutedByHost: audio?.off?.byRemoteRequest,
name: participant.user_name,
};
}
function getNewParticipant(participant, participants) {
const id = getId(participant); const id = getId(participant);
const { local } = participant; const { local } = participant;
@ -119,11 +92,41 @@ function getNewParticipant(participant, participants) {
lastActiveDate: null, lastActiveDate: null,
micMutedByHost: audio?.off?.byRemoteRequest, micMutedByHost: audio?.off?.byRemoteRequest,
name: participant.user_name, name: participant.user_name,
position: local ? 0 : getMaxPosition(participants) + 1,
}; };
} }
function getScreenItem(participant, participants) { function getUpdatedParticipant(participant, participants) {
const id = getId(participant);
const prevItem = participants.find((p) => p.id === id);
// In case we haven't set up this participant, yet.
if (!prevItem) return getNewParticipant(participant);
const { local } = participant;
const { audio, video } = participant.tracks;
return {
...prevItem,
camMutedByHost: video?.off?.byRemoteRequest,
hasNameSet: !!participant.user_name,
id,
isCamMuted:
video?.state === DEVICE_STATE_OFF ||
video?.state === DEVICE_STATE_BLOCKED,
isLoading:
audio?.state === DEVICE_STATE_LOADING ||
video?.state === DEVICE_STATE_LOADING,
isLocal: local,
isMicMuted:
audio?.state === DEVICE_STATE_OFF ||
audio?.state === DEVICE_STATE_BLOCKED,
isOwner: !!participant.owner,
isRecording: !!participant.record,
micMutedByHost: audio?.off?.byRemoteRequest,
name: participant.user_name,
};
}
function getScreenItem(participant) {
const id = getId(participant); const id = getId(participant);
return { return {
hasNameSet: null, hasNameSet: null,
@ -133,7 +136,6 @@ function getScreenItem(participant, participants) {
isScreenshare: true, isScreenshare: true,
lastActiveDate: null, lastActiveDate: null,
name: participant.user_name, name: participant.user_name,
position: getMaxPosition(participants) + 1,
}; };
} }
@ -151,49 +153,66 @@ function participantsReducer(prevState, action) {
switch (action.type) { switch (action.type) {
case ACTIVE_SPEAKER: { case ACTIVE_SPEAKER: {
const { participants, ...state } = prevState; const { participants, ...state } = prevState;
if (!action.id) return prevState; if (!action.id)
return {
...prevState,
lastPendingUnknownActiveSpeaker: null,
};
const date = new Date();
const isParticipantKnown = participants.some((p) => p.id === action.id);
return { return {
...state, ...state,
participants: Object.keys(participants).reduce( lastPendingUnknownActiveSpeaker: isParticipantKnown
(items, id) => ({ ? null
...items, : {
[id]: { date,
...participants[id], id: action.id,
isActiveSpeaker: id === action.id,
lastActiveDate:
id === action.id
? new Date()
: participants[id]?.lastActiveDate,
}, },
}), participants: participants.map((p) => ({
{} ...p,
), isActiveSpeaker: p.id === action.id,
lastActiveDate: p.id === action.id ? date : p?.lastActiveDate,
})),
}; };
} }
case PARTICIPANT_JOINED: { case PARTICIPANT_JOINED: {
const item = getNewParticipant( const item = getNewParticipant(action.participant);
action.participant,
prevState.participants
);
const { id } = item;
const screenId = getScreenId(id);
const newParticipants = { const participants = [...prevState.participants];
...prevState.participants, const screens = [...prevState.screens];
[id]: item,
}; const isPendingActiveSpeaker =
item.id === prevState.lastPendingUnknownActiveSpeaker?.id;
if (isPendingActiveSpeaker) {
item.isActiveSpeaker = true;
item.lastActiveDate = prevState.lastPendingUnknownActiveSpeaker?.date;
}
if (item.isCamMuted) {
participants.push(item);
} else {
const firstInactiveCamOffIndex = prevState.participants.findIndex(
(p) => p.isCamMuted && !p.isLocal && !p.isActiveSpeaker
);
if (firstInactiveCamOffIndex >= 0) {
participants.splice(firstInactiveCamOffIndex, 0, item);
} else {
participants.push(item);
}
}
// Participant is sharing screen // Participant is sharing screen
if (action.participant.screen) { if (action.participant.screen) {
newParticipants[screenId] = getScreenItem( screens.push(getScreenItem(action.participant));
action.participant,
newParticipants
);
} }
return { return {
...prevState, ...prevState,
participants: newParticipants, lastPendingUnknownActiveSpeaker: isPendingActiveSpeaker
? null
: prevState.lastPendingUnknownActiveSpeaker,
participants,
screens,
}; };
} }
case PARTICIPANT_UPDATED: { case PARTICIPANT_UPDATED: {
@ -204,60 +223,58 @@ function participantsReducer(prevState, action) {
const { id } = item; const { id } = item;
const screenId = getScreenId(id); const screenId = getScreenId(id);
const newParticipants = { const participants = [...prevState.participants];
...prevState.participants, const idx = participants.findIndex((p) => p.id === id);
}; participants[idx] = item;
newParticipants[id] = item;
const screens = [...prevState.screens];
const screenIdx = screens.findIndex((s) => s.id === screenId);
if (action.participant.screen) { if (action.participant.screen) {
newParticipants[screenId] = getScreenItem( const screenItem = getScreenItem(action.participant);
action.participant, if (screenIdx >= 0) {
newParticipants screens[screenIdx] = screenItem;
); } else {
} else { screens.push(screenItem);
delete newParticipants[screenId]; }
} else if (screenIdx >= 0) {
screens.splice(screenIdx, 1);
} }
return { const newState = {
...prevState, ...prevState,
participants: newParticipants, participants,
screens,
}; };
if (fasteq(newState, prevState)) {
return prevState;
}
return newState;
} }
case PARTICIPANT_LEFT: { case PARTICIPANT_LEFT: {
const id = getId(action.participant); const id = getId(action.participant);
const screenId = getScreenId(id); const screenId = getScreenId(id);
const { ...participants } = prevState.participants;
delete participants[id];
delete participants[screenId];
return { return {
...prevState, ...prevState,
participants, participants: [...prevState.participants].filter((p) => p.id !== id),
screens: [...prevState.screens].filter((s) => s.id !== screenId),
}; };
} }
case SWAP_POSITION: { case SWAP_POSITION: {
const { participants, ...state } = prevState; const participants = [...prevState.participants];
if (!action.id1 || !action.id2) return prevState; if (!action.id1 || !action.id2) return prevState;
const pos1 = participants[action.id1]?.position; const idx1 = participants.findIndex((p) => p.id === action.id1);
const pos2 = participants[action.id2]?.position; const idx2 = participants.findIndex((p) => p.id === action.id2);
if (!pos1 || !pos2) return prevState; if (idx1 === -1 || idx2 === -1) return prevState;
const tmp = participants[idx1];
participants[idx1] = participants[idx2];
participants[idx2] = tmp;
return { return {
...state, ...prevState,
participants: Object.keys(participants).reduce((items, id) => { participants,
let { position } = participants[id];
if (action.id1 === id) {
position = pos2;
}
if (action.id2 === id) {
position = pos1;
}
return {
...items,
[id]: {
...participants[id],
position,
},
};
}, {}),
}; };
} }
default: default:

View File

@ -7,6 +7,7 @@
"@daily-co/daily-js": "^0.12.0", "@daily-co/daily-js": "^0.12.0",
"classnames": "^2.3.1", "classnames": "^2.3.1",
"debounce": "^1.2.1", "debounce": "^1.2.1",
"fast-deep-equal": "^3.1.3",
"nanoid": "^3.1.23", "nanoid": "^3.1.23",
"no-scroll": "^2.1.1", "no-scroll": "^2.1.1",
"prop-types": "^15.7.2", "prop-types": "^15.7.2",

View File

@ -1532,7 +1532,7 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
md5.js "^1.3.4" md5.js "^1.3.4"
safe-buffer "^5.1.1" safe-buffer "^5.1.1"
fast-deep-equal@^3.1.1: fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3" version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==