| @@ -0,0 +1,46 @@ | |||
| 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) | |||
| @@ -1,5 +1,7 @@ | |||
| import { useCallback } from 'react' | |||
| import { useTranslation } from 'react-i18next' | |||
| import { useChatWithHistoryContext } from '../context' | |||
| import Input from './form-input' | |||
| import { PortalSelect } from '@/app/components/base/select' | |||
| const Form = () => { | |||
| @@ -11,43 +13,31 @@ const Form = () => { | |||
| isMobile, | |||
| } = useChatWithHistoryContext() | |||
| const handleFormChange = (variable: string, value: string) => { | |||
| const handleFormChange = useCallback((variable: string, value: string) => { | |||
| handleNewConversationInputsChange({ | |||
| ...newConversationInputs, | |||
| [variable]: value, | |||
| }) | |||
| } | |||
| }, [newConversationInputs, handleNewConversationInputsChange]) | |||
| const renderField = (form: any) => { | |||
| const { | |||
| label, | |||
| required, | |||
| max_length, | |||
| variable, | |||
| options, | |||
| } = 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 ( | |||
| <textarea | |||
| <Input | |||
| form={form} | |||
| 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 ( | |||
| <PortalSelect | |||
| popupClassName='w-[200px]' | |||
| @@ -16,6 +16,7 @@ import type { | |||
| } from '@/models/share' | |||
| export type ChatWithHistoryContextValue = { | |||
| appInfoError?: any | |||
| appInfoLoading?: boolean | |||
| appMeta?: AppMeta | |||
| appData?: AppData | |||
| @@ -40,7 +40,7 @@ import { changeLanguage } from '@/i18n/i18next-config' | |||
| export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| 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(() => { | |||
| if (isInstalledApp) { | |||
| @@ -350,6 +350,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => { | |||
| }, [isInstalledApp, appId, t, notify]) | |||
| return { | |||
| appInfoError, | |||
| appInfoLoading, | |||
| isInstalledApp, | |||
| appId, | |||
| @@ -17,6 +17,7 @@ import type { InstalledApp } from '@/models/explore' | |||
| import Loading from '@/app/components/base/loading' | |||
| import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' | |||
| import { checkOrSetAccessToken } from '@/app/components/share/utils' | |||
| import AppUnavailable from '@/app/components/base/app-unavailable' | |||
| type ChatWithHistoryProps = { | |||
| className?: string | |||
| @@ -25,6 +26,7 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({ | |||
| className, | |||
| }) => { | |||
| const { | |||
| appInfoError, | |||
| appData, | |||
| appInfoLoading, | |||
| appPrevChatList, | |||
| @@ -53,6 +55,12 @@ const ChatWithHistory: FC<ChatWithHistoryProps> = ({ | |||
| ) | |||
| } | |||
| if (appInfoError) { | |||
| return ( | |||
| <AppUnavailable /> | |||
| ) | |||
| } | |||
| return ( | |||
| <div className={`h-full flex bg-white ${className} ${isMobile && 'flex-col'}`}> | |||
| { | |||
| @@ -100,6 +108,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({ | |||
| const isMobile = media === MediaType.mobile | |||
| const { | |||
| appInfoError, | |||
| appInfoLoading, | |||
| appData, | |||
| appParams, | |||
| @@ -132,6 +141,7 @@ const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({ | |||
| return ( | |||
| <ChatWithHistoryContext.Provider value={{ | |||
| appInfoError, | |||
| appInfoLoading, | |||
| appData, | |||
| appParams, | |||
| @@ -172,15 +182,32 @@ const ChatWithHistoryWrapWithCheckToken: FC<ChatWithHistoryWrapProps> = ({ | |||
| className, | |||
| }) => { | |||
| const [inited, setInited] = useState(false) | |||
| const [appUnavailable, setAppUnavailable] = useState<boolean>(false) | |||
| const [isUnknwonReason, setIsUnknwonReason] = useState<boolean>(false) | |||
| useAsyncEffect(async () => { | |||
| 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) | |||
| } | |||
| }, []) | |||
| if (appUnavailable) | |||
| return <AppUnavailable isUnknwonReason={isUnknwonReason} /> | |||
| if (!inited) | |||
| return null | |||