import React, { createContext, RefObject, useCallback, useContext, useState, useRef, } from 'react'; import { container } from 'tsyringe'; import { FileSystemService, fileSystemServiceToken, } from '../services/filesystem/fileSystem.trait'; import { WorkspaceService, workspaceServiceToken, } from '../services/workspace/workspace.trait'; const serviceToken = { fileSystem: fileSystemServiceToken, workspace: workspaceServiceToken, } as const; type ServiceUpdateCallbacks = Record void>; export interface ServiceContextData { updateCallbacks?: RefObject; fileSystem?: FileSystemService; workspace?: WorkspaceService; } export const ServiceContext = createContext({}); export const useServiceContext = () => { const { updateCallbacks, ...ctx } = useContext(ServiceContext); const register = useCallback( ( serviceName: T, value: ServiceContextData[T], ) => { container.register(serviceToken[serviceName], { useValue: value }); updateCallbacks?.current?.[serviceToken[serviceName]]?.(value); }, [updateCallbacks], ); return { ...ctx, register }; }; const useServiceInterceptor = < T extends keyof typeof serviceToken, S = ServiceContextData[T], >( serviceName: T, ) => { const token = serviceToken[serviceName]; const [service, setService] = useState(() => { if (container.isRegistered(token)) { return container.resolve(token); } }); return [service, setService] as const; }; export const ServiceProvider: React.FC = ({ children }) => { const [fileSystem, setFileSystemService] = useServiceInterceptor<'fileSystem'>('fileSystem'); const [workspace, setWorkspaceService] = useServiceInterceptor<'workspace'>('workspace'); const updateCallbacks = useRef({ [fileSystemServiceToken]: setFileSystemService, [workspaceServiceToken]: setWorkspaceService, }); return ( {children} ); };