From b002579c31064013edab9e4dc3d76197840e6b32 Mon Sep 17 00:00:00 2001 From: Linda Malm <109201562+malmen237@users.noreply.github.com> Date: Thu, 10 Oct 2024 10:43:41 +0200 Subject: [PATCH] Feat/restyle multiview layout (#32) * fix: feedback updates * feat: made the layout-config universal, made some file-restructure * fix: some typos and minor changes --- src/api/ateliereLive/websocket.ts | 5 -- src/api/mongoClient/defaults/preset.ts | 2 +- .../[source_name]/thumbnail/route.ts | 1 + src/app/api/manager/streams/route.ts | 2 + src/app/production/[id]/page.tsx | 7 +- .../ConfigureMultiviewModal.tsx | 29 ++++--- .../MultiviewLayout.tsx | 2 +- .../MultiviewLayoutSettings.tsx | 79 +++++++++++++------ .../MultiviewSettings.tsx | 62 ++++++--------- ...ls.tsx => useCheckProductionPipelines.tsx} | 6 +- 10 files changed, 100 insertions(+), 95 deletions(-) rename src/components/modal/{configureOutputModal => configureMultiviewModal}/MultiviewLayoutSettings/MultiviewLayout.tsx (96%) rename src/components/modal/{configureOutputModal => configureMultiviewModal}/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx (71%) rename src/components/modal/{configureOutputModal => configureMultiviewModal}/MultiviewSettings.tsx (84%) rename src/hooks/{useCheckProductionPipelinesAndControlPanels.tsx => useCheckProductionPipelines.tsx} (87%) diff --git a/src/api/ateliereLive/websocket.ts b/src/api/ateliereLive/websocket.ts index 0cbe35d8..17ac7849 100644 --- a/src/api/ateliereLive/websocket.ts +++ b/src/api/ateliereLive/websocket.ts @@ -5,11 +5,6 @@ function createWebSocket(): Promise { const ws = new WebSocket(`ws://${process.env.CONTROL_PANEL_WS}`); ws.on('error', reject); ws.on('open', () => { - // const send = ws.send.bind(ws); - // ws.send = (message) => { - // console.debug(`[websocket] sending message: ${message}`); - // send(message); - // }; resolve(ws); }); }); diff --git a/src/api/mongoClient/defaults/preset.ts b/src/api/mongoClient/defaults/preset.ts index e3022f66..4c424104 100644 --- a/src/api/mongoClient/defaults/preset.ts +++ b/src/api/mongoClient/defaults/preset.ts @@ -306,7 +306,7 @@ export const defaultMultiview = [ }, { _id: new ObjectId('65cb266c00fecda4a1faf977'), - name: '12 inputs HD', + name: '13 inputs HD', layout: { output_height: 1080, output_width: 1920, diff --git a/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts b/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts index 8d69748f..926d3e33 100644 --- a/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts +++ b/src/app/api/manager/sources/[ingest_name]/[source_name]/thumbnail/route.ts @@ -20,6 +20,7 @@ export async function GET( status: 403 }); } + try { const ingestUuid = await getUuidFromIngestName(params.ingest_name); const sourceId = await getSourceIdFromSourceName( diff --git a/src/app/api/manager/streams/route.ts b/src/app/api/manager/streams/route.ts index 06c7ff6a..689dd0f6 100644 --- a/src/app/api/manager/streams/route.ts +++ b/src/app/api/manager/streams/route.ts @@ -15,8 +15,10 @@ export async function POST(request: NextRequest): Promise { status: 403 }); } + const data = await request.json(); const createStreamRequest = data as CreateStreamRequestBody; + return await createStream( createStreamRequest.source, createStreamRequest.production, diff --git a/src/app/production/[id]/page.tsx b/src/app/production/[id]/page.tsx index 38e142f6..1bc2eeb6 100644 --- a/src/app/production/[id]/page.tsx +++ b/src/app/production/[id]/page.tsx @@ -51,7 +51,7 @@ import { useGetFirstEmptySlot } from '../../../hooks/useGetFirstEmptySlot'; import { useWebsocket } from '../../../hooks/useWebsocket'; import { ConfigureMultiviewButton } from '../../../components/modal/configureMultiviewModal/ConfigureMultiviewButton'; import { useUpdateSourceInputSlotOnMultiviewLayouts } from '../../../hooks/useUpdateSourceInputSlotOnMultiviewLayouts'; -import { useCheckProductionPipelinesAndControlPanels } from '../../../hooks/useCheckProductionPipelinesAndControlPanels'; +import { useCheckProductionPipelines } from '../../../hooks/useCheckProductionPipelines'; export default function ProductionConfiguration({ params }: PageProps) { const t = useTranslate(); @@ -109,8 +109,7 @@ export default function ProductionConfiguration({ params }: PageProps) { // Websocket const [closeWebsocket] = useWebsocket(); - const [checkProductionPipelinesAndControlPanels] = - useCheckProductionPipelinesAndControlPanels(); + const [checkProductionPipelines] = useCheckProductionPipelines(); const { locked } = useContext(GlobalContext); @@ -213,7 +212,7 @@ export default function ProductionConfiguration({ params }: PageProps) { // check if production has pipelines in use or control panels in use, if so update production const production = config.isActive ? config - : checkProductionPipelinesAndControlPanels(config, pipelines); + : checkProductionPipelines(config, pipelines); putProduction(production._id, production); setProductionSetup(production); diff --git a/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx b/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx index e83b50ed..be9c32a5 100644 --- a/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx +++ b/src/components/modal/configureMultiviewModal/ConfigureMultiviewModal.tsx @@ -7,10 +7,11 @@ import { MultiviewSettings } from '../../../interfaces/multiview'; import { IconPlus, IconTrash } from '@tabler/icons-react'; import { Production } from '../../../interfaces/production'; import deepclone from 'lodash.clonedeep'; -import MultiviewSettingsConfig from '../configureOutputModal/MultiviewSettings'; -import MultiviewLayoutSettings from '../configureOutputModal/MultiviewLayoutSettings/MultiviewLayoutSettings'; +import MultiviewSettingsConfig from './MultiviewSettings'; import { usePutMultiviewLayout } from '../../../hooks/multiviewLayout'; import Decision from '../configureOutputModal/Decision'; +import MultiviewLayoutSettings from './MultiviewLayoutSettings/MultiviewLayoutSettings'; +import { IconSettings } from '@tabler/icons-react'; type ConfigureMultiviewModalProps = { open: boolean; @@ -32,9 +33,6 @@ export function ConfigureMultiviewModal({ [] ); const [layoutModalOpen, setLayoutModalOpen] = useState(false); - const [selectedMultiviewLayout, setSelectedMultiviewLayout] = useState< - { layout: TMultiviewLayout; tableIndex: number } | undefined - >(); const [newMultiviewLayout, setNewMultiviewLayout] = useState(null); const addNewLayout = usePutMultiviewLayout(); @@ -91,11 +89,6 @@ export function ConfigureMultiviewModal({ return; } - const updatedMultiviews = multiviews.map((item, i) => - i === multiviews.length - 1 ? { ...item, ...newMultiviewLayout } : item - ); - - setMultiviews(updatedMultiviews); addNewLayout(newMultiviewLayout); setLayoutModalOpen(false); }; @@ -115,6 +108,7 @@ export function ConfigureMultiviewModal({ ports.forEach((port, index) => { if (seenPorts.has(port)) { duplicateIndices.push(index); + // Also include the first occurrence if it's not already included const firstIndex = ports.indexOf(port); if (!duplicateIndices.includes(firstIndex)) { @@ -173,13 +167,21 @@ export function ConfigureMultiviewModal({ multiviews.map((singleItem, index) => { return (
-
+ {index !== 0 && ( +
+ )} +
setLayoutModalOpen(true)} newMultiviewLayout={newMultiviewLayout} - tableIndex={index} lastItem={multiviews.length === index + 1} multiview={singleItem} handleUpdateMultiview={(input) => @@ -190,8 +192,6 @@ export function ConfigureMultiviewModal({ ? portDuplicateIndexes.includes(index) : false } - setSelectedMultiviewLayout={setSelectedMultiviewLayout} - selectedMultiviewLayout={selectedMultiviewLayout} />
)} diff --git a/src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayout.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx similarity index 96% rename from src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayout.tsx rename to src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx index 761ca294..9d345135 100644 --- a/src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayout.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayout.tsx @@ -1,6 +1,6 @@ import { TListSource } from '../../../../interfaces/multiview'; import { MultiviewPreset } from '../../../../interfaces/preset'; -import Options from '../Options'; +import Options from '../../configureOutputModal/Options'; export default function MultiviewLayout({ multiviewPresetLayout, diff --git a/src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx similarity index 71% rename from src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx rename to src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx index a903f67b..5e370148 100644 --- a/src/components/modal/configureOutputModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewLayoutSettings/MultiviewLayoutSettings.tsx @@ -1,17 +1,17 @@ import { useEffect, useState } from 'react'; import { useMultiviewPresets } from '../../../../hooks/multiviewPreset'; -import Options from '../Options'; import { MultiviewPreset } from '../../../../interfaces/preset'; import { useTranslate } from '../../../../i18n/useTranslate'; import { useSetupMultiviewLayout } from '../../../../hooks/useSetupMultiviewLayout'; import { useMultiviewLayouts } from '../../../../hooks/multiviewLayout'; import { Production } from '../../../../interfaces/production'; import { useConfigureMultiviewLayout } from '../../../../hooks/useConfigureMultiviewLayout'; -import Input from '../Input'; import { TMultiviewLayout } from '../../../../interfaces/preset'; import { useCreateInputArray } from '../../../../hooks/useCreateInputArray'; -import MultiviewLayout from './MultiviewLayout'; import { TListSource } from '../../../../interfaces/multiview'; +import Options from '../../configureOutputModal/Options'; +import Input from '../../configureOutputModal/Input'; +import MultiviewLayout from './MultiviewLayout'; type ChangeLayout = { defaultLabel?: string; @@ -21,11 +21,9 @@ type ChangeLayout = { export default function MultiviewLayoutSettings({ production, - selectedMultiviewLayout, setNewMultiviewPreset }: { production: Production | undefined; - selectedMultiviewLayout: TMultiviewLayout | undefined; setNewMultiviewPreset: (preset: TMultiviewLayout | null) => void; }) { const [selectedMultiviewPreset, setSelectedMultiviewPreset] = @@ -53,18 +51,21 @@ export default function MultiviewLayoutSettings({ ? multiviewPresets?.map((preset) => preset.name) : []; + const availableMultiviewLayouts = multiviewLayouts?.filter( + (layout) => layout.productionId === production?._id || !layout.productionId + ); + const multiviewLayoutNames = + availableMultiviewLayouts?.map((layout) => layout.name) || []; + // This useEffect is used to set the drawn layout of the multiviewer on start, // if this fails then the modal will be empty useEffect(() => { - if (selectedMultiviewLayout) { - setLayoutToChange(selectedMultiviewLayout.name); - setSelectedMultiviewPreset(selectedMultiviewLayout); - } else if (multiviewPresets && multiviewPresets[0]) { + if (multiviewPresets && multiviewPresets[0]) { setSelectedMultiviewPreset(multiviewPresets[0]); } - }, [multiviewPresets, selectedMultiviewLayout]); + }, [multiviewPresets]); - const layoutNameAlreadyExist = multiviewLayouts?.find( + const layoutNameAlreadyExist = availableMultiviewLayouts?.find( (singlePreset) => singlePreset.name === multiviewLayout?.name )?.name; @@ -78,23 +79,36 @@ export default function MultiviewLayoutSettings({ } }, [multiviewLayout]); - const handlePresetUpdate = (name: string) => { - const presetLayout = multiviewPresets?.find( + const handleLayoutUpdate = (name: string, type: string) => { + const chosenLayout = availableMultiviewLayouts?.find( + (singleLayout) => singleLayout.name === name + ); + const addBasePreset = multiviewPresets?.find( (singlePreset) => singlePreset.name === name ); setLayoutToChange(''); setNewPresetName(name); - if (presetLayout) { - setSelectedMultiviewPreset(presetLayout); + + switch (type) { + case 'layout': + if (chosenLayout) { + setSelectedMultiviewPreset(chosenLayout); + } + break; + case 'preset': + if (addBasePreset) { + setSelectedMultiviewPreset(addBasePreset); + } + break; } }; const handleChange = (viewId: string, value: string) => { - if (inputList && multiviewLayouts) { + if (inputList && availableMultiviewLayouts) { // Remove 2 from id to remove id for Preview- and Program-view // Add 1 to index to get the correct input_slot const idFirstInputView = parseInt(viewId, 10) - 2 + 1; - const defaultLabel = multiviewLayouts[0].layout.views.find( + const defaultLabel = availableMultiviewLayouts[0].layout.views.find( (item) => item.input_slot === idFirstInputView )?.label; inputList.map((source) => { @@ -112,14 +126,17 @@ export default function MultiviewLayoutSettings({ <> {selectedMultiviewPreset && (
- {multiviewPresetLayout && ( - + ({ + label: singleItem + }))} + value={ + selectedMultiviewPreset ? selectedMultiviewPreset.name : '' + } + update={(value) => handleLayoutUpdate(value, 'layout')} /> - )} -
({ @@ -128,12 +145,22 @@ export default function MultiviewLayoutSettings({ value={ selectedMultiviewPreset ? selectedMultiviewPreset.name : '' } - update={handlePresetUpdate} + update={(value) => handleLayoutUpdate(value, 'preset')} + /> +
+ + {multiviewPresetLayout && ( + + )} +
handleLayoutUpdate(value, 'layout')} placeholder={t('preset.new_preset_name')} /> {layoutNameAlreadyExist && ( diff --git a/src/components/modal/configureOutputModal/MultiviewSettings.tsx b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx similarity index 84% rename from src/components/modal/configureOutputModal/MultiviewSettings.tsx rename to src/components/modal/configureMultiviewModal/MultiviewSettings.tsx index e9e23d3a..a1fdb1cd 100644 --- a/src/components/modal/configureOutputModal/MultiviewSettings.tsx +++ b/src/components/modal/configureMultiviewModal/MultiviewSettings.tsx @@ -1,15 +1,14 @@ -import { use, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslate } from '../../../i18n/useTranslate'; import { MultiviewSettings } from '../../../interfaces/multiview'; import { TMultiviewLayout } from '../../../interfaces/preset'; -import Input from './Input'; -import Options from './Options'; +import Input from '../configureOutputModal/Input'; +import Options from '../configureOutputModal/Options'; import toast from 'react-hot-toast'; import { IconSettings } from '@tabler/icons-react'; import { useMultiviewLayouts } from '../../../hooks/multiviewLayout'; type MultiviewSettingsProps = { - tableIndex: number; lastItem: boolean; multiview?: MultiviewSettings; handleUpdateMultiview: (multiview: MultiviewSettings) => void; @@ -17,43 +16,33 @@ type MultiviewSettingsProps = { openConfigModal: () => void; newMultiviewLayout: TMultiviewLayout | null; productionId: string | undefined; - setSelectedMultiviewLayout: (props: { - layout: TMultiviewLayout; - tableIndex: number; - }) => void; - selectedMultiviewLayout: - | { layout: TMultiviewLayout; tableIndex: number } - | undefined; }; export default function MultiviewSettingsConfig({ - tableIndex, lastItem, multiview, handleUpdateMultiview, portDuplicateError, openConfigModal, newMultiviewLayout, - productionId, - setSelectedMultiviewLayout, - selectedMultiviewLayout + productionId }: MultiviewSettingsProps) { const t = useTranslate(); const [multiviewLayouts] = useMultiviewLayouts(); - const currentValue = multiview || selectedMultiviewLayout?.layout; + const [selectedMultiviewLayout, setSelectedMultiviewLayout] = useState< + TMultiviewLayout | undefined + >(); + const currentValue = multiview || selectedMultiviewLayout; const avaliableMultiviewLayouts = multiviewLayouts?.filter( (layout) => layout.productionId === productionId || !layout.productionId ); + const multiviewLayoutNames = avaliableMultiviewLayouts?.map((layout) => layout.name) || []; - const currentMultiviewValue = - selectedMultiviewLayout?.tableIndex === tableIndex - ? selectedMultiviewLayout?.layout.name - : currentValue?.name; useEffect(() => { if (multiview) { - setSelectedMultiviewLayout({ layout: multiview, tableIndex: tableIndex }); + setSelectedMultiviewLayout(multiview); return; } if (multiviewLayouts) { @@ -61,10 +50,7 @@ export default function MultiviewSettingsConfig({ (m) => m.productionId !== undefined ); if (defaultMultiview) { - setSelectedMultiviewLayout({ - layout: defaultMultiview, - tableIndex: tableIndex - }); + setSelectedMultiviewLayout(defaultMultiview); } } }, [lastItem, multiview, multiviewLayouts, newMultiviewLayout]); @@ -93,10 +79,7 @@ export default function MultiviewSettingsConfig({ }, output: multiview?.output || selected.output }; - setSelectedMultiviewLayout({ - layout: updatedMultiview, - tableIndex: tableIndex - }); + setSelectedMultiviewLayout(updatedMultiview); handleUpdateMultiview({ ...updatedMultiview, for_pipeline_idx: 0 }); }; @@ -184,8 +167,16 @@ export default function MultiviewSettingsConfig({ return (
-
+

{t('preset.multiview_output_settings')}

+ {lastItem && ( + + )}
({ label: singleItem }))} - value={currentMultiviewValue || ''} + value={selectedMultiviewLayout?.name || ''} update={(value) => handleSetSelectedMultiviewLayout(value)} /> - {lastItem && ( - - )}
{ const [loading, setLoading] = useState(false); - const checkProductionPipelinesAndControlPanels = ( + const checkProductionPipelines = ( production: Production, pipelines: ResourcesCompactPipelineResponse[] | undefined ) => { @@ -42,5 +42,5 @@ export function useCheckProductionPipelinesAndControlPanels(): CallbackHook< } }; }; - return [checkProductionPipelinesAndControlPanels, loading]; + return [checkProductionPipelines, loading]; }