| import type { FC } from 'react' | |||||
| import { useTranslation } from 'react-i18next' | |||||
| import { memo } from 'react' | |||||
| type InputProps = { | |||||
| form: any | |||||
| value: string | |||||
| onChange: (variable: string, value: string) => void | |||||
| } | |||||
| const FormInput: FC<InputProps> = ({ | |||||
| form, | |||||
| value, | |||||
| onChange, | |||||
| }) => { | |||||
| const { t } = useTranslation() | |||||
| const { | |||||
| type, | |||||
| label, | |||||
| required, | |||||
| max_length, | |||||
| variable, | |||||
| } = form | |||||
| if (type === 'paragraph') { | |||||
| return ( | |||||
| <textarea | |||||
| value={value} | |||||
| className='grow h-[104px] rounded-lg bg-gray-100 px-2.5 py-2 outline-none appearance-none resize-none' | |||||
| onChange={e => onChange(variable, e.target.value)} | |||||
| placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`} | |||||
| /> | |||||
| ) | |||||
| } | |||||
| return ( | |||||
| <input | |||||
| className='grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none' | |||||
| value={value || ''} | |||||
| maxLength={max_length} | |||||
| onChange={e => onChange(variable, e.target.value)} | |||||
| placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`} | |||||
| /> | |||||
| ) | |||||
| } | |||||
| export default memo(FormInput) |
| import { useCallback } from 'react' | |||||
| import { useTranslation } from 'react-i18next' | import { useTranslation } from 'react-i18next' | ||||
| import { useChatWithHistoryContext } from '../context' | import { useChatWithHistoryContext } from '../context' | ||||
| import Input from './form-input' | |||||
| import { PortalSelect } from '@/app/components/base/select' | import { PortalSelect } from '@/app/components/base/select' | ||||
| const Form = () => { | const Form = () => { | ||||
| isMobile, | isMobile, | ||||
| } = useChatWithHistoryContext() | } = useChatWithHistoryContext() | ||||
| const handleFormChange = (variable: string, value: string) => { | |||||
| const handleFormChange = useCallback((variable: string, value: string) => { | |||||
| handleNewConversationInputsChange({ | handleNewConversationInputsChange({ | ||||
| ...newConversationInputs, | ...newConversationInputs, | ||||
| [variable]: value, | [variable]: value, | ||||
| }) | }) | ||||
| } | |||||
| }, [newConversationInputs, handleNewConversationInputsChange]) | |||||
| const renderField = (form: any) => { | const renderField = (form: any) => { | ||||
| const { | const { | ||||
| label, | label, | ||||
| required, | required, | ||||
| max_length, | |||||
| variable, | variable, | ||||
| options, | options, | ||||
| } = form | } = form | ||||
| if (form.type === 'text-input') { | |||||
| return ( | |||||
| <input | |||||
| className='grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none' | |||||
| value={newConversationInputs[variable] || ''} | |||||
| maxLength={max_length} | |||||
| onChange={e => handleFormChange(variable, e.target.value)} | |||||
| placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`} | |||||
| /> | |||||
| ) | |||||
| } | |||||
| if (form.type === 'paragraph') { | |||||
| if (form.type === 'text-input' || form.type === 'paragraph') { | |||||
| return ( | return ( | ||||
| <textarea | |||||
| <Input | |||||
| form={form} | |||||
| value={newConversationInputs[variable]} | value={newConversationInputs[variable]} | ||||
| className='grow h-[104px] rounded-lg bg-gray-100 px-2.5 py-2 outline-none appearance-none resize-none' | |||||
| onChange={e => handleFormChange(variable, e.target.value)} | |||||
| placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`} | |||||
| onChange={handleFormChange} | |||||
| /> | /> | ||||
| ) | ) | ||||
| } | } | ||||
| return ( | return ( | ||||
| <PortalSelect | <PortalSelect | ||||
| popupClassName='w-[200px]' | popupClassName='w-[200px]' |
| } from '@/models/share' | } from '@/models/share' | ||||
| export type ChatWithHistoryContextValue = { | export type ChatWithHistoryContextValue = { | ||||
| appInfoError?: any | |||||
| appInfoLoading?: boolean | appInfoLoading?: boolean | ||||
| appMeta?: AppMeta | appMeta?: AppMeta | ||||
| appData?: AppData | appData?: AppData |
| export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | ||||
| const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]) | const isInstalledApp = useMemo(() => !!installedAppInfo, [installedAppInfo]) | ||||
| const { data: appInfo, isLoading: appInfoLoading } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo) | |||||
| const { data: appInfo, isLoading: appInfoLoading, error: appInfoError } = useSWR(installedAppInfo ? null : 'appInfo', fetchAppInfo) | |||||
| const appData = useMemo(() => { | const appData = useMemo(() => { | ||||
| if (isInstalledApp) { | if (isInstalledApp) { | ||||
| }, [isInstalledApp, appId, t, notify]) | }, [isInstalledApp, appId, t, notify]) | ||||
| return { | return { | ||||
| appInfoError, | |||||
| appInfoLoading, | appInfoLoading, | ||||
| isInstalledApp, | isInstalledApp, | ||||
| appId, | appId, |
| import Loading from '@/app/components/base/loading' | import Loading from '@/app/components/base/loading' | ||||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | ||||
| import { checkOrSetAccessToken } from '@/app/components/share/utils' | import { checkOrSetAccessToken } from '@/app/components/share/utils' | ||||
| import AppUnavailable from '@/app/components/base/app-unavailable' | |||||
| type ChatWithHistoryProps = { | type ChatWithHistoryProps = { | ||||
| className?: string | className?: string | ||||
| className, | className, | ||||
| }) => { | }) => { | ||||
| const { | const { | ||||
| appInfoError, | |||||
| appData, | appData, | ||||
| appInfoLoading, | appInfoLoading, | ||||
| appPrevChatList, | appPrevChatList, | ||||
| ) | ) | ||||
| } | } | ||||
| if (appInfoError) { | |||||
| return ( | |||||
| <AppUnavailable /> | |||||
| ) | |||||
| } | |||||
| return ( | return ( | ||||
| <div className={`h-full flex bg-white ${className} ${isMobile && 'flex-col'}`}> | <div className={`h-full flex bg-white ${className} ${isMobile && 'flex-col'}`}> | ||||
| { | { | ||||
| const isMobile = media === MediaType.mobile | const isMobile = media === MediaType.mobile | ||||
| const { | const { | ||||
| appInfoError, | |||||
| appInfoLoading, | appInfoLoading, | ||||
| appData, | appData, | ||||
| appParams, | appParams, | ||||
| return ( | return ( | ||||
| <ChatWithHistoryContext.Provider value={{ | <ChatWithHistoryContext.Provider value={{ | ||||
| appInfoError, | |||||
| appInfoLoading, | appInfoLoading, | ||||
| appData, | appData, | ||||
| appParams, | appParams, | ||||
| className, | className, | ||||
| }) => { | }) => { | ||||
| const [inited, setInited] = useState(false) | const [inited, setInited] = useState(false) | ||||
| const [appUnavailable, setAppUnavailable] = useState<boolean>(false) | |||||
| const [isUnknwonReason, setIsUnknwonReason] = useState<boolean>(false) | |||||
| useAsyncEffect(async () => { | useAsyncEffect(async () => { | ||||
| if (!inited) { | if (!inited) { | ||||
| if (!installedAppInfo) | |||||
| await checkOrSetAccessToken() | |||||
| if (!installedAppInfo) { | |||||
| try { | |||||
| await checkOrSetAccessToken() | |||||
| } | |||||
| catch (e: any) { | |||||
| if (e.status === 404) { | |||||
| setAppUnavailable(true) | |||||
| } | |||||
| else { | |||||
| setIsUnknwonReason(true) | |||||
| setAppUnavailable(true) | |||||
| } | |||||
| } | |||||
| } | |||||
| setInited(true) | setInited(true) | ||||
| } | } | ||||
| }, []) | }, []) | ||||
| if (appUnavailable) | |||||
| return <AppUnavailable isUnknwonReason={isUnknwonReason} /> | |||||
| if (!inited) | if (!inited) | ||||
| return null | return null | ||||