'use client'; import { FileType } from '@/api/asset_interfaces/chat'; import { useEffect, useMemo, useState } from 'react'; import { FileConfig, FileView, FileViewConfig, FileViewSecondary } from './interfaces'; import { useMemoizedFn, useUpdateEffect } from '@/hooks'; import { create } from 'mutative'; import { ChatLayoutView } from '../../interfaces'; import type { SelectedFile } from '../../interfaces'; import { timeout } from '@/lib'; import { useRouter } from 'next/navigation'; import { BusterRoutes } from '@/routes'; import { SelectedFileSecondaryRenderRecord } from '../../FileContainer/FileContainerSecondary'; import { ChatParams } from '../useGetChatParams'; import { initializeFileViews } from './helpers'; import { DEFAULT_FILE_VIEW } from '../helpers'; import { useAppLayoutContextSelector } from '@/context/BusterAppLayout'; export const useLayoutConfig = ({ selectedFile, isVersionHistoryMode, chatId, onSetSelectedFile, animateOpenSplitter, metricId, dashboardId, currentRoute, secondaryView }: { selectedFile: SelectedFile | null; isVersionHistoryMode: boolean; chatId: string | undefined; animateOpenSplitter: (side: 'left' | 'right' | 'both') => void; onSetSelectedFile: (file: SelectedFile | null) => void; } & ChatParams) => { const onChangeQueryParams = useAppLayoutContextSelector((x) => x.onChangeQueryParams); const onChangePage = useAppLayoutContextSelector((x) => x.onChangePage); const [fileViews, setFileViews] = useState>(() => initializeFileViews({ secondaryView, metricId, dashboardId, currentRoute }) ); const selectedFileId = selectedFile?.id; const selectedFileType = selectedFile?.type; const selectedFileView: FileView | undefined = useMemo(() => { if (!selectedFileId) return undefined; return ( fileViews[selectedFileId]?.selectedFileView || DEFAULT_FILE_VIEW[selectedFileType as FileType] ); }, [fileViews, selectedFileId, selectedFileType]); const selectedFileViewConfig: FileViewConfig | undefined = useMemo(() => { if (!selectedFileId) return undefined; return fileViews[selectedFileId]?.fileViewConfig; }, [fileViews, selectedFileId]); const selectedFileViewSecondary: FileViewSecondary | null = useMemo(() => { if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return null; return selectedFileViewConfig?.[selectedFileView]?.secondaryView ?? null; }, [selectedFileViewConfig, selectedFileId, selectedFileView]); const selectedFileViewRenderSecondary: boolean = useMemo(() => { if (!selectedFileViewSecondary || !selectedFileType) return false; if ( selectedFileType in SelectedFileSecondaryRenderRecord && SelectedFileSecondaryRenderRecord[selectedFileType as FileType]?.[ selectedFileViewSecondary ] !== undefined ) { return ( SelectedFileSecondaryRenderRecord[selectedFileType as FileType]?.[ selectedFileViewSecondary ] ?? false ); } return true; }, [selectedFileViewConfig, selectedFileId, selectedFileView, selectedFileType]); const onSetFileView = useMemoizedFn( async ({ fileView, fileId: fileIdProp, secondaryView }: { fileView?: FileView; fileId?: string | undefined; secondaryView?: FileViewSecondary; }) => { const fileId = fileIdProp ?? selectedFileId; if (!fileId) { onCollapseFileClick(); return; } if (secondaryView) { animateOpenSplitter('right'); await timeout(chatId ? 250 : 0); //wait for splitter to close before opening secondary view } else if (chatId) { animateOpenSplitter('both'); } setFileViews((prev) => { return create(prev, (draft) => { if (!draft[fileId]) { draft[fileId] = { selectedFileView: DEFAULT_FILE_VIEW[selectedFileType as FileType] || 'file', fileViewConfig: {} }; } const usedFileView = fileView ?? draft[fileId].selectedFileView ?? DEFAULT_FILE_VIEW[selectedFileType as FileType]; if (fileView !== undefined) { draft[fileId].selectedFileView = fileView; } if (secondaryView !== undefined) { if (!draft[fileId].fileViewConfig) { draft[fileId].fileViewConfig = {}; } draft[fileId].fileViewConfig[usedFileView] = { ...(draft[fileId].fileViewConfig[usedFileView] || {}), secondaryView }; } }); }); } ); const closeSecondaryView = useMemoizedFn(async () => { if (!selectedFileId || !selectedFileViewConfig || !selectedFileView) return; setFileViews((prev) => { return create(prev, (draft) => { if (!draft[selectedFileId]?.fileViewConfig?.[selectedFileView]) return; draft[selectedFileId].fileViewConfig[selectedFileView].secondaryView = null; }); }); onChangeQueryParams({ secondary_view: null }); await timeout(250); //wait for the panel to close }); const onCollapseFileClick = useMemoizedFn(async (navigateToChat: boolean = true) => { onSetSelectedFile(null); if (selectedFileViewSecondary) { await closeSecondaryView(); } if (navigateToChat && chatId) { onChangePage({ route: BusterRoutes.APP_CHAT_ID, chatId }); } }); const selectedLayout: ChatLayoutView = useMemo(() => { if (chatId) { if (selectedFileViewSecondary) return 'chat-hidden'; if (selectedFileId) return 'both'; return 'chat-only'; } return 'file-only'; }, [selectedFileId, chatId, selectedFileViewSecondary]); //we need to use for when the user clicks the back or forward in the browser useUpdateEffect(() => { const newInitialFileViews = initializeFileViews({ secondaryView, metricId, dashboardId, currentRoute }); const fileId = Object.keys(newInitialFileViews)[0]; const fileView = newInitialFileViews[fileId]?.selectedFileView; const secondaryViewFromSelected = newInitialFileViews[fileId]?.fileViewConfig?.[fileView]?.secondaryView; onSetFileView({ fileId, fileView, secondaryView: secondaryViewFromSelected }); }, [metricId, secondaryView, dashboardId, currentRoute]); return useMemo( () => ({ selectedLayout, selectedFileView, selectedFileViewSecondary, selectedFileViewRenderSecondary, onSetFileView, closeSecondaryView, onCollapseFileClick }), [ selectedLayout, selectedFileView, selectedFileViewSecondary, selectedFileViewRenderSecondary, onSetFileView, closeSecondaryView, onCollapseFileClick ] ); };