Skip to content

Commit

Permalink
chore: replace chat in web app (langgenius#2373)
Browse files Browse the repository at this point in the history
  • Loading branch information
zxhlyh authored Feb 4, 2024
1 parent 3f0c515 commit 51d3592
Show file tree
Hide file tree
Showing 49 changed files with 2,100 additions and 92 deletions.
6 changes: 4 additions & 2 deletions web/app/(shareLayout)/chat/[token]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
'use client'

import type { FC } from 'react'
import React from 'react'

import type { IMainProps } from '@/app/components/share/chat'
import Main from '@/app/components/share/chat'
import ChatWithHistoryWrap from '@/app/components/base/chat/chat-with-history'

const Chat: FC<IMainProps> = () => {
return (
<Main />
<ChatWithHistoryWrap />
)
}

Expand Down
141 changes: 141 additions & 0 deletions web/app/components/base/chat/chat-with-history/chat-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { useCallback, useEffect, useMemo } from 'react'
import Chat from '../chat'
import type {
ChatConfig,
OnSend,
} from '../types'
import { useChat } from '../chat/hooks'
import { useChatWithHistoryContext } from './context'
import Header from './header'
import ConfigPanel from './config-panel'
import {
fetchSuggestedQuestions,
getUrl,
} from '@/service/share'

const ChatWrapper = () => {
const {
appParams,
appPrevChatList,
currentConversationId,
currentConversationItem,
inputsForms,
newConversationInputs,
handleNewConversationCompleted,
isMobile,
isInstalledApp,
appId,
appMeta,
handleFeedback,
currentChatInstanceRef,
} = useChatWithHistoryContext()
const appConfig = useMemo(() => {
const config = appParams || {}

return {
...config,
supportFeedback: true,
} as ChatConfig
}, [appParams])
const {
chatList,
handleSend,
handleStop,
isResponsing,
suggestedQuestions,
} = useChat(
appConfig,
undefined,
appPrevChatList,
)

useEffect(() => {
if (currentChatInstanceRef.current)
currentChatInstanceRef.current.handleStop = handleStop
}, [])

const doSend: OnSend = useCallback((message, files) => {
const data: any = {
query: message,
inputs: currentConversationId ? currentConversationItem?.inputs : newConversationInputs,
conversation_id: currentConversationId,
}

if (appConfig?.file_upload?.image.enabled && files?.length)
data.files = files

handleSend(
getUrl('chat-messages', isInstalledApp, appId || ''),
data,
{
onGetSuggestedQuestions: responseItemId => fetchSuggestedQuestions(responseItemId, isInstalledApp, appId),
onConversationComplete: currentConversationId ? undefined : handleNewConversationCompleted,
isPublicAPI: !isInstalledApp,
},
)
}, [
appConfig,
currentConversationId,
currentConversationItem,
handleSend,
newConversationInputs,
handleNewConversationCompleted,
isInstalledApp,
appId,
])
const chatNode = useMemo(() => {
if (inputsForms.length) {
return (
<>
<Header
isMobile={isMobile}
title={currentConversationItem?.name || ''}
/>
{
!currentConversationId && (
<div className={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}>
<div className='mb-6' />
<ConfigPanel />
<div
className='my-6 h-[1px]'
style={{ background: 'linear-gradient(90deg, rgba(242, 244, 247, 0.00) 0%, #F2F4F7 49.17%, rgba(242, 244, 247, 0.00) 100%)' }}
/>
</div>
)
}
</>
)
}

return (
<Header
isMobile={isMobile}
title={currentConversationItem?.name || ''}
/>
)
}, [
currentConversationId,
inputsForms,
currentConversationItem,
isMobile,
])

return (
<Chat
config={appConfig}
chatList={chatList}
isResponsing={isResponsing}
chatContainerInnerClassName={`mx-auto pt-6 w-full max-w-[720px] ${isMobile && 'px-4'}`}
chatFooterClassName='pb-4'
chatFooterInnerClassName={`mx-auto w-full max-w-[720px] ${isMobile && 'px-4'}`}
onSend={doSend}
onStopResponding={handleStop}
chatNode={chatNode}
allToolIcons={appMeta?.tool_icons || {}}
onFeedback={handleFeedback}
suggestedQuestions={suggestedQuestions}
/>
)
}

export default ChatWrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { useTranslation } from 'react-i18next'
import { useChatWithHistoryContext } from '../context'
import { PortalSelect } from '@/app/components/base/select'

const Form = () => {
const { t } = useTranslation()
const {
inputsForms,
newConversationInputs,
handleNewConversationInputsChange,
isMobile,
} = useChatWithHistoryContext()

const handleFormChange = (variable: string, value: string) => {
handleNewConversationInputsChange({
...newConversationInputs,
[variable]: value,
})
}

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') {
return (
<textarea
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')})` : ''}`}
/>
)
}
return (
<PortalSelect
popupClassName='w-[200px]'
value={newConversationInputs[variable]}
items={options.map((option: string) => ({ value: option, name: option }))}
onSelect={item => handleFormChange(variable, item.value as string)}
placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
/>
)
}

if (!inputsForms.length)
return null

return (
<div className='mb-4 py-2'>
{
inputsForms.map(form => (
<div
key={form.variable}
className={`flex mb-3 last-of-type:mb-0 text-sm text-gray-900 ${isMobile && '!flex-wrap'}`}
>
<div className={`shrink-0 mr-2 py-2 w-[128px] ${isMobile && '!w-full'}`}>{form.label}</div>
{renderField(form)}
</div>
))
}
</div>
)
}

export default Form
Loading

0 comments on commit 51d3592

Please sign in to comment.