| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- import {
- createContext,
- useContext,
- useCallback,
- useMemo,
- useEffect,
- Dispatch,
- SetStateAction,
- FC,
- } from 'react'
- import useScopeValue from '../hooks/use-scope-value'
- import useDetachLayout from '../hooks/use-detach-layout'
- import localStorage from '../../infrastructure/local-storage'
- import getMeta from '../../utils/meta'
- import { debugConsole } from '@/utils/debugging'
- import { BinaryFile } from '@/features/file-view/types/binary-file'
- export type IdeLayout = 'sideBySide' | 'flat'
- export type IdeView = 'editor' | 'file' | 'pdf' | 'history'
- type LayoutContextValue = {
- reattach: () => void
- detach: () => void
- detachIsLinked: boolean
- detachRole: 'detacher' | 'detached' | null
- changeLayout: (newLayout: IdeLayout, newView?: IdeView) => void
- view: IdeView
- setView: (view: IdeView) => void
- chatIsOpen: boolean
- setChatIsOpen: Dispatch<SetStateAction<LayoutContextValue['chatIsOpen']>>
- reviewPanelOpen: boolean
- setReviewPanelOpen: Dispatch<
- SetStateAction<LayoutContextValue['reviewPanelOpen']>
- >
- leftMenuShown: boolean
- setLeftMenuShown: Dispatch<
- SetStateAction<LayoutContextValue['leftMenuShown']>
- >
- loadingStyleSheet: boolean
- setLoadingStyleSheet: Dispatch<
- SetStateAction<LayoutContextValue['loadingStyleSheet']>
- >
- pdfLayout: IdeLayout
- pdfPreviewOpen: boolean
- }
- const debugPdfDetach = getMeta('ol-debugPdfDetach')
- export const LayoutContext = createContext<LayoutContextValue | undefined>(
- undefined
- )
- function setLayoutInLocalStorage(pdfLayout: IdeLayout) {
- localStorage.setItem(
- 'pdf.layout',
- pdfLayout === 'sideBySide' ? 'split' : 'flat'
- )
- }
- export const LayoutProvider: FC = ({ children }) => {
- // what to show in the "flat" view (editor or pdf)
- const [view, _setView] = useScopeValue<IdeView>('ui.view')
- const [toggleHistory] = useScopeValue<() => void>('toggleHistory')
- const [openFile] = useScopeValue<BinaryFile | null>('openFile')
- const setView = useCallback(
- (value: IdeView) => {
- _setView(oldValue => {
- // ensure that the "history:toggle" event is broadcast when switching in or out of history view
- if (value === 'history' || oldValue === 'history') {
- toggleHistory()
- }
- if (value === 'editor' && openFile) {
- // if a file is currently opened, ensure the view is 'file' instead of
- // 'editor' when the 'editor' view is requested. This is to ensure
- // that the entity selected in the file tree is the one visible and
- // that docs don't take precedence over files.
- return 'file'
- }
- return value
- })
- },
- [_setView, openFile, toggleHistory]
- )
- // whether the chat pane is open
- const [chatIsOpen, setChatIsOpen] = useScopeValue<boolean>('ui.chatOpen')
- // whether the review pane is open
- const [reviewPanelOpen, setReviewPanelOpen] =
- useScopeValue('ui.reviewPanelOpen')
- // whether the menu pane is open
- const [leftMenuShown, setLeftMenuShown] =
- useScopeValue<boolean>('ui.leftMenuShown')
- // whether to display the editor and preview side-by-side or full-width ("flat")
- const [pdfLayout, setPdfLayout] = useScopeValue<IdeLayout>('ui.pdfLayout')
- // whether stylesheet on theme is loading
- const [loadingStyleSheet, setLoadingStyleSheet] = useScopeValue<boolean>(
- 'ui.loadingStyleSheet'
- )
- const changeLayout = useCallback(
- (newLayout: IdeLayout, newView: IdeView = 'editor') => {
- setPdfLayout(newLayout)
- setView(newLayout === 'sideBySide' ? 'editor' : newView)
- setLayoutInLocalStorage(newLayout)
- },
- [setPdfLayout, setView]
- )
- const {
- reattach,
- detach,
- isLinking: detachIsLinking,
- isLinked: detachIsLinked,
- role: detachRole,
- isRedundant: detachIsRedundant,
- } = useDetachLayout()
- const pdfPreviewOpen =
- pdfLayout === 'sideBySide' || view === 'pdf' || detachRole === 'detacher'
- useEffect(() => {
- if (debugPdfDetach) {
- debugConsole.warn('Layout Effect', {
- detachIsRedundant,
- detachRole,
- detachIsLinking,
- detachIsLinked,
- })
- }
- if (detachRole !== 'detacher') return // not in a PDF detacher layout
- if (detachIsRedundant) {
- changeLayout('sideBySide')
- return
- }
- if (detachIsLinking || detachIsLinked) {
- // the tab is linked to a detached tab (or about to be linked); show
- // editor only
- changeLayout('flat', 'editor')
- }
- }, [
- detachIsRedundant,
- detachRole,
- detachIsLinking,
- detachIsLinked,
- changeLayout,
- ])
- const value = useMemo<LayoutContextValue>(
- () => ({
- reattach,
- detach,
- detachIsLinked,
- detachRole,
- changeLayout,
- chatIsOpen,
- leftMenuShown,
- pdfLayout,
- pdfPreviewOpen,
- reviewPanelOpen,
- loadingStyleSheet,
- setChatIsOpen,
- setLeftMenuShown,
- setPdfLayout,
- setReviewPanelOpen,
- setLoadingStyleSheet,
- setView,
- view,
- }),
- [
- reattach,
- detach,
- detachIsLinked,
- detachRole,
- changeLayout,
- chatIsOpen,
- leftMenuShown,
- pdfLayout,
- pdfPreviewOpen,
- reviewPanelOpen,
- loadingStyleSheet,
- setChatIsOpen,
- setLeftMenuShown,
- setPdfLayout,
- setReviewPanelOpen,
- setLoadingStyleSheet,
- setView,
- view,
- ]
- )
- return (
- <LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
- )
- }
- export function useLayoutContext() {
- const context = useContext(LayoutContext)
- if (!context) {
- throw new Error('useLayoutContext is only available inside LayoutProvider')
- }
- return context
- }
|