diff --git a/src/components/config/Keyboard.tsx b/src/components/config/Keyboard.tsx new file mode 100644 index 00000000..3dfcbba4 --- /dev/null +++ b/src/components/config/Keyboard.tsx @@ -0,0 +1,93 @@ +import React, { useState, useImperativeHandle, forwardRef } from "react"; +import { LocalParticipant } from "livekit-client"; + +interface KeyboardProps { + localParticipant: LocalParticipant; + className?: string; +} + +interface KeyConfig { + label: string; + intKey: number; + strKey: string; +} + +const keyConfigs: KeyConfig[] = [ + { label: "1", intKey: 1, strKey: "1" }, + { label: "2", intKey: 2, strKey: "2" }, + { label: "3", intKey: 3, strKey: "3" }, + { label: "4", intKey: 4, strKey: "4" }, + { label: "5", intKey: 5, strKey: "5" }, + { label: "6", intKey: 6, strKey: "6" }, + { label: "7", intKey: 7, strKey: "7" }, + { label: "8", intKey: 8, strKey: "8" }, + { label: "9", intKey: 9, strKey: "9" }, + { label: "*", intKey: 10, strKey: "*" }, + { label: "0", intKey: 0, strKey: "0" }, + { label: "#", intKey: 11, strKey: "#" }, +]; + +export interface KeyboardRef { + setPressedSequence: (key: string[]) => void; +} + +export const Keyboard = forwardRef( + ({ localParticipant, className = "" }, ref) => { + const [pressedKey, setPressedKey] = useState(null); + const [pressedSequence, setPressedSequence] = useState([]); + + useImperativeHandle(ref, () => ({ + setPressedSequence, + })); + + const handleKeyPress = async (keyConfig: KeyConfig) => { + setPressedKey(keyConfig.label); + setPressedSequence((seq) => [...seq, keyConfig.label]); + console.log("Publishing DTMF:", keyConfig.label); + + try { + await localParticipant.publishDtmf(keyConfig.intKey, keyConfig.strKey); + } catch (error) { + console.error("Failed to publish DTMF:", error); + } finally { + setTimeout(() => { + setPressedKey(null); + }, 150); + } + }; + + return ( +
+
+
+ {pressedSequence.length > 0 ? pressedSequence.join(" ") : ""} +
+
+ {keyConfigs.map((keyConfig) => ( + + ))} +
+
+
+ ); + } +); + +Keyboard.displayName = "Keyboard"; diff --git a/src/components/playground/Playground.tsx b/src/components/playground/Playground.tsx index 77d3cc2f..97553f92 100644 --- a/src/components/playground/Playground.tsx +++ b/src/components/playground/Playground.tsx @@ -5,6 +5,7 @@ import { ChatMessageType } from "@/components/chat/ChatTile"; import { ColorPicker } from "@/components/colorPicker/ColorPicker"; import { AudioInputTile } from "@/components/config/AudioInputTile"; import { ConfigurationPanelItem } from "@/components/config/ConfigurationPanelItem"; +import { Keyboard, KeyboardRef } from "@/components/config/Keyboard"; import { NameValueRow } from "@/components/config/NameValueRow"; import { PlaygroundHeader } from "@/components/playground/PlaygroundHeader"; import { @@ -28,7 +29,7 @@ import { } from "@livekit/components-react"; import { ConnectionState, LocalParticipant, Track } from "livekit-client"; import { QRCodeSVG } from "qrcode.react"; -import { ReactNode, useCallback, useEffect, useMemo, useState } from "react"; +import React, { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react"; import tailwindTheme from "../../lib/tailwindTheme.preval"; import { EditableNameValueRow } from "@/components/config/NameValueRow"; import { AttributesInspector } from "@/components/config/AttributesInspector"; @@ -67,6 +68,8 @@ export default function Playground({ const [rpcPayload, setRpcPayload] = useState(""); const [showRpc, setShowRpc] = useState(false); + const keyboardRef = useRef(null); + useEffect(() => { if (roomState === ConnectionState.Connected) { localParticipant.setCameraEnabled(config.settings.inputs.camera); @@ -255,6 +258,16 @@ export default function Playground({ )} + {localParticipant && ( + + + + )} +
{ + if (roomState !== ConnectionState.Disconnected) { + keyboardRef.current?.setPressedSequence([]); + } + onConnect(roomState === ConnectionState.Disconnected); + }; + return ( <> - onConnect(roomState === ConnectionState.Disconnected) - } + onConnectClicked={handleConnectClick} />