166 lines
5.2 KiB
TypeScript
166 lines
5.2 KiB
TypeScript
"use client";
|
|
|
|
import {useEffect, useMemo, useState} from "react";
|
|
import {fetchConfig} from "@/app/utils/fetchConfig";
|
|
import BusControlTabs from "@/app/components/BusControlTabs";
|
|
import MainMix from "@/app/components/MainMix";
|
|
import Settings from "@/app/components/Settings";
|
|
import {Config} from "@/app/types/config";
|
|
|
|
// initialize global websocket connection
|
|
import {fetchValue, initConnection, isConnected} from "@/app/lib/x32CommunicationManager";
|
|
|
|
|
|
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
|
|
return new Promise<T>((resolve, reject) => {
|
|
const timeout = setTimeout(() => {
|
|
reject(new Error("Request timed out"));
|
|
}, timeoutMs);
|
|
|
|
promise
|
|
.then((result) => {
|
|
clearTimeout(timeout);
|
|
resolve(result);
|
|
})
|
|
.catch((error) => {
|
|
clearTimeout(timeout);
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
const Page = () => {
|
|
const [appConfig, setAppConfig] = useState(null);
|
|
const [activeTab, setActiveTab] = useState("mainMix");
|
|
|
|
const [webSocketConnected, setWebSocketConnected] = useState(false);
|
|
const [mixerConnected, setMixerConnected] = useState(false);
|
|
|
|
|
|
useEffect(() => {
|
|
fetchConfig().then((config: Config) => {
|
|
setAppConfig(config);
|
|
initConnection(`ws://${window.location.hostname}:${config.webserver.port}`);
|
|
});
|
|
}, []);
|
|
|
|
// periodically check if the websocket connection is still alive
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
// wait for appConfig to be fetched
|
|
if (!appConfig) {
|
|
console.log("AppConfig not fetched yet");
|
|
return;
|
|
}
|
|
setWebSocketConnected(isConnected);
|
|
if (!isConnected) {
|
|
console.log("Reconnecting to WebSocket");
|
|
initConnection(`ws://${window.location.hostname}:${appConfig.webserver.port}`);
|
|
} else {
|
|
// check if mixer is connected
|
|
withTimeout(fetchValue("/info"), 1500)
|
|
.then((response) => {
|
|
// response is a string starting with "V", which is the API version, if the mixer is connected
|
|
if (response.startsWith("V")) {
|
|
setMixerConnected(true);
|
|
} else {
|
|
setMixerConnected(false);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.log("Mixer connection check failed:", error.message);
|
|
setMixerConnected(false);
|
|
});
|
|
|
|
}
|
|
}, 2000);
|
|
return () => clearInterval(interval);
|
|
}, [appConfig]);
|
|
|
|
|
|
const tabComponents = useMemo(() => {
|
|
return [
|
|
{
|
|
id: "mainMix",
|
|
label: "Main Mix",
|
|
content: (
|
|
<MainMix/>
|
|
),
|
|
},
|
|
{
|
|
id: "busControl",
|
|
label: "Bus Control",
|
|
content: (
|
|
<BusControlTabs/>
|
|
),
|
|
},
|
|
{
|
|
id: "recording",
|
|
label: "Recording",
|
|
content: (
|
|
<div>
|
|
<h2>Recording</h2>
|
|
<p>Coming soon...</p>
|
|
</div>
|
|
),
|
|
},
|
|
{
|
|
id: "settings",
|
|
label: "Settings",
|
|
content: (
|
|
<Settings/>
|
|
),
|
|
}
|
|
]
|
|
}
|
|
, []);
|
|
|
|
if (!appConfig) {
|
|
return <p>Error fetching config</p>;
|
|
}
|
|
|
|
if (!webSocketConnected) {
|
|
return <p>WebSocket not connected</p>;
|
|
}
|
|
|
|
if (!mixerConnected) {
|
|
return <p>Mixer not connected</p>;
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<div className="p-4">
|
|
{/* Tabs Header */}
|
|
<div className="flex border-b mb-4">
|
|
{tabComponents.map((tab) => (
|
|
<button
|
|
key={tab.id}
|
|
className={`px-4 py-2 ${
|
|
activeTab === tab.id
|
|
? "border-b-2 border-blue-500 text-blue-500"
|
|
: "text-gray-500"
|
|
}`}
|
|
onClick={() => setActiveTab(tab.id)}
|
|
>
|
|
{tab.label}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
{/* Tabs Content */}
|
|
<div>
|
|
{tabComponents.map((tab) => (
|
|
<div
|
|
key={tab.id}
|
|
style={{display: activeTab === tab.id ? "block" : "none"}}
|
|
>
|
|
{tab.content}
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Page; |