fixes #117
This commit is contained in:
@@ -25,6 +25,7 @@ import { useSounds } from "_components/Audio/useSounds";
|
||||
export const Audio = () => {
|
||||
const {
|
||||
speakingParticipants,
|
||||
resetSpeakingParticipants,
|
||||
isTalking,
|
||||
toggleTalking,
|
||||
transmitBlocked,
|
||||
@@ -104,7 +105,7 @@ export const Audio = () => {
|
||||
data-tip="Nachricht entfernen"
|
||||
>
|
||||
<button
|
||||
className={cn("btn btn-sm btn-ghost border-warning bg-transparent ")}
|
||||
className={cn("btn btn-sm btn-ghost border-warning bg-transparent")}
|
||||
onClick={() => {
|
||||
removeMessage();
|
||||
}}
|
||||
@@ -123,9 +124,9 @@ export const Audio = () => {
|
||||
>
|
||||
<button
|
||||
className={cn(
|
||||
"btn btn-sm btn-soft bg-transparent border",
|
||||
"btn btn-sm btn-soft border bg-transparent",
|
||||
canStopOtherSpeakers && speakingParticipants.length > 0 && "hover:bg-error",
|
||||
speakingParticipants.length > 0 && " hover:bg-errorborder",
|
||||
speakingParticipants.length > 0 && "hover:bg-errorborder",
|
||||
isReceivingBlick && "border-warning",
|
||||
)}
|
||||
onClick={() => {
|
||||
@@ -133,6 +134,7 @@ export const Audio = () => {
|
||||
const payload = JSON.stringify({
|
||||
by: role,
|
||||
});
|
||||
resetSpeakingParticipants("dich");
|
||||
speakingParticipants.forEach(async (p) => {
|
||||
await room?.localParticipant.performRpc({
|
||||
destinationIdentity: p.identity,
|
||||
@@ -159,24 +161,24 @@ export const Audio = () => {
|
||||
transmitBlocked && "bg-yellow-500 hover:bg-yellow-500",
|
||||
state === "disconnected" && "bg-red-500 hover:bg-red-500",
|
||||
state === "error" && "bg-red-500 hover:bg-red-500",
|
||||
state === "connecting" && "bg-yellow-500 hover:bg-yellow-500 cursor-default",
|
||||
state === "connecting" && "cursor-default bg-yellow-500 hover:bg-yellow-500",
|
||||
)}
|
||||
>
|
||||
{state === "connected" && <Mic className="w-5 h-5" />}
|
||||
{state === "disconnected" && <WifiOff className="w-5 h-5" />}
|
||||
{state === "connecting" && <PlugZap className="w-5 h-5" />}
|
||||
{state === "error" && <ServerCrash className="w-5 h-5" />}
|
||||
{state === "connected" && <Mic className="h-5 w-5" />}
|
||||
{state === "disconnected" && <WifiOff className="h-5 w-5" />}
|
||||
{state === "connecting" && <PlugZap className="h-5 w-5" />}
|
||||
{state === "error" && <ServerCrash className="h-5 w-5" />}
|
||||
</button>
|
||||
|
||||
{state === "connected" && (
|
||||
<details className="dropdown relative z-[1050]">
|
||||
<summary className="dropdown btn btn-ghost flex items-center gap-1">
|
||||
{connectionQuality === ConnectionQuality.Excellent && <Signal className="w-5 h-5" />}
|
||||
{connectionQuality === ConnectionQuality.Good && <SignalMedium className="w-5 h-5" />}
|
||||
{connectionQuality === ConnectionQuality.Poor && <SignalLow className="w-5 h-5" />}
|
||||
{connectionQuality === ConnectionQuality.Lost && <ZapOff className="w-5 h-5" />}
|
||||
{connectionQuality === ConnectionQuality.Excellent && <Signal className="h-5 w-5" />}
|
||||
{connectionQuality === ConnectionQuality.Good && <SignalMedium className="h-5 w-5" />}
|
||||
{connectionQuality === ConnectionQuality.Poor && <SignalLow className="h-5 w-5" />}
|
||||
{connectionQuality === ConnectionQuality.Lost && <ZapOff className="h-5 w-5" />}
|
||||
{connectionQuality === ConnectionQuality.Unknown && (
|
||||
<ShieldQuestion className="w-5 h-5" />
|
||||
<ShieldQuestion className="h-5 w-5" />
|
||||
)}
|
||||
<div className="badge badge-sm badge-soft badge-success">{remoteParticipants}</div>
|
||||
</summary>
|
||||
@@ -184,7 +186,7 @@ export const Audio = () => {
|
||||
{ROOMS.map((r) => (
|
||||
<li key={r}>
|
||||
<button
|
||||
className="btn btn-sm btn-ghost text-left flex items-center justify-start gap-2 relative"
|
||||
className="btn btn-sm btn-ghost relative flex items-center justify-start gap-2 text-left"
|
||||
onClick={() => {
|
||||
if (!role) return;
|
||||
if (selectedRoom === r) return;
|
||||
@@ -193,7 +195,7 @@ export const Audio = () => {
|
||||
}}
|
||||
>
|
||||
{room?.name === r && (
|
||||
<Disc className="text-success text-sm absolute left-2" width={15} />
|
||||
<Disc className="text-success absolute left-2 text-sm" width={15} />
|
||||
)}
|
||||
<span className="flex-1 text-center">{r}</span>
|
||||
</button>
|
||||
@@ -201,12 +203,12 @@ export const Audio = () => {
|
||||
))}
|
||||
<li>
|
||||
<button
|
||||
className="btn btn-sm btn-ghost text-left flex items-center justify-start gap-2 relative"
|
||||
className="btn btn-sm btn-ghost relative flex items-center justify-start gap-2 text-left"
|
||||
onClick={() => {
|
||||
disconnect();
|
||||
}}
|
||||
>
|
||||
<WifiOff className="text-error text-sm absolute left-2" width={15} />
|
||||
<WifiOff className="text-error absolute left-2 text-sm" width={15} />
|
||||
<span className="flex-1 text-center">Disconnect</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
@@ -25,28 +25,29 @@ import { usePilotConnectionStore } from "_store/pilot/connectionStore";
|
||||
let interval: NodeJS.Timeout;
|
||||
|
||||
type TalkState = {
|
||||
addSpeakingParticipant: (participant: Participant) => void;
|
||||
connect: (roomName: string, role: string) => void;
|
||||
connectionQuality: ConnectionQuality;
|
||||
disconnect: () => void;
|
||||
isTalking: boolean;
|
||||
localRadioTrack: LocalTrackPublication | undefined;
|
||||
message: string | null;
|
||||
removeMessage: () => void;
|
||||
removeSpeakingParticipant: (speakingParticipants: Participant) => void;
|
||||
remoteParticipants: number;
|
||||
resetSpeakingParticipants: (source: string) => void;
|
||||
room: Room | null;
|
||||
setSettings: (settings: Partial<TalkState["settings"]>) => void;
|
||||
settings: {
|
||||
micDeviceId: string | null;
|
||||
micVolume: number;
|
||||
radioVolume: number;
|
||||
dmeVolume: number;
|
||||
};
|
||||
isTalking: boolean;
|
||||
transmitBlocked: boolean;
|
||||
removeMessage: () => void;
|
||||
state: "connecting" | "connected" | "disconnected" | "error";
|
||||
message: string | null;
|
||||
connectionQuality: ConnectionQuality;
|
||||
remoteParticipants: number;
|
||||
toggleTalking: () => void;
|
||||
setSettings: (settings: Partial<TalkState["settings"]>) => void;
|
||||
connect: (roomName: string, role: string) => void;
|
||||
disconnect: () => void;
|
||||
speakingParticipants: Participant[];
|
||||
addSpeakingParticipant: (participant: Participant) => void;
|
||||
removeSpeakingParticipant: (speakingParticipants: Participant) => void;
|
||||
room: Room | null;
|
||||
localRadioTrack: LocalTrackPublication | undefined;
|
||||
state: "connecting" | "connected" | "disconnected" | "error";
|
||||
toggleTalking: () => void;
|
||||
transmitBlocked: boolean;
|
||||
};
|
||||
const getToken = async (roomName: string) => {
|
||||
const response = await axios.get(`/api/livekit-token?roomName=${roomName}`);
|
||||
@@ -71,6 +72,15 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
||||
remoteParticipants: 0,
|
||||
connectionQuality: ConnectionQuality.Unknown,
|
||||
room: null,
|
||||
resetSpeakingParticipants: (source: string) => {
|
||||
set({
|
||||
speakingParticipants: [],
|
||||
isTalking: false,
|
||||
transmitBlocked: false,
|
||||
message: `Ruf beendet durch ${source || "eine unsichtbare Macht"}`,
|
||||
});
|
||||
get().room?.localParticipant.setMicrophoneEnabled(false);
|
||||
},
|
||||
addSpeakingParticipant: (participant) => {
|
||||
set((state) => {
|
||||
if (!state.speakingParticipants.some((p) => p.identity === participant.identity)) {
|
||||
@@ -201,10 +211,15 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
||||
|
||||
set({ localRadioTrack: publishedTrack });
|
||||
|
||||
set({ state: "connected", room, message: null });
|
||||
set({ state: "connected", room, isTalking: false, message: null });
|
||||
})
|
||||
.on(RoomEvent.Disconnected, () => {
|
||||
set({ state: "disconnected", speakingParticipants: [], transmitBlocked: false });
|
||||
set({
|
||||
state: "disconnected",
|
||||
speakingParticipants: [],
|
||||
transmitBlocked: false,
|
||||
isTalking: false,
|
||||
});
|
||||
|
||||
handleDisconnect();
|
||||
})
|
||||
@@ -223,17 +238,22 @@ export const useAudioStore = create<TalkState>((set, get) => ({
|
||||
|
||||
room.registerRpcMethod("force-mute", async (data: RpcInvocationData) => {
|
||||
const { by } = JSON.parse(data.payload);
|
||||
room.localParticipant.setMicrophoneEnabled(false);
|
||||
useAudioStore.setState({
|
||||
isTalking: false,
|
||||
message: `Ruf beendet durch ${by || "eine unsichtbare Macht"}`,
|
||||
});
|
||||
return `Hello, ${data.callerIdentity}!`;
|
||||
get().resetSpeakingParticipants(by);
|
||||
return "OK";
|
||||
});
|
||||
|
||||
interval = setInterval(() => {
|
||||
// Filter forgotten participants
|
||||
const oldSpeakingParticipants = get().speakingParticipants;
|
||||
const speakingParticipants = oldSpeakingParticipants.filter((oP) => {
|
||||
return Array.from(room.remoteParticipants.values()).find(
|
||||
(p) => p.identity === oP.identity,
|
||||
);
|
||||
});
|
||||
|
||||
set({
|
||||
remoteParticipants: room.numParticipants === 0 ? 0 : room.numParticipants - 1, // Unreliable and delayed
|
||||
speakingParticipants,
|
||||
});
|
||||
}, 500);
|
||||
} catch (error: Error | unknown) {
|
||||
|
||||
Reference in New Issue
Block a user