style: refactor context

This commit is contained in:
AkaraChen 2024-11-15 14:26:46 +08:00
parent 6b759795d5
commit 5ad148b8f9
28 changed files with 130 additions and 420 deletions

View File

@ -1,7 +1,7 @@
'use client'
import { createContext, useContext } from 'use-context-selector'
import type { ModelAndParameter } from '../types'
import { createSelectorCtx } from '@/utils/context'
export type DebugWithMultipleModelContextType = {
multipleModelConfigs: ModelAndParameter[]
@ -9,13 +9,9 @@ export type DebugWithMultipleModelContextType = {
onDebugWithMultipleModelChange: (singleModelConfig: ModelAndParameter) => void
checkCanSend?: () => boolean
}
const DebugWithMultipleModelContext = createContext<DebugWithMultipleModelContextType>({
multipleModelConfigs: [],
onMultipleModelConfigsChange: () => {},
onDebugWithMultipleModelChange: () => {},
})
const [,useDebugWithMultipleModelContext, DebugWithMultipleModelContext] = createSelectorCtx<DebugWithMultipleModelContextType>()
export const useDebugWithMultipleModelContext = () => useContext(DebugWithMultipleModelContext)
export { useDebugWithMultipleModelContext }
type DebugWithMultipleModelContextProviderProps = {
children: React.ReactNode

View File

@ -13,7 +13,7 @@ import InfiniteScroll from 'react-infinite-scroll-component'
import dayjs from 'dayjs'
import utc from 'dayjs/plugin/utc'
import timezone from 'dayjs/plugin/timezone'
import { createContext, useContext } from 'use-context-selector'
import { useContext } from 'use-context-selector'
import { useShallow } from 'zustand/react/shallow'
import { useTranslation } from 'react-i18next'
import type { ChatItemInTree } from '../../base/chat/types'
@ -45,6 +45,7 @@ import { CopyIcon } from '@/app/components/base/copy-icon'
import { buildChatItemTree, getThreadMessages } from '@/app/components/base/chat/utils'
import { getProcessedFilesFromResponse } from '@/app/components/base/file-uploader/utils'
import { correctProvider } from '@/utils'
import { createSelectorCtx } from '@/utils/context'
dayjs.extend(utc)
dayjs.extend(timezone)
@ -62,7 +63,7 @@ type IDrawerContext = {
appDetail?: App
}
const DrawerContext = createContext<IDrawerContext>({} as IDrawerContext)
const [,, DrawerContext] = createSelectorCtx<IDrawerContext>()
/**
* Icon component with numbers

View File

@ -1,7 +1,6 @@
'use client'
import type { RefObject } from 'react'
import { createContext, useContext } from 'use-context-selector'
import type {
Callback,
ChatConfig,
@ -15,8 +14,9 @@ import type {
AppMeta,
ConversationItem,
} from '@/models/share'
import { createSelectorCtx } from '@/utils/context'
export interface ChatWithHistoryContextValue {
export type ChatWithHistoryContextValue = {
appInfoError?: any
appInfoLoading?: boolean
appMeta?: AppMeta
@ -51,29 +51,4 @@ export interface ChatWithHistoryContextValue {
themeBuilder?: ThemeBuilder
}
export const ChatWithHistoryContext = createContext<ChatWithHistoryContextValue>({
currentConversationId: '',
appPrevChatList: [],
pinnedConversationList: [],
conversationList: [],
showConfigPanelBeforeChat: false,
newConversationInputs: {},
newConversationInputsRef: { current: {} },
handleNewConversationInputsChange: () => {},
inputsForms: [],
handleNewConversation: () => {},
handleStartChat: () => {},
handleChangeConversation: () => {},
handlePinConversation: () => {},
handleUnpinConversation: () => {},
handleDeleteConversation: () => {},
conversationRenaming: false,
handleRenameConversation: () => {},
handleNewConversationCompleted: () => {},
chatShouldReloadKey: '',
isMobile: false,
isInstalledApp: false,
handleFeedback: () => {},
currentChatInstanceRef: { current: { handleStop: () => {} } },
})
export const useChatWithHistoryContext = () => useContext(ChatWithHistoryContext)
export const [, useChatWithHistoryContext, ChatWithHistoryContext] = createSelectorCtx<ChatWithHistoryContextValue>()

View File

@ -1,8 +1,8 @@
'use client'
import type { ReactNode } from 'react'
import { createContext, useContext } from 'use-context-selector'
import type { ChatProps } from './index'
import { createSelectorCtx } from '@/utils/context'
export type ChatContextValue = Pick<ChatProps, 'config'
| 'isResponding'
@ -18,9 +18,7 @@ export type ChatContextValue = Pick<ChatProps, 'config'
| 'onFeedback'
>
const ChatContext = createContext<ChatContextValue>({
chatList: [],
})
const [, useChatContext, ChatContext] = createSelectorCtx<ChatContextValue>()
type ChatContextProviderProps = {
children: ReactNode
@ -61,6 +59,6 @@ export const ChatContextProvider = ({
)
}
export const useChatContext = () => useContext(ChatContext)
export { useChatContext }
export default ChatContext

View File

@ -1,7 +1,6 @@
'use client'
import type { RefObject } from 'react'
import { createContext, useContext } from 'use-context-selector'
import type {
ChatConfig,
ChatItem,
@ -14,8 +13,9 @@ import type {
AppMeta,
ConversationItem,
} from '@/models/share'
import { createSelectorCtx } from '@/utils/context'
export interface EmbeddedChatbotContextValue {
export type EmbeddedChatbotContextValue = {
appInfoError?: any
appInfoLoading?: boolean
appMeta?: AppMeta
@ -45,24 +45,4 @@ export interface EmbeddedChatbotContextValue {
themeBuilder?: ThemeBuilder
}
export const EmbeddedChatbotContext = createContext<EmbeddedChatbotContextValue>({
currentConversationId: '',
appPrevChatList: [],
pinnedConversationList: [],
conversationList: [],
showConfigPanelBeforeChat: false,
newConversationInputs: {},
newConversationInputsRef: { current: {} },
handleNewConversationInputsChange: () => {},
inputsForms: [],
handleNewConversation: () => {},
handleStartChat: () => {},
handleChangeConversation: () => {},
handleNewConversationCompleted: () => {},
chatShouldReloadKey: '',
isMobile: false,
isInstalledApp: false,
handleFeedback: () => {},
currentChatInstanceRef: { current: { handleStop: () => {} } },
})
export const useEmbeddedChatbotContext = () => useContext(EmbeddedChatbotContext)
export const [, useEmbeddedChatbotContext, EmbeddedChatbotContext] = createSelectorCtx<EmbeddedChatbotContextValue>()

View File

@ -1,5 +1,4 @@
import {
createContext,
useRef,
} from 'react'
import type {
@ -7,8 +6,9 @@ import type {
FeaturesStore,
} from './store'
import { createFeaturesStore } from './store'
import { createCtx } from '@/utils/context'
export const FeaturesContext = createContext<FeaturesStore | null>(null)
export const [, , FeaturesContext] = createCtx<FeaturesStore>()
type FeaturesProviderProps = {
children: React.ReactNode

View File

@ -1,6 +1,4 @@
import {
createContext,
useContext,
useRef,
} from 'react'
import {
@ -10,6 +8,7 @@ import {
import type {
FileEntity,
} from './types'
import { createCtx } from '@/utils/context'
type Shape = {
files: FileEntity[]
@ -30,20 +29,13 @@ export const createFileStore = (
}
type FileStore = ReturnType<typeof createFileStore>
export const FileContext = createContext<FileStore | null>(null)
export const [, useFileStore, FileContext] = createCtx<FileStore>()
export function useStore<T>(selector: (state: Shape) => T): T {
const store = useContext(FileContext)
if (!store)
throw new Error('Missing FileContext.Provider in the tree')
const store = useFileStore()
return useZustandStore(store, selector)
}
export const useFileStore = () => {
return useContext(FileContext)!
}
type FileProviderProps = {
children: React.ReactNode
value?: FileEntity[]

View File

@ -17,6 +17,7 @@ import {
import type { OffsetOptions, Placement } from '@floating-ui/react'
import cn from '@/utils/classnames'
import { createCtx } from '@/utils/context'
export type PortalToFollowElemOptions = {
/*
* top, bottom, left, right
@ -78,18 +79,9 @@ export function usePortalToFollowElem({
)
}
type ContextType = ReturnType<typeof usePortalToFollowElem> | null
type ContextType = ReturnType<typeof usePortalToFollowElem>
const PortalToFollowElemContext = React.createContext<ContextType>(null)
export function usePortalToFollowElemContext() {
const context = React.useContext(PortalToFollowElemContext)
if (context == null)
throw new Error('PortalToFollowElem components must be wrapped in <PortalToFollowElem />')
return context
}
const [, usePortalToFollowElemContext, PortalToFollowElemContext] = createCtx<ContextType>()
export function PortalToFollowElem({
children,

View File

@ -8,8 +8,8 @@ import {
InformationCircleIcon,
XCircleIcon,
} from '@heroicons/react/20/solid'
import { createContext, useContext } from 'use-context-selector'
import classNames from '@/utils/classnames'
import { createSelectorCtx } from '@/utils/context'
export type IToastProps = {
type?: 'success' | 'error' | 'warning' | 'info'
@ -23,8 +23,7 @@ type IToastContext = {
notify: (props: IToastProps) => void
}
export const ToastContext = createContext<IToastContext>({} as IToastContext)
export const useToastContext = () => useContext(ToastContext)
export const [, useToastContext, ToastContext] = createSelectorCtx<IToastContext>()
const Toast = ({
type = 'info',
message,
@ -46,7 +45,7 @@ const Toast = ({
type === 'info' ? 'bg-blue-50' : '',
)}>
<div className="flex">
<div className="flex-shrink-0">
<div className="shrink-0">
{type === 'success' && <CheckCircleIcon className="w-5 h-5 text-green-400" aria-hidden="true" />}
{type === 'error' && <XCircleIcon className="w-5 h-5 text-red-400" aria-hidden="true" />}
{type === 'warning' && <ExclamationTriangleIcon className="w-5 h-5 text-yellow-400" aria-hidden="true" />}

View File

@ -3,7 +3,7 @@ import type { FC } from 'react'
import React, { useState } from 'react'
import useSWR from 'swr'
import { ArrowLeftIcon } from '@heroicons/react/24/solid'
import { createContext, useContext } from 'use-context-selector'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import { useRouter } from 'next/navigation'
import { omit } from 'lodash-es'
@ -25,8 +25,9 @@ import type { DocForm } from '@/models/datasets'
import { useDatasetDetailContext } from '@/context/dataset-detail'
import FloatRightContainer from '@/app/components/base/float-right-container'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { createSelectorCtx } from '@/utils/context'
export const DocumentContext = createContext<{ datasetId?: string; documentId?: string; docForm: string }>({ docForm: '' })
export const [,, DocumentContext] = createSelectorCtx<{ datasetId?: string; documentId?: string; docForm: string }>()
type DocumentTitleProps = {
extension?: string

View File

@ -9,7 +9,6 @@ import {
useState,
} from 'react'
import {
createContext,
useContextSelector,
} from 'use-context-selector'
import { PLUGIN_TYPE_SEARCH_MAP } from './plugin-type-switch'
@ -23,6 +22,7 @@ import {
useMarketplaceCollectionsAndPlugins,
useMarketplacePlugins,
} from './hooks'
import { createSelectorCtx } from '@/utils/context'
export type MarketplaceContextValue = {
intersected: boolean
@ -44,25 +44,7 @@ export type MarketplaceContextValue = {
isLoading: boolean
}
export const MarketplaceContext = createContext<MarketplaceContextValue>({
intersected: true,
setIntersected: () => {},
searchPluginText: '',
handleSearchPluginTextChange: () => {},
filterPluginTags: [],
handleFilterPluginTagsChange: () => {},
activePluginType: PLUGIN_TYPE_SEARCH_MAP.all,
handleActivePluginTypeChange: () => {},
plugins: undefined,
resetPlugins: () => {},
sort: DEFAULT_SORT,
handleSortChange: () => {},
marketplaceCollectionsFromClient: [],
setMarketplaceCollectionsFromClient: () => {},
marketplaceCollectionPluginsMapFromClient: {},
setMarketplaceCollectionPluginsMapFromClient: () => {},
isLoading: false,
})
export const [,, MarketplaceContext] = createSelectorCtx<MarketplaceContextValue>()
type MarketplaceContextProviderProps = {
children: ReactNode

View File

@ -7,7 +7,6 @@ import {
useState,
} from 'react'
import {
createContext,
useContextSelector,
} from 'use-context-selector'
import { useSelector as useAppContextSelector } from '@/context/app-context'
@ -15,6 +14,7 @@ import type { PluginDetail } from '../types'
import type { FilterState } from './filter-management'
import { useTranslation } from 'react-i18next'
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
import { createSelectorCtx } from '@/utils/context'
export type PluginPageContextValue = {
containerRef: React.RefObject<HTMLDivElement>
@ -27,20 +27,7 @@ export type PluginPageContextValue = {
options: Array<{ value: string, text: string }>
}
export const PluginPageContext = createContext<PluginPageContextValue>({
containerRef: { current: null },
currentPluginDetail: undefined,
setCurrentPluginDetail: () => { },
filters: {
categories: [],
tags: [],
searchQuery: '',
},
setFilters: () => { },
activeTab: '',
setActiveTab: () => { },
options: [],
})
export const [,, PluginPageContext] = createSelectorCtx<PluginPageContextValue>()
type PluginPageContextProviderProps = {
children: ReactNode

View File

@ -1,11 +1,11 @@
import {
createContext,
useRef,
} from 'react'
import { createWorkflowStore } from './store'
import { createCtx } from '@/utils/context'
type WorkflowStore = ReturnType<typeof createWorkflowStore>
export const WorkflowContext = createContext<WorkflowStore | null>(null)
export const [,, WorkflowContext] = createCtx<WorkflowStore>()
type WorkflowProviderProps = {
children: React.ReactNode

View File

@ -1,7 +1,6 @@
'use client'
import {
createContext,
memo,
useRef,
} from 'react'
@ -13,9 +12,10 @@ import {
} from '@lexical/list'
import { createNoteEditorStore } from './store'
import theme from './theme'
import { createCtx } from '@/utils/context'
type NoteEditorStore = ReturnType<typeof createNoteEditorStore>
const NoteEditorContext = createContext<NoteEditorStore | null>(null)
const [,, NoteEditorContext] = createCtx<NoteEditorStore>()
type NoteEditorContextProviderProps = {
value: string

View File

@ -1,12 +1,12 @@
import { type ReactNode, createContext, useContext, useMemo, useState } from 'react'
import { type ReactNode, useContext, useMemo, useState } from 'react'
import { type StoreApi, create } from 'zustand'
import { type TemporalState, temporal } from 'zundo'
import isDeepEqual from 'fast-deep-equal'
import type { Edge, Node } from './types'
import type { WorkflowHistoryEvent } from './hooks'
import { createCtx } from '@/utils/context'
export const WorkflowHistoryStoreContext = createContext<WorkflowHistoryStoreContextType>({ store: null, shortcutsEnabled: true, setShortcutsEnabled: () => {} })
export const Provider = WorkflowHistoryStoreContext.Provider
export const [Provider,,WorkflowHistoryStoreContext] = createCtx<WorkflowHistoryStoreContextType>()
export function WorkflowHistoryProvider({
nodes,

View File

@ -1,8 +1,8 @@
'use client'
import { createRef, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import useSWR from 'swr'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { useContext, useContextSelector } from 'use-context-selector'
import type { FC, ReactNode } from 'react'
import { fetchAppList } from '@/service/apps'
import Loading from '@/app/components/base/loading'
@ -13,6 +13,7 @@ import type { ICurrentWorkspace, LangGeniusVersionResponse, UserProfileResponse
import MaintenanceNotice from '@/app/components/header/maintenance-notice'
import type { SystemFeatures } from '@/types/feature'
import { defaultSystemFeatures } from '@/types/feature'
import { createSelectorCtx } from '@/utils/context'
export type AppContextValue = {
theme: Theme
@ -54,30 +55,7 @@ const initialWorkspaceInfo: ICurrentWorkspace = {
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,
})
const [,, AppContext] = createSelectorCtx<AppContextValue>()
export function useSelector<T>(selector: (value: AppContextValue) => T): T {
return useContextSelector(AppContext, selector)

View File

@ -1,8 +1,8 @@
import { createContext, useContext } from 'use-context-selector'
import type { DataSet } from '@/models/datasets'
import { createSelectorCtx } from '@/utils/context'
const DatasetDetailContext = createContext<{ indexingTechnique?: string; dataset?: DataSet; mutateDatasetRes?: () => void }>({})
const [, useDatasetDetailContext, DatasetDetailContext] = createSelectorCtx<{ indexingTechnique?: string; dataset?: DataSet; mutateDatasetRes?: () => void }>()
export const useDatasetDetailContext = () => useContext(DatasetDetailContext)
export { useDatasetDetailContext }
export default DatasetDetailContext

View File

@ -1,7 +1,7 @@
'use client'
import { createContext, useContext } from 'use-context-selector'
import type { DataSet } from '@/models/datasets'
import { createSelectorCtx } from '@/utils/context'
export type DatasetsContextValue = {
datasets: DataSet[]
@ -9,12 +9,8 @@ export type DatasetsContextValue = {
currentDataset?: DataSet
}
const DatasetsContext = createContext<DatasetsContextValue>({
datasets: [],
mutateDatasets: () => {},
currentDataset: undefined,
})
const [, useDatasetsContext, DatasetsContext] = createSelectorCtx<DatasetsContextValue>()
export const useDatasetsContext = () => useContext(DatasetsContext)
export { useDatasetsContext }
export default DatasetsContext

View File

@ -1,5 +1,4 @@
import { createContext, useContext } from 'use-context-selector'
import { PromptMode } from '@/models/debug'
import type { PromptMode } from '@/models/debug'
import type {
AnnotationReplyConfig,
BlockStatus,
@ -20,13 +19,12 @@ import type {
} from '@/models/debug'
import type { ExternalDataTool } from '@/models/common'
import type { DataSet } from '@/models/datasets'
import type { VisionSettings } from '@/types/app'
import { ModelModeType, RETRIEVE_TYPE, Resolution, TransferMethod } from '@/types/app'
import { ANNOTATION_DEFAULT, DEFAULT_AGENT_SETTING, DEFAULT_CHAT_PROMPT_CONFIG, DEFAULT_COMPLETION_PROMPT_CONFIG } from '@/config'
import type { ModelModeType, VisionSettings } from '@/types/app'
import type { FormValue } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { Collection } from '@/app/components/tools/types'
import { createSelectorCtx } from '@/utils/context'
interface IDebugConfiguration {
type IDebugConfiguration = {
appId: string
isAPIKeySet: boolean
isTrailFinished: boolean
@ -101,153 +99,8 @@ interface IDebugConfiguration {
setRerankSettingModalOpen: (rerankSettingModalOpen: boolean) => void
}
const DebugConfigurationContext = createContext<IDebugConfiguration>({
appId: '',
isAPIKeySet: false,
isTrailFinished: false,
mode: '',
modelModeType: ModelModeType.chat,
promptMode: PromptMode.simple,
setPromptMode: () => { },
isAdvancedMode: false,
isAgent: false,
isFunctionCall: false,
isOpenAI: false,
collectionList: [],
canReturnToSimpleMode: false,
setCanReturnToSimpleMode: () => { },
chatPromptConfig: DEFAULT_CHAT_PROMPT_CONFIG,
completionPromptConfig: DEFAULT_COMPLETION_PROMPT_CONFIG,
currentAdvancedPrompt: [],
showHistoryModal: () => { },
conversationHistoriesRole: {
user_prefix: 'user',
assistant_prefix: 'assistant',
},
setConversationHistoriesRole: () => { },
setCurrentAdvancedPrompt: () => { },
hasSetBlockStatus: {
context: false,
history: false,
query: false,
},
conversationId: '',
setConversationId: () => { },
introduction: '',
setIntroduction: () => { },
suggestedQuestions: [],
setSuggestedQuestions: () => { },
controlClearChatMessage: 0,
setControlClearChatMessage: () => { },
prevPromptConfig: {
prompt_template: '',
prompt_variables: [],
},
setPrevPromptConfig: () => { },
moreLikeThisConfig: {
enabled: false,
},
setMoreLikeThisConfig: () => { },
suggestedQuestionsAfterAnswerConfig: {
enabled: false,
},
setSuggestedQuestionsAfterAnswerConfig: () => { },
speechToTextConfig: {
enabled: false,
},
setSpeechToTextConfig: () => { },
textToSpeechConfig: {
enabled: false,
voice: '',
language: '',
},
setTextToSpeechConfig: () => { },
citationConfig: {
enabled: false,
},
setCitationConfig: () => { },
moderationConfig: {
enabled: false,
},
annotationConfig: {
id: '',
enabled: false,
score_threshold: ANNOTATION_DEFAULT.score_threshold,
embedding_model: {
embedding_model_name: '',
embedding_provider_name: '',
},
},
setAnnotationConfig: () => { },
setModerationConfig: () => { },
externalDataToolsConfig: [],
setExternalDataToolsConfig: () => { },
formattingChanged: false,
setFormattingChanged: () => { },
inputs: {},
setInputs: () => { },
query: '',
setQuery: () => { },
completionParams: {
max_tokens: 16,
temperature: 1, // 0-2
top_p: 1,
presence_penalty: 1, // -2-2
frequency_penalty: 1, // -2-2
},
setCompletionParams: () => { },
modelConfig: {
provider: 'OPENAI', // 'OPENAI'
model_id: 'gpt-3.5-turbo', // 'gpt-3.5-turbo'
mode: ModelModeType.unset,
configs: {
prompt_template: '',
prompt_variables: [],
},
more_like_this: null,
opening_statement: '',
suggested_questions: [],
sensitive_word_avoidance: null,
speech_to_text: null,
text_to_speech: null,
file_upload: null,
suggested_questions_after_answer: null,
retriever_resource: null,
annotation_reply: null,
dataSets: [],
agentConfig: DEFAULT_AGENT_SETTING,
},
setModelConfig: () => { },
dataSets: [],
showSelectDataSet: () => { },
setDataSets: () => { },
datasetConfigs: {
retrieval_model: RETRIEVE_TYPE.multiWay,
reranking_model: {
reranking_provider_name: '',
reranking_model_name: '',
},
top_k: 2,
score_threshold_enabled: false,
score_threshold: 0.7,
datasets: {
datasets: [],
},
},
setDatasetConfigs: () => { },
hasSetContextVar: false,
isShowVisionConfig: false,
visionConfig: {
enabled: false,
number_limits: 2,
detail: Resolution.low,
transfer_methods: [TransferMethod.remote_url],
},
setVisionConfig: () => { },
rerankSettingModalOpen: false,
setRerankSettingModalOpen: () => { },
})
const [, useDebugConfigurationContext, DebugConfigurationContext] = createSelectorCtx<IDebugConfiguration>()
export const useDebugConfigurationContext = () => useContext(DebugConfigurationContext)
export { useDebugConfigurationContext }
export default DebugConfigurationContext

View File

@ -1,14 +1,12 @@
'use client'
import { createContext, useContext } from 'use-context-selector'
import { useEventEmitter } from 'ahooks'
import type { EventEmitter } from 'ahooks/lib/useEventEmitter'
import { createSelectorCtx } from '@/utils/context'
const EventEmitterContext = createContext<{ eventEmitter: EventEmitter<string> | null }>({
eventEmitter: null,
})
const [, useEventEmitterContextContext, EventEmitterContext] = createSelectorCtx<{ eventEmitter: EventEmitter<string> }>()
export const useEventEmitterContextContext = () => useContext(EventEmitterContext)
export{ useEventEmitterContextContext }
type EventEmitterContextProviderProps = {
children: React.ReactNode

View File

@ -1,5 +1,5 @@
import { createContext } from 'use-context-selector'
import type { InstalledApp } from '@/models/explore'
import { createSelectorCtx } from '@/utils/context'
type IExplore = {
controlUpdateInstalledApps: number
@ -9,12 +9,6 @@ type IExplore = {
setInstalledApps: (installedApps: InstalledApp[]) => void
}
const ExploreContext = createContext<IExplore>({
controlUpdateInstalledApps: 0,
setControlUpdateInstalledApps: () => { },
hasEditPermission: false,
installedApps: [],
setInstalledApps: () => { },
})
const [,, ExploreContext] = createSelectorCtx<IExplore>()
export default ExploreContext

View File

@ -1,13 +1,14 @@
'use client'
import React, { createContext, useContext, useState } from 'react'
import { createCtx } from '@/utils/context'
import React, { useState } from 'react'
type ExternalApiPanelContextType = {
showExternalApiPanel: boolean
setShowExternalApiPanel: (show: boolean) => void
}
const ExternalApiPanelContext = createContext<ExternalApiPanelContextType | undefined>(undefined)
const [, useExternalApiPanel, ExternalApiPanelContext] = createCtx<ExternalApiPanelContextType>()
export const ExternalApiPanelProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [showExternalApiPanel, setShowExternalApiPanel] = useState(false)
@ -19,10 +20,4 @@ export const ExternalApiPanelProvider: React.FC<{ children: React.ReactNode }> =
)
}
export const useExternalApiPanel = () => {
const context = useContext(ExternalApiPanelContext)
if (context === undefined)
throw new Error('useExternalApiPanel must be used within an ExternalApiPanelProvider')
return context
}
export { useExternalApiPanel }

View File

@ -1,10 +1,11 @@
'use client'
import { createContext, useContext, useMemo } from 'react'
import { useMemo } from 'react'
import type { FC, ReactNode } from 'react'
import useSWR from 'swr'
import type { ExternalAPIItem, ExternalAPIListResponse } from '@/models/datasets'
import { fetchExternalAPIList } from '@/service/datasets'
import { createCtx } from '@/utils/context'
type ExternalKnowledgeApiContextType = {
externalKnowledgeApiList: ExternalAPIItem[]
@ -12,7 +13,7 @@ type ExternalKnowledgeApiContextType = {
isLoading: boolean
}
const ExternalKnowledgeApiContext = createContext<ExternalKnowledgeApiContextType | undefined>(undefined)
const [,useExternalKnowledgeApi, ExternalKnowledgeApiContext] = createCtx<ExternalKnowledgeApiContextType>()
export type ExternalKnowledgeApiProviderProps = {
children: ReactNode
@ -37,10 +38,4 @@ export const ExternalKnowledgeApiProvider: FC<ExternalKnowledgeApiProviderProps>
)
}
export const useExternalKnowledgeApi = () => {
const context = useContext(ExternalKnowledgeApiContext)
if (context === undefined)
throw new Error('useExternalKnowledgeApi must be used within a ExternalKnowledgeApiProvider')
return context
}
export { useExternalKnowledgeApi }

View File

@ -1,9 +1,6 @@
import {
createContext,
useContext,
} from 'use-context-selector'
import type { Locale } from '@/i18n'
import { getLanguage } from '@/i18n/language'
import { createSelectorCtx } from '@/utils/context'
type II18NContext = {
locale: Locale
@ -11,13 +8,9 @@ type II18NContext = {
setLocaleOnClient: (_lang: Locale, _reloadPage?: boolean) => void
}
const I18NContext = createContext<II18NContext>({
locale: 'en-US',
i18n: {},
setLocaleOnClient: (_lang: Locale, _reloadPage?: boolean) => { },
})
const [, useI18N, I18NContext] = createSelectorCtx<II18NContext>()
export const useI18N = () => useContext(I18NContext)
export { useI18N }
export const useGetLanguage = () => {
const { locale } = useI18N()

View File

@ -2,7 +2,7 @@
import type { Dispatch, SetStateAction } from 'react'
import { useCallback, useState } from 'react'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { useContextSelector } from 'use-context-selector'
import { useRouter, useSearchParams } from 'next/navigation'
import AccountSetting from '@/app/components/header/account-setting'
import ApiBasedExtensionModal from '@/app/components/header/account-setting/api-based-extension-page/modal'
@ -33,6 +33,7 @@ import type { OpeningStatement } from '@/app/components/base/features/types'
import type { InputVar } from '@/app/components/workflow/types'
import type { UpdatePluginPayload } from '@/app/components/plugins/types'
import UpdatePlugin from '@/app/components/plugins/update-plugin'
import { createSelectorCtx } from '@/utils/context'
export type ModalState<T> = {
payload: T
@ -73,22 +74,9 @@ export type ModalContextState = {
}> | null>>
setShowUpdatePluginModal: Dispatch<SetStateAction<ModalState<UpdatePluginPayload> | null>>
}
const ModalContext = createContext<ModalContextState>({
setShowAccountSettingModal: () => { },
setShowApiBasedExtensionModal: () => { },
setShowModerationSettingModal: () => { },
setShowExternalDataToolModal: () => { },
setShowPricingModal: () => { },
setShowAnnotationFullModal: () => { },
setShowModelModal: () => { },
setShowExternalKnowledgeAPIModal: () => { },
setShowModelLoadBalancingModal: () => { },
setShowModelLoadBalancingEntryModal: () => { },
setShowOpeningModal: () => { },
setShowUpdatePluginModal: () => { },
})
const [,useModalContext, ModalContext] = createSelectorCtx<ModalContextState>()
export const useModalContext = () => useContext(ModalContext)
export { useModalContext }
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713

View File

@ -1,6 +1,6 @@
'use client'
import { createContext, useContext, useContextSelector } from 'use-context-selector'
import { useContextSelector } from 'use-context-selector'
import useSWR from 'swr'
import { useEffect, useState } from 'react'
import {
@ -14,10 +14,12 @@ import {
} from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { Model, ModelProvider } from '@/app/components/header/account-setting/model-provider-page/declarations'
import type { RETRIEVE_METHOD } from '@/types/app'
import { Plan, type UsagePlanInfo } from '@/app/components/billing/type'
import type { Plan } from '@/app/components/billing/type'
import type { UsagePlanInfo } from '@/app/components/billing/type'
import { fetchCurrentPlanInfo } from '@/service/billing'
import { parseCurrentPlan } from '@/app/components/billing/utils'
import { defaultPlan } from '@/app/components/billing/config'
import { createSelectorCtx } from '@/utils/context'
type ProviderContextState = {
modelProviders: ModelProvider[]
@ -36,37 +38,9 @@ type ProviderContextState = {
modelLoadBalancingEnabled: boolean
datasetOperatorEnabled: boolean
}
const ProviderContext = createContext<ProviderContextState>({
modelProviders: [],
textGenerationModelList: [],
supportRetrievalMethods: [],
isAPIKeySet: true,
plan: {
type: Plan.sandbox,
usage: {
vectorSpace: 32,
buildApps: 12,
teamMembers: 1,
annotatedResponse: 1,
documentsUploadQuota: 50,
},
total: {
vectorSpace: 200,
buildApps: 50,
teamMembers: 1,
annotatedResponse: 10,
documentsUploadQuota: 500,
},
},
isFetchedPlan: false,
enableBilling: false,
onPlanInfoChanged: () => { },
enableReplaceWebAppLogo: false,
modelLoadBalancingEnabled: false,
datasetOperatorEnabled: false,
})
const [, useProviderContext, ProviderContext] = createSelectorCtx<ProviderContextState>()
export const useProviderContext = () => useContext(ProviderContext)
export { useProviderContext }
// Adding a dangling comma to avoid the generic parsing issue in tsx, see:
// https://github.com/microsoft/TypeScript/issues/15713

View File

@ -1,17 +1,15 @@
'use client'
import { createContext, useContext } from 'use-context-selector'
import useSWR from 'swr'
import { fetchWorkspaces } from '@/service/common'
import type { IWorkspace } from '@/models/common'
import { createSelectorCtx } from '@/utils/context'
export type WorkspacesContextValue = {
workspaces: IWorkspace[]
}
const WorkspacesContext = createContext<WorkspacesContextValue>({
workspaces: [],
})
const [, useWorkspacesContext, WorkspacesContext] = createSelectorCtx<WorkspacesContextValue>()
type IWorkspaceProviderProps = {
children: React.ReactNode
@ -31,6 +29,6 @@ export const WorkspaceProvider = ({
)
}
export const useWorkspacesContext = () => useContext(WorkspacesContext)
export { useWorkspacesContext }
export default WorkspacesContext

45
web/utils/context.ts Normal file
View File

@ -0,0 +1,45 @@
import { type Context, type Provider, createContext, useContext } from 'react'
import * as selector from 'use-context-selector'
const createCreateCtxFunction = (
useContextImpl: typeof useContext,
createContextImpl: typeof createContext) => {
return function<T>({ name, defaultValue }: CreateCtxOptions<T> = {}): CreateCtxReturn<T> {
const emptySymbol = Symbol(`empty ${name}`)
// @ts-expect-error it's ok here
const context = createContextImpl<T>(defaultValue ?? emptySymbol)
const useContextValue = () => {
const ctx = useContextImpl(context)
if (ctx === emptySymbol)
throw new Error(`No ${name ?? 'related'} context found.`)
return ctx
}
const result = [context.Provider, useContextValue, context] as CreateCtxReturn<T>
result.context = context
result.provider = context.Provider
result.useContextValue = useContextValue
return result
}
}
type CreateCtxOptions<T> = {
defaultValue?: T
name?: string
}
type CreateCtxReturn<T> = [Provider<T>, () => T, Context<T>] & {
context: Context<T>;
provider: Provider<T>;
useContextValue: () => T;
}
// example
// const [AppProvider, useApp, AppContext] = createCtx<AppContextValue>()
export const createCtx = createCreateCtxFunction(useContext, createContext)
export const createSelectorCtx = createCreateCtxFunction(
selector.useContext,
selector.createContext as typeof createContext,
)