Spaces:
Sleeping
Sleeping
Jon Taylor
commited on
Commit
•
35f4179
1
Parent(s):
24d8b3c
call state implemented
Browse files- frontend/app/components/App.js +90 -0
- frontend/app/components/Call.js +10 -67
- frontend/app/components/Controls.js +24 -4
- frontend/app/components/TrayButton.js +2 -1
- frontend/app/page.js +2 -5
- frontend/app/utils.js +0 -14
frontend/app/components/App.js
ADDED
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"use client";
|
2 |
+
|
3 |
+
import {
|
4 |
+
DailyVideo,
|
5 |
+
useAppMessage,
|
6 |
+
useLocalSessionId,
|
7 |
+
useParticipantIds,
|
8 |
+
} from "@daily-co/daily-react";
|
9 |
+
import { ActionIcon, LoadingOverlay } from "@mantine/core";
|
10 |
+
import {
|
11 |
+
IconDoorExit,
|
12 |
+
IconInfoSquareRoundedFilled,
|
13 |
+
IconLayoutSidebarRightCollapse,
|
14 |
+
IconLayoutSidebarRightExpand,
|
15 |
+
} from "@tabler/icons-react";
|
16 |
+
import { useCallback, useState } from "react";
|
17 |
+
import Card from "./Card";
|
18 |
+
import Controls from "./Controls";
|
19 |
+
import { HUDItem } from "./HUDItem";
|
20 |
+
import { TrayButton } from "./TrayButton";
|
21 |
+
|
22 |
+
export default function App({ onLeave }) {
|
23 |
+
const [panelHidden, setPanelHidden] = useState(false);
|
24 |
+
const localSessionId = useLocalSessionId();
|
25 |
+
const participantIds = useParticipantIds({ filter: "remote" });
|
26 |
+
const [params, setParams] = useState();
|
27 |
+
const sendAppMessage = useAppMessage({
|
28 |
+
onAppMessage: useCallback((ev) => setParams(ev.data), []),
|
29 |
+
});
|
30 |
+
|
31 |
+
return (
|
32 |
+
<div className="flex flex-col min-h-screen">
|
33 |
+
<div
|
34 |
+
className={`flex-1 grid ${
|
35 |
+
panelHidden ? "grid-cols-[0px_1fr]" : "grid-cols-[460px_1fr]"
|
36 |
+
}`}
|
37 |
+
>
|
38 |
+
<aside className="overflow-y-auto max-h-screen border-r border-zinc-200 bg-white">
|
39 |
+
<Controls
|
40 |
+
remoteParams={params}
|
41 |
+
onData={(data) => sendAppMessage(data, "*")}
|
42 |
+
/>
|
43 |
+
</aside>
|
44 |
+
<div className="max-h-screen bg-zinc-100 flex items-center justify-center relative">
|
45 |
+
<header className="flex flex-row justify-between px-3 absolute top-3 w-full">
|
46 |
+
<ActionIcon
|
47 |
+
size="lg"
|
48 |
+
className="rounded-md bg-zinc-200 hover:bg-indigo-500 hover:text-indigo-50 text-zinc-500 transition-colors duration-300"
|
49 |
+
onClick={() => setPanelHidden(!panelHidden)}
|
50 |
+
>
|
51 |
+
{panelHidden ? (
|
52 |
+
<IconLayoutSidebarRightCollapse size={20} stroke={2} />
|
53 |
+
) : (
|
54 |
+
<IconLayoutSidebarRightExpand size={20} stroke={2} />
|
55 |
+
)}
|
56 |
+
</ActionIcon>
|
57 |
+
<div className="flex flex-row gap-2">
|
58 |
+
<HUDItem label="Expiry" value="3:00" />
|
59 |
+
<HUDItem label="FPS" value="0" />
|
60 |
+
</div>
|
61 |
+
</header>
|
62 |
+
|
63 |
+
<main className="w-full h-full flex flex-col max-w-lg xl:max-w-2xl 2xl:max-w-full 2xl:flex-row items-center justify-center p-6 gap-3">
|
64 |
+
<Card>
|
65 |
+
<DailyVideo automirror sessionId={localSessionId} />
|
66 |
+
</Card>
|
67 |
+
<Card>
|
68 |
+
{participantIds.length ? (
|
69 |
+
<DailyVideo sessionId={participantIds[0]} />
|
70 |
+
) : (
|
71 |
+
<LoadingOverlay
|
72 |
+
visible={true}
|
73 |
+
zIndex={1000}
|
74 |
+
className="bg-zinc-300"
|
75 |
+
/>
|
76 |
+
)}
|
77 |
+
</Card>
|
78 |
+
</main>
|
79 |
+
|
80 |
+
<footer className="absolute flex flex-row gap-2 bottom-3 right-3">
|
81 |
+
<TrayButton Icon={IconInfoSquareRoundedFilled}>About</TrayButton>
|
82 |
+
<TrayButton red Icon={IconDoorExit} onClick={() => onLeave()}>
|
83 |
+
Leave
|
84 |
+
</TrayButton>
|
85 |
+
</footer>
|
86 |
+
</div>
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
);
|
90 |
+
}
|
frontend/app/components/Call.js
CHANGED
@@ -1,17 +1,11 @@
|
|
1 |
"use client";
|
2 |
|
3 |
-
import {
|
4 |
-
DailyVideo,
|
5 |
-
useAppMessage,
|
6 |
-
useLocalSessionId,
|
7 |
-
useParticipantIds,
|
8 |
-
} from "@daily-co/daily-react";
|
9 |
import { useCallback, useEffect, useState } from "react";
|
10 |
-
import Avatar from "../components/Avatar";
|
11 |
-
import Card from "../components/Card";
|
12 |
import CreateRoom from "../components/CreateRoom";
|
13 |
import Join from "../components/Joining";
|
14 |
import { apiUrl } from "../utils";
|
|
|
15 |
|
16 |
const STATE_IDLE = "idle";
|
17 |
const STATE_JOINING = "joining";
|
@@ -21,16 +15,14 @@ const BOT_STATE_STARTING = "bot_starting";
|
|
21 |
const BOT_STATE_STARTED = "bot_started";
|
22 |
|
23 |
export default function Call() {
|
|
|
24 |
const [callState, setCallState] = useState(STATE_IDLE);
|
25 |
const [roomUrl, setRoomUrl] = useState();
|
26 |
const [botState, setBotState] = useState(BOT_STATE_STARTING);
|
27 |
-
const [params, setParams] = useState({});
|
28 |
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
onAppMessage: useCallback((ev) => setParams(ev.data), []),
|
33 |
-
});
|
34 |
|
35 |
const start = useCallback(async () => {
|
36 |
if (!process.env.NEXT_PUBLIC_DISABLE_LOCAL_AGENT) return;
|
@@ -70,58 +62,9 @@ export default function Call() {
|
|
70 |
return <Join roomUrl={roomUrl} onJoin={() => setCallState(STATE_JOINED)} />;
|
71 |
}
|
72 |
|
|
|
|
|
|
|
73 |
// Main call loop
|
74 |
-
return (
|
75 |
-
<main className="container py-12">
|
76 |
-
<div className="grid grid-cols-2 grid-flow-col gap-12">
|
77 |
-
<div>
|
78 |
-
<Card headerText="Local Webcam">
|
79 |
-
<div className="overflow-hidden bg-gray-50 sm:rounded-lg">
|
80 |
-
<div className="aspect-video flex items-center justify-center">
|
81 |
-
<DailyVideo automirror sessionId={localSessionId} />
|
82 |
-
</div>
|
83 |
-
</div>
|
84 |
-
</Card>
|
85 |
-
<div className="relative">
|
86 |
-
<div>
|
87 |
-
<label
|
88 |
-
htmlFor="comment"
|
89 |
-
className="block text-sm font-medium leading-6 text-gray-900"
|
90 |
-
>
|
91 |
-
Add your comment
|
92 |
-
</label>
|
93 |
-
<div className="mt-2">
|
94 |
-
<textarea
|
95 |
-
rows={4}
|
96 |
-
name="comment"
|
97 |
-
id="comment"
|
98 |
-
className="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
|
99 |
-
defaultValue={params.prompt || ""}
|
100 |
-
/>
|
101 |
-
</div>
|
102 |
-
</div>
|
103 |
-
Config - Resolution, Mbps, FPS
|
104 |
-
<button
|
105 |
-
onClick={() => sendAppMessage({ prompt: "Big ol' car" }, "*")}
|
106 |
-
>
|
107 |
-
Send test app message
|
108 |
-
</button>
|
109 |
-
</div>
|
110 |
-
</div>
|
111 |
-
<div>
|
112 |
-
<Card headerText="Inference">
|
113 |
-
<div className="overflow-hidden bg-gray-50 sm:rounded-lg">
|
114 |
-
<div className="aspect-video flex items-center justify-center">
|
115 |
-
{participantIds.length ? (
|
116 |
-
<DailyVideo sessionId={participantIds[0]} />
|
117 |
-
) : (
|
118 |
-
<Avatar />
|
119 |
-
)}
|
120 |
-
</div>
|
121 |
-
</div>
|
122 |
-
</Card>
|
123 |
-
</div>
|
124 |
-
</div>
|
125 |
-
</main>
|
126 |
-
);
|
127 |
}
|
|
|
1 |
"use client";
|
2 |
|
3 |
+
import { useDaily } from "@daily-co/daily-react";
|
|
|
|
|
|
|
|
|
|
|
4 |
import { useCallback, useEffect, useState } from "react";
|
|
|
|
|
5 |
import CreateRoom from "../components/CreateRoom";
|
6 |
import Join from "../components/Joining";
|
7 |
import { apiUrl } from "../utils";
|
8 |
+
import App from "./App";
|
9 |
|
10 |
const STATE_IDLE = "idle";
|
11 |
const STATE_JOINING = "joining";
|
|
|
15 |
const BOT_STATE_STARTED = "bot_started";
|
16 |
|
17 |
export default function Call() {
|
18 |
+
const daily = useDaily();
|
19 |
const [callState, setCallState] = useState(STATE_IDLE);
|
20 |
const [roomUrl, setRoomUrl] = useState();
|
21 |
const [botState, setBotState] = useState(BOT_STATE_STARTING);
|
|
|
22 |
|
23 |
+
function leave() {
|
24 |
+
setCallState(STATE_LEFT), daily.leave();
|
25 |
+
}
|
|
|
|
|
26 |
|
27 |
const start = useCallback(async () => {
|
28 |
if (!process.env.NEXT_PUBLIC_DISABLE_LOCAL_AGENT) return;
|
|
|
62 |
return <Join roomUrl={roomUrl} onJoin={() => setCallState(STATE_JOINED)} />;
|
63 |
}
|
64 |
|
65 |
+
if (callState === STATE_LEFT) {
|
66 |
+
return <div>Left</div>;
|
67 |
+
}
|
68 |
// Main call loop
|
69 |
+
return <App onLeave={() => leave()} />;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
frontend/app/components/Controls.js
CHANGED
@@ -3,6 +3,7 @@
|
|
3 |
import {
|
4 |
Accordion,
|
5 |
ActionIcon,
|
|
|
6 |
SegmentedControl,
|
7 |
Textarea,
|
8 |
} from "@mantine/core";
|
@@ -21,7 +22,7 @@ import SelectInput from "./SelectInput";
|
|
21 |
import SlideInput from "./SliderInput";
|
22 |
import TextInput from "./TextInput";
|
23 |
|
24 |
-
export const Controls = React.memo(() => {
|
25 |
const form = useForm({
|
26 |
initialValues: {
|
27 |
positivePrompt: "some kind of prompt",
|
@@ -35,17 +36,36 @@ export const Controls = React.memo(() => {
|
|
35 |
|
36 |
const debounce = useDebounce();
|
37 |
|
38 |
-
const handleChange = (
|
39 |
-
|
40 |
};
|
41 |
|
|
|
|
|
|
|
|
|
42 |
useEffect(() => {
|
43 |
if (!form) return;
|
44 |
form.isDirty() && debounce(() => handleChange(form.values), 500);
|
45 |
}, [form, debounce]);
|
46 |
|
47 |
return (
|
48 |
-
<div className="flex flex-col gap-4">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
<Accordion
|
50 |
defaultValue="prompt"
|
51 |
classNames={{
|
|
|
3 |
import {
|
4 |
Accordion,
|
5 |
ActionIcon,
|
6 |
+
LoadingOverlay,
|
7 |
SegmentedControl,
|
8 |
Textarea,
|
9 |
} from "@mantine/core";
|
|
|
22 |
import SlideInput from "./SliderInput";
|
23 |
import TextInput from "./TextInput";
|
24 |
|
25 |
+
export const Controls = React.memo(({ remoteParams, onData }) => {
|
26 |
const form = useForm({
|
27 |
initialValues: {
|
28 |
positivePrompt: "some kind of prompt",
|
|
|
36 |
|
37 |
const debounce = useDebounce();
|
38 |
|
39 |
+
const handleChange = (v) => {
|
40 |
+
onData(v);
|
41 |
};
|
42 |
|
43 |
+
useEffect(() => {
|
44 |
+
if (remoteParams) return;
|
45 |
+
}, [remoteParams]);
|
46 |
+
|
47 |
useEffect(() => {
|
48 |
if (!form) return;
|
49 |
form.isDirty() && debounce(() => handleChange(form.values), 500);
|
50 |
}, [form, debounce]);
|
51 |
|
52 |
return (
|
53 |
+
<div className="relative flex flex-col gap-4">
|
54 |
+
{!remoteParams && (
|
55 |
+
<LoadingOverlay
|
56 |
+
visible={true}
|
57 |
+
zIndex={1000}
|
58 |
+
overlayProps={{ blur: 2 }}
|
59 |
+
loaderProps={{
|
60 |
+
children: (
|
61 |
+
<div className="animate-pulse font-medium">
|
62 |
+
Waiting for bot to join...
|
63 |
+
</div>
|
64 |
+
),
|
65 |
+
}}
|
66 |
+
className="h-screen"
|
67 |
+
/>
|
68 |
+
)}
|
69 |
<Accordion
|
70 |
defaultValue="prompt"
|
71 |
classNames={{
|
frontend/app/components/TrayButton.js
CHANGED
@@ -8,7 +8,7 @@ const RED_COLORS =
|
|
8 |
"bg-rose-700 text-rose-100 hover:text-rose-50 hover:bg-rose-600";
|
9 |
const RED_ICON_COLORS = "text-rose-300 group-hover:text-rose-200";
|
10 |
|
11 |
-
export const TrayButton = ({ Icon, red = false, children }) => (
|
12 |
<Button
|
13 |
size="md"
|
14 |
radius="xl"
|
@@ -24,6 +24,7 @@ export const TrayButton = ({ Icon, red = false, children }) => (
|
|
24 |
} transition-colors duration-300`}
|
25 |
/>
|
26 |
}
|
|
|
27 |
>
|
28 |
{children}
|
29 |
</Button>
|
|
|
8 |
"bg-rose-700 text-rose-100 hover:text-rose-50 hover:bg-rose-600";
|
9 |
const RED_ICON_COLORS = "text-rose-300 group-hover:text-rose-200";
|
10 |
|
11 |
+
export const TrayButton = ({ Icon, red = false, children, ...other }) => (
|
12 |
<Button
|
13 |
size="md"
|
14 |
radius="xl"
|
|
|
24 |
} transition-colors duration-300`}
|
25 |
/>
|
26 |
}
|
27 |
+
{...other}
|
28 |
>
|
29 |
{children}
|
30 |
</Button>
|
frontend/app/page.js
CHANGED
@@ -4,6 +4,7 @@ import Daily from "@daily-co/daily-js";
|
|
4 |
import { DailyProvider } from "@daily-co/daily-react";
|
5 |
|
6 |
import { useEffect, useState } from "react";
|
|
|
7 |
import Call from "./components/Call";
|
8 |
|
9 |
export default function Home() {
|
@@ -17,11 +18,7 @@ export default function Home() {
|
|
17 |
|
18 |
return (
|
19 |
<DailyProvider callObject={daily}>
|
20 |
-
<
|
21 |
-
<div className="self-center w-full">
|
22 |
-
<Call />
|
23 |
-
</div>
|
24 |
-
</div>
|
25 |
</DailyProvider>
|
26 |
);
|
27 |
}
|
|
|
4 |
import { DailyProvider } from "@daily-co/daily-react";
|
5 |
|
6 |
import { useEffect, useState } from "react";
|
7 |
+
|
8 |
import Call from "./components/Call";
|
9 |
|
10 |
export default function Home() {
|
|
|
18 |
|
19 |
return (
|
20 |
<DailyProvider callObject={daily}>
|
21 |
+
<Call />
|
|
|
|
|
|
|
|
|
22 |
</DailyProvider>
|
23 |
);
|
24 |
}
|
frontend/app/utils.js
CHANGED
@@ -5,17 +5,3 @@ export const apiUrl =
|
|
5 |
`${window.location.protocol === "https:" ? "https" : "http"}://${
|
6 |
window.location.host
|
7 |
}`;
|
8 |
-
|
9 |
-
export function debounce(func, wait) {
|
10 |
-
let timeout;
|
11 |
-
|
12 |
-
return function executedFunction(...args) {
|
13 |
-
const later = () => {
|
14 |
-
clearTimeout(timeout);
|
15 |
-
func(...args);
|
16 |
-
};
|
17 |
-
|
18 |
-
clearTimeout(timeout);
|
19 |
-
timeout = setTimeout(later, wait);
|
20 |
-
};
|
21 |
-
}
|
|
|
5 |
`${window.location.protocol === "https:" ? "https" : "http"}://${
|
6 |
window.location.host
|
7 |
}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|