feat(federation): client voice store handles federated call data (FED-009)
This commit is contained in:
@@ -383,7 +383,7 @@ export type ServerEvent =
|
|||||||
| { type: 'channel_ack'; channelId: string; messageId: string }
|
| { type: 'channel_ack'; channelId: string; messageId: string }
|
||||||
| { type: 'friend_request_received'; request: FriendRequest }
|
| { type: 'friend_request_received'; request: FriendRequest }
|
||||||
| { type: 'friend_request_accepted'; friend: Friend; requestId: string }
|
| { type: 'friend_request_accepted'; friend: Friend; requestId: string }
|
||||||
| { type: 'dm_call_incoming'; dmChannelId: string; callerId: string; callerName: string }
|
| { type: 'dm_call_incoming'; dmChannelId: string; callerId: string; callerName: string; livekitUrl?: string; livekitToken?: string }
|
||||||
| { type: 'dm_call_accepted'; dmChannelId: string }
|
| { type: 'dm_call_accepted'; dmChannelId: string }
|
||||||
| { type: 'dm_call_rejected'; dmChannelId: string }
|
| { type: 'dm_call_rejected'; dmChannelId: string }
|
||||||
| { type: 'dm_call_ended'; dmChannelId: string }
|
| { type: 'dm_call_ended'; dmChannelId: string }
|
||||||
|
|||||||
@@ -312,14 +312,18 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
|
|
||||||
// Restore DM call state from server (all origins — federated DMs live on remote instances)
|
// Restore DM call state from server (all origins — federated DMs live on remote instances)
|
||||||
{
|
{
|
||||||
const { activeDmCall, setActiveDmCall, setIncomingCall, incomingCall, connectFn, disconnectFn } = useVoiceStore.getState();
|
const { activeDmCall, setActiveDmCall, setIncomingCall, incomingCall, connectFn, disconnectFn, setFederatedCallData } = useVoiceStore.getState();
|
||||||
const myId = event.user.id;
|
const myId = event.user.id;
|
||||||
if (event.activeCalls && event.activeCalls.length > 0) {
|
if (event.activeCalls && event.activeCalls.length > 0) {
|
||||||
for (const call of event.activeCalls) {
|
for (const call of event.activeCalls) {
|
||||||
const isParticipant = call.participants.includes(myId);
|
// For federated calls, check membership via token presence (participants may be empty)
|
||||||
|
const isParticipant = call.participants.includes(myId) || !!call.livekitToken;
|
||||||
if (call.state === 'active' && isParticipant) {
|
if (call.state === 'active' && isParticipant) {
|
||||||
setActiveDmCall({ dmChannelId: call.dmChannelId });
|
setActiveDmCall({ dmChannelId: call.dmChannelId });
|
||||||
// Re-establish LiveKit connection for DM call if needed (WS reconnect)
|
// Store federated call data if present (server already filtered to this user's token)
|
||||||
|
if (call.livekitUrl && call.livekitToken) {
|
||||||
|
setFederatedCallData(call.livekitToken, call.livekitUrl);
|
||||||
|
}
|
||||||
if (connectFn) {
|
if (connectFn) {
|
||||||
connectFn(call.dmChannelId, true).catch((err) => {
|
connectFn(call.dmChannelId, true).catch((err) => {
|
||||||
console.error('[WS] DM call reconnect failed:', err);
|
console.error('[WS] DM call reconnect failed:', err);
|
||||||
@@ -334,6 +338,10 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
callerId: call.callerId,
|
callerId: call.callerId,
|
||||||
callerName: callerUser?.displayName || callerUser?.username || call.callerId,
|
callerName: callerUser?.displayName || callerUser?.username || call.callerId,
|
||||||
});
|
});
|
||||||
|
// Store federated call data for ringing calls too
|
||||||
|
if (call.livekitUrl && call.livekitToken) {
|
||||||
|
setFederatedCallData(call.livekitToken, call.livekitUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -745,22 +753,26 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
// ─── DM call events (all origins) ──────────────────────────────────────
|
// ─── DM call events (all origins) ──────────────────────────────────────
|
||||||
|
|
||||||
case 'dm_call_incoming': {
|
case 'dm_call_incoming': {
|
||||||
const { setIncomingCall } = useVoiceStore.getState();
|
const { setIncomingCall, setFederatedCallData } = useVoiceStore.getState();
|
||||||
setIncomingCall({
|
setIncomingCall({
|
||||||
dmChannelId: event.dmChannelId,
|
dmChannelId: event.dmChannelId,
|
||||||
callerId: event.callerId,
|
callerId: event.callerId,
|
||||||
callerName: event.callerName,
|
callerName: event.callerName,
|
||||||
});
|
});
|
||||||
|
// Store federated call data if present (remote LiveKit URL + token)
|
||||||
|
if (event.livekitUrl && event.livekitToken) {
|
||||||
|
setFederatedCallData(event.livekitToken, event.livekitUrl);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'dm_call_accepted': {
|
case 'dm_call_accepted': {
|
||||||
const { setIncomingCall, setOutgoingCall, setActiveDmCall, connectFn } = useVoiceStore.getState();
|
const { setIncomingCall, setOutgoingCall, setActiveDmCall, connectFn, isLiveKitConnected } = useVoiceStore.getState();
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
setOutgoingCall(null);
|
setOutgoingCall(null);
|
||||||
setActiveDmCall({ dmChannelId: event.dmChannelId });
|
setActiveDmCall({ dmChannelId: event.dmChannelId });
|
||||||
// Initiate LiveKit connection for the DM call
|
// Only connect if not already connected (federated acceptor connects immediately in handleAccept)
|
||||||
if (connectFn) {
|
if (connectFn && !isLiveKitConnected) {
|
||||||
connectFn(event.dmChannelId, true).catch((err) => {
|
connectFn(event.dmChannelId, true).catch((err) => {
|
||||||
console.error('[WS] DM call connect failed:', err);
|
console.error('[WS] DM call connect failed:', err);
|
||||||
});
|
});
|
||||||
@@ -769,19 +781,21 @@ function handleEvent(origin: string, event: ServerEvent): void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'dm_call_rejected': {
|
case 'dm_call_rejected': {
|
||||||
const { setIncomingCall, setOutgoingCall, setActiveDmCall, disconnectFn } = useVoiceStore.getState();
|
const { setIncomingCall, setOutgoingCall, setActiveDmCall, disconnectFn, clearFederatedCallData } = useVoiceStore.getState();
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
setOutgoingCall(null);
|
setOutgoingCall(null);
|
||||||
setActiveDmCall(null);
|
setActiveDmCall(null);
|
||||||
|
clearFederatedCallData();
|
||||||
if (disconnectFn) disconnectFn();
|
if (disconnectFn) disconnectFn();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case 'dm_call_ended': {
|
case 'dm_call_ended': {
|
||||||
const { setIncomingCall, setOutgoingCall, setActiveDmCall, disconnectFn } = useVoiceStore.getState();
|
const { setIncomingCall, setOutgoingCall, setActiveDmCall, disconnectFn, clearFederatedCallData } = useVoiceStore.getState();
|
||||||
setIncomingCall(null);
|
setIncomingCall(null);
|
||||||
setOutgoingCall(null);
|
setOutgoingCall(null);
|
||||||
setActiveDmCall(null);
|
setActiveDmCall(null);
|
||||||
|
clearFederatedCallData();
|
||||||
if (disconnectFn) disconnectFn();
|
if (disconnectFn) disconnectFn();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,10 @@ interface VoiceState {
|
|||||||
setIncomingCall: (call: { dmChannelId: string; callerId: string; callerName: string } | null) => void;
|
setIncomingCall: (call: { dmChannelId: string; callerId: string; callerName: string } | null) => void;
|
||||||
setOutgoingCall: (call: { dmChannelId: string } | null) => void;
|
setOutgoingCall: (call: { dmChannelId: string } | null) => void;
|
||||||
setActiveDmCall: (call: { dmChannelId: string } | null) => void;
|
setActiveDmCall: (call: { dmChannelId: string } | null) => void;
|
||||||
|
federatedCallToken: string | null;
|
||||||
|
federatedCallUrl: string | null;
|
||||||
|
setFederatedCallData: (token: string, url: string) => void;
|
||||||
|
clearFederatedCallData: () => void;
|
||||||
setVoiceUsers: (channelId: string, userIds: string[]) => void;
|
setVoiceUsers: (channelId: string, userIds: string[]) => void;
|
||||||
addVoiceUser: (channelId: string, userId: string) => void;
|
addVoiceUser: (channelId: string, userId: string) => void;
|
||||||
removeVoiceUser: (channelId: string, userId: string) => void;
|
removeVoiceUser: (channelId: string, userId: string) => void;
|
||||||
@@ -240,10 +244,14 @@ export const useVoiceStore = create<VoiceState>()(
|
|||||||
incomingCall: null,
|
incomingCall: null,
|
||||||
outgoingCall: null,
|
outgoingCall: null,
|
||||||
activeDmCall: null,
|
activeDmCall: null,
|
||||||
|
federatedCallToken: null,
|
||||||
|
federatedCallUrl: null,
|
||||||
|
|
||||||
setIncomingCall: (call) => set({ incomingCall: call }),
|
setIncomingCall: (call) => set({ incomingCall: call }),
|
||||||
setOutgoingCall: (call) => set({ outgoingCall: call }),
|
setOutgoingCall: (call) => set({ outgoingCall: call }),
|
||||||
setActiveDmCall: (call) => set({ activeDmCall: call }),
|
setActiveDmCall: (call) => set({ activeDmCall: call }),
|
||||||
|
setFederatedCallData: (token, url) => set({ federatedCallToken: token, federatedCallUrl: url }),
|
||||||
|
clearFederatedCallData: () => set({ federatedCallToken: null, federatedCallUrl: null }),
|
||||||
|
|
||||||
setVoiceUsers: (channelId, userIds) => {
|
setVoiceUsers: (channelId, userIds) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
@@ -416,6 +424,8 @@ export const useVoiceStore = create<VoiceState>()(
|
|||||||
incomingCall: null,
|
incomingCall: null,
|
||||||
outgoingCall: null,
|
outgoingCall: null,
|
||||||
activeDmCall: null,
|
activeDmCall: null,
|
||||||
|
federatedCallToken: null,
|
||||||
|
federatedCallUrl: null,
|
||||||
// Per-session media state
|
// Per-session media state
|
||||||
isCameraOn: false,
|
isCameraOn: false,
|
||||||
isScreenSharing: false,
|
isScreenSharing: false,
|
||||||
@@ -539,6 +549,8 @@ export const useVoiceStore = create<VoiceState>()(
|
|||||||
incomingCall: null,
|
incomingCall: null,
|
||||||
outgoingCall: null,
|
outgoingCall: null,
|
||||||
activeDmCall: null,
|
activeDmCall: null,
|
||||||
|
federatedCallToken: null,
|
||||||
|
federatedCallUrl: null,
|
||||||
deafenedUserIds: new Set(),
|
deafenedUserIds: new Set(),
|
||||||
voiceUserStates: new Map(),
|
voiceUserStates: new Map(),
|
||||||
streamVolumes: new Map(),
|
streamVolumes: new Map(),
|
||||||
|
|||||||
Reference in New Issue
Block a user