| 'use client' |
|
|
| import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react' |
| import useSWR from 'swr' |
| import { createContext, useContext, useContextSelector } from 'use-context-selector' |
| import type { FC, ReactNode } from 'react' |
| import { fetchAppList } from '@/service/apps' |
| import Loading from '@/app/components/base/loading' |
| import { fetchCurrentWorkspace, fetchLanggeniusVersion, fetchUserProfile, getSystemFeatures } from '@/service/common' |
| import type { App } from '@/types/app' |
| import { Theme } from '@/types/app' |
| import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse } from '@/models/common' |
| import MaintenanceNotice from '@/app/components/header/maintenance-notice' |
| import type { SystemFeatures } from '@/types/feature' |
| import { defaultSystemFeatures } from '@/types/feature' |
|
|
| export type AppContextValue = { |
| theme: Theme |
| setTheme: (theme: Theme) => void |
| apps: App[] |
| systemFeatures: SystemFeatures |
| mutateApps: VoidFunction |
| userProfile: UserProfileResponse |
| mutateUserProfile: VoidFunction |
| currentWorkspace: ICurrentWorkspace |
| isCurrentWorkspaceManager: boolean |
| isCurrentWorkspaceOwner: boolean |
| isCurrentWorkspaceEditor: boolean |
| isCurrentWorkspaceDatasetOperator: boolean |
| mutateCurrentWorkspace: VoidFunction |
| pageContainerRef: React.RefObject<HTMLDivElement> |
| langeniusVersionInfo: LangGeniusVersionResponse |
| useSelector: typeof useSelector |
| } |
|
|
| const initialLangeniusVersionInfo = { |
| current_env: '', |
| current_version: '', |
| latest_version: '', |
| release_date: '', |
| release_notes: '', |
| version: '', |
| can_auto_update: false, |
| } |
|
|
| const initialWorkspaceInfo: ICurrentWorkspace = { |
| id: '', |
| name: '', |
| plan: '', |
| status: '', |
| created_at: 0, |
| role: 'normal', |
| providers: [], |
| in_trail: true, |
| } |
|
|
| const AppContext = createContext<AppContextValue>({ |
| theme: Theme.light, |
| systemFeatures: defaultSystemFeatures, |
| setTheme: () => { }, |
| apps: [], |
| mutateApps: () => { }, |
| userProfile: { |
| id: '', |
| name: '', |
| email: '', |
| avatar: '', |
| is_password_set: false, |
| }, |
| currentWorkspace: initialWorkspaceInfo, |
| isCurrentWorkspaceManager: false, |
| isCurrentWorkspaceOwner: false, |
| isCurrentWorkspaceEditor: false, |
| isCurrentWorkspaceDatasetOperator: false, |
| mutateUserProfile: () => { }, |
| mutateCurrentWorkspace: () => { }, |
| pageContainerRef: createRef(), |
| langeniusVersionInfo: initialLangeniusVersionInfo, |
| useSelector, |
| }) |
|
|
| export function useSelector<T>(selector: (value: AppContextValue) => T): T { |
| return useContextSelector(AppContext, selector) |
| } |
|
|
| export type AppContextProviderProps = { |
| children: ReactNode |
| } |
|
|
| export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) => { |
| const pageContainerRef = useRef<HTMLDivElement>(null) |
|
|
| const { data: appList, mutate: mutateApps } = useSWR({ url: '/apps', params: { page: 1, limit: 30, name: '' } }, fetchAppList) |
| const { data: userProfileResponse, mutate: mutateUserProfile } = useSWR({ url: '/account/profile', params: {} }, fetchUserProfile) |
| const { data: currentWorkspaceResponse, mutate: mutateCurrentWorkspace } = useSWR({ url: '/workspaces/current', params: {} }, fetchCurrentWorkspace) |
|
|
| const { data: systemFeatures } = useSWR({ url: '/console/system-features' }, getSystemFeatures, { |
| fallbackData: defaultSystemFeatures, |
| }) |
|
|
| const [userProfile, setUserProfile] = useState<UserProfileResponse>() |
| const [langeniusVersionInfo, setLangeniusVersionInfo] = useState<LangGeniusVersionResponse>(initialLangeniusVersionInfo) |
| const [currentWorkspace, setCurrentWorkspace] = useState<ICurrentWorkspace>(initialWorkspaceInfo) |
| const isCurrentWorkspaceManager = useMemo(() => ['owner', 'admin'].includes(currentWorkspace.role), [currentWorkspace.role]) |
| const isCurrentWorkspaceOwner = useMemo(() => currentWorkspace.role === 'owner', [currentWorkspace.role]) |
| const isCurrentWorkspaceEditor = useMemo(() => ['owner', 'admin', 'editor'].includes(currentWorkspace.role), [currentWorkspace.role]) |
| const isCurrentWorkspaceDatasetOperator = useMemo(() => currentWorkspace.role === 'dataset_operator', [currentWorkspace.role]) |
| const updateUserProfileAndVersion = useCallback(async () => { |
| if (userProfileResponse && !userProfileResponse.bodyUsed) { |
| const result = await userProfileResponse.json() |
| setUserProfile(result) |
| const current_version = userProfileResponse.headers.get('x-version') |
| const current_env = process.env.NODE_ENV === 'development' ? 'DEVELOPMENT' : userProfileResponse.headers.get('x-env') |
| const versionData = await fetchLanggeniusVersion({ url: '/version', params: { current_version } }) |
| setLangeniusVersionInfo({ ...versionData, current_version, latest_version: versionData.version, current_env }) |
| } |
| }, [userProfileResponse]) |
|
|
| useEffect(() => { |
| updateUserProfileAndVersion() |
| }, [updateUserProfileAndVersion, userProfileResponse]) |
|
|
| useEffect(() => { |
| if (currentWorkspaceResponse) |
| setCurrentWorkspace(currentWorkspaceResponse) |
| }, [currentWorkspaceResponse]) |
|
|
| const [theme, setTheme] = useState<Theme>(Theme.light) |
| const handleSetTheme = useCallback((theme: Theme) => { |
| setTheme(theme) |
| globalThis.document.documentElement.setAttribute('data-theme', theme) |
| }, []) |
|
|
| useEffect(() => { |
| globalThis.document.documentElement.setAttribute('data-theme', theme) |
| |
| }, []) |
|
|
| if (!appList || !userProfile) |
| return <Loading type='app' /> |
|
|
| return ( |
| <AppContext.Provider value={{ |
| theme, |
| setTheme: handleSetTheme, |
| apps: appList.data, |
| systemFeatures, |
| mutateApps, |
| userProfile, |
| mutateUserProfile, |
| pageContainerRef, |
| langeniusVersionInfo, |
| useSelector, |
| currentWorkspace, |
| isCurrentWorkspaceManager, |
| isCurrentWorkspaceOwner, |
| isCurrentWorkspaceEditor, |
| isCurrentWorkspaceDatasetOperator, |
| mutateCurrentWorkspace, |
| }}> |
| <div className='flex flex-col h-full overflow-y-auto'> |
| {globalThis.document?.body?.getAttribute('data-public-maintenance-notice') && <MaintenanceNotice />} |
| <div ref={pageContainerRef} className='grow relative flex flex-col overflow-y-auto overflow-x-hidden bg-background-body'> |
| {children} |
| </div> |
| </div> |
| </AppContext.Provider> |
| ) |
| } |
|
|
| export const useAppContext = () => useContext(AppContext) |
|
|
| export default AppContext |
|
|