Merge pull request '- added OSD configuration components and hooks for managing overlay settings' (#15) from feature/osdOverlayOptions into develop
Reviewed-on: #15
This commit is contained in:
27
src/features/output/components/OSDFieldToggle.tsx
Normal file
27
src/features/output/components/OSDFieldToggle.tsx
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Field } from "formik";
|
||||
|
||||
type OSDFieldToggleProps = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const OSDFieldToggle = ({ value, label }: OSDFieldToggleProps) => {
|
||||
const spacesWords = (label: string) => {
|
||||
if (label.includes("VRM")) return label.replace("VRM", " VRM");
|
||||
return label.replace(/([A-Z])/g, " $1").trim();
|
||||
};
|
||||
|
||||
return (
|
||||
<label className="flex items-center gap-3 cursor-pointer select-none w-full justify-between">
|
||||
<span className="text-lg">{spacesWords(label)}</span>
|
||||
<Field id={value} type="checkbox" name={value} className="sr-only peer" />
|
||||
<div
|
||||
className="relative w-10 h-5 rounded-full bg-gray-300 transition peer-checked:bg-blue-500 after:content-['']
|
||||
after:absolute after:top-0.5 after:left-0.5 after:w-4 after:h-4 after:rounded-full after:bg-white after:shadow after:transition
|
||||
after:duration-300 peer-checked:after:translate-x-5"
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
};
|
||||
|
||||
export default OSDFieldToggle;
|
||||
70
src/features/output/components/OSDFields.tsx
Normal file
70
src/features/output/components/OSDFields.tsx
Normal file
@@ -0,0 +1,70 @@
|
||||
import { Field, useFormikContext } from "formik";
|
||||
import { useOSDConfig } from "../hooks/useOSDConfig";
|
||||
import OSDFieldToggle from "./OSDFieldToggle";
|
||||
import type { OSDConfigFields } from "../../../types/types";
|
||||
|
||||
type OSDFieldsProps = {
|
||||
isOSDLoading: boolean;
|
||||
};
|
||||
|
||||
const OSDFields = ({ isOSDLoading }: OSDFieldsProps) => {
|
||||
const { osdMutation } = useOSDConfig();
|
||||
const { values } = useFormikContext<OSDConfigFields>();
|
||||
|
||||
const includeKeys = Object.keys(values as OSDConfigFields).filter((value) => value.includes("include"));
|
||||
|
||||
const handleSubmit = async (values: OSDConfigFields) => {
|
||||
const result = await osdMutation.mutateAsync(values);
|
||||
console.log(result);
|
||||
};
|
||||
|
||||
if (isOSDLoading) {
|
||||
return <div>Loading OSD Options...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex flex-col space-y-4">
|
||||
<div className="p-4 border border-gray-600 rounded-lg flex flex-col space-y-4">
|
||||
<h2 className="text-2xl mb-4">OSD Options</h2>
|
||||
<div className="flex flex-col space-y-4">
|
||||
{includeKeys.map((key) => (
|
||||
<OSDFieldToggle key={key} value={key} label={key.replace("include", "Include ")} />
|
||||
))}
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<label htmlFor="overlayPosition">Overlay Position</label>
|
||||
<Field
|
||||
as="select"
|
||||
name="overlayPosition"
|
||||
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
||||
>
|
||||
<option value="Top">Top</option>
|
||||
<option value="Bottom">Bottom</option>
|
||||
</Field>
|
||||
</div>
|
||||
<div className="flex flex-row justify-between">
|
||||
<label htmlFor="OSDTimestampFormat">OSD Timestamp Format</label>
|
||||
<Field
|
||||
as="select"
|
||||
name="OSDTimestampFormat"
|
||||
className="p-2 border border-gray-400 rounded-lg text-white bg-[#253445] w-full md:w-60"
|
||||
>
|
||||
<option value="UTC">UTC</option>
|
||||
<option value="LOCAL">Local</option>
|
||||
</Field>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleSubmit(values)}
|
||||
className="w-full md:w-1/4 text-white bg-green-700 hover:bg-green-800 font-small rounded-lg text-sm px-2 py-2.5 hover:cursor-pointer"
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default OSDFields;
|
||||
18
src/features/output/components/OSDOptionsCard.tsx
Normal file
18
src/features/output/components/OSDOptionsCard.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import Card from "../../../ui/Card";
|
||||
import CardHeader from "../../../ui/CardHeader";
|
||||
import OSDFields from "./OSDFields";
|
||||
|
||||
type OSDOptionsCardProps = {
|
||||
isOSDLoading: boolean;
|
||||
};
|
||||
|
||||
const OSDOptionsCard = ({ isOSDLoading }: OSDOptionsCardProps) => {
|
||||
return (
|
||||
<Card className="p-4 flex-1">
|
||||
<CardHeader title="OSD Payload Options" />
|
||||
<OSDFields isOSDLoading={isOSDLoading} />
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default OSDOptionsCard;
|
||||
@@ -6,14 +6,25 @@ import { usePostBearerConfig } from "../hooks/useBearer";
|
||||
import { useDispatcherConfig } from "../hooks/useDispatcherConfig";
|
||||
import { useOptionalConstants } from "../hooks/useOptionalConstants";
|
||||
import { useCustomFields } from "../hooks/useCustomFields";
|
||||
import OSDOptionsCard from "./OSDOptionsCard";
|
||||
import { useOSDConfig } from "../hooks/useOSDConfig";
|
||||
|
||||
const OutputForms = () => {
|
||||
const { bearerMutation } = usePostBearerConfig();
|
||||
const { dispatcherQuery, dispatcherMutation } = useDispatcherConfig();
|
||||
const { customFieldsQuery, customFieldsMutation } = useCustomFields();
|
||||
const { osdQuery } = useOSDConfig();
|
||||
|
||||
const isLoading = dispatcherQuery?.isLoading;
|
||||
const isOSDLoading = osdQuery?.isLoading;
|
||||
|
||||
const includeVRM = osdQuery?.data?.propIncludeVRM?.value.toLowerCase() === "true";
|
||||
const includeMotion = osdQuery?.data?.propIncludeMotion?.value.toLowerCase() === "true";
|
||||
const includeTimeStamp = osdQuery?.data?.propIncludeTimestamp?.value.toLowerCase() === "true";
|
||||
const includeCameraName = osdQuery?.data?.propIncludeCameraName?.value.toLowerCase() === "true";
|
||||
const overlayPosition = osdQuery?.data?.propOverlayPosition?.value;
|
||||
const OSDTimestampFormat = osdQuery?.data?.propTimestampFormat?.value;
|
||||
console.log(includeVRM);
|
||||
const format = dispatcherQuery?.data?.propFormat?.value;
|
||||
const { optionalConstantsQuery, optionalConstantsMutation } = useOptionalConstants(format?.toLowerCase());
|
||||
const FFID = optionalConstantsQuery?.data?.propFeedIdentifier?.value;
|
||||
@@ -70,6 +81,14 @@ const OutputForms = () => {
|
||||
|
||||
//custom fields
|
||||
customFields: initialCustomFields,
|
||||
|
||||
// OSD Options
|
||||
includeVRM: includeVRM ?? false,
|
||||
includeMotion: includeMotion ?? false,
|
||||
includeTimeStamp: includeTimeStamp ?? false,
|
||||
includeCameraName: includeCameraName ?? false,
|
||||
overlayPosition: overlayPosition ?? "Top",
|
||||
OSDTimestampFormat: OSDTimestampFormat ?? "UTC",
|
||||
};
|
||||
|
||||
const handleSubmit = async (values: FormTypes) => {
|
||||
@@ -128,8 +147,11 @@ const OutputForms = () => {
|
||||
|
||||
return (
|
||||
<Formik initialValues={inititalValues} onSubmit={handleSubmit} enableReinitialize>
|
||||
<Form className="grid grid-cols-1 md:grid-cols-2">
|
||||
<Form className="grid grid-cols-1 md:grid-cols-2 gap-4 h-[50%]">
|
||||
<div>
|
||||
<BearerTypeCard />
|
||||
<OSDOptionsCard isOSDLoading={isOSDLoading} />
|
||||
</div>
|
||||
<ChannelCard />
|
||||
</Form>
|
||||
</Formik>
|
||||
|
||||
53
src/features/output/hooks/useOSDConfig.ts
Normal file
53
src/features/output/hooks/useOSDConfig.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { CAMBASE } from "../../../utils/config";
|
||||
import type { OSDConfigFields } from "../../../types/types";
|
||||
|
||||
const fetchOSDConfig = async () => {
|
||||
const response = await fetch(`${CAMBASE}/api/fetch-config?id=SightingAmmend0-overlay`);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Network response was not ok");
|
||||
}
|
||||
return response.json();
|
||||
};
|
||||
|
||||
const postOSDConfig = async (data: OSDConfigFields) => {
|
||||
const fields = [
|
||||
{ property: "propIncludeVRM", value: data.includeVRM },
|
||||
{ property: "propIncludeMotion", value: data.includeMotion },
|
||||
{ property: "propIncludeTimestamp", value: data.includeTimeStamp },
|
||||
{ property: "propIncludeCameraName", value: data.includeCameraName },
|
||||
{ property: "propOverlayPosition", value: data.overlayPosition },
|
||||
{ property: "propTimestampFormat", value: data.OSDTimestampFormat },
|
||||
];
|
||||
|
||||
const osdConfigPayload = {
|
||||
id: "SightingAmmend0-overlay",
|
||||
fields: fields,
|
||||
};
|
||||
|
||||
console.log(osdConfigPayload);
|
||||
const response = await fetch(`${CAMBASE}/api/update-config`, {
|
||||
method: "POST",
|
||||
body: JSON.stringify(osdConfigPayload),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to post OSD Config");
|
||||
}
|
||||
return response.json();
|
||||
};
|
||||
|
||||
export const useOSDConfig = () => {
|
||||
const osdQuery = useQuery({
|
||||
queryKey: ["osdConfig"],
|
||||
queryFn: fetchOSDConfig,
|
||||
});
|
||||
|
||||
const osdMutation = useMutation({
|
||||
mutationFn: postOSDConfig,
|
||||
mutationKey: ["postOSDConfig"],
|
||||
});
|
||||
|
||||
return { osdQuery, osdMutation };
|
||||
};
|
||||
@@ -73,7 +73,16 @@ export type InitialValuesFormErrors = {
|
||||
readTimeoutSeconds?: string;
|
||||
};
|
||||
|
||||
export type FormTypes = BearerTypeFields & OptionalConstants & OptionalLaneIDs & CustomFields;
|
||||
export type OSDConfigFields = {
|
||||
includeVRM: boolean;
|
||||
includeMotion: boolean;
|
||||
includeTimeStamp: boolean;
|
||||
includeCameraName: boolean;
|
||||
overlayPosition: "Top" | "Bottom" | "Left" | "Right";
|
||||
OSDTimestampFormat: "UTC" | "LOCAL";
|
||||
};
|
||||
|
||||
export type FormTypes = BearerTypeFields & OptionalConstants & OptionalLaneIDs & CustomFields & OSDConfigFields;
|
||||
type FieldProperty = {
|
||||
datatype: string;
|
||||
value: string;
|
||||
|
||||
Reference in New Issue
Block a user