Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import {
  2. useEffect,
  3. useState,
  4. } from 'react'
  5. import { useAsyncEffect } from 'ahooks'
  6. import { useTranslation } from 'react-i18next'
  7. import {
  8. EmbeddedChatbotContext,
  9. useEmbeddedChatbotContext,
  10. } from './context'
  11. import { useEmbeddedChatbot } from './hooks'
  12. import { isDify } from './utils'
  13. import { useThemeContext } from './theme/theme-context'
  14. import { CssTransform } from './theme/utils'
  15. import { checkOrSetAccessToken } from '@/app/components/share/utils'
  16. import AppUnavailable from '@/app/components/base/app-unavailable'
  17. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  18. import Loading from '@/app/components/base/loading'
  19. import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
  20. import Header from '@/app/components/base/chat/embedded-chatbot/header'
  21. import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper'
  22. import DifyLogo from '@/app/components/base/logo/dify-logo'
  23. import cn from '@/utils/classnames'
  24. import useDocumentTitle from '@/hooks/use-document-title'
  25. const Chatbot = () => {
  26. const {
  27. userCanAccess,
  28. isMobile,
  29. allowResetChat,
  30. appInfoError,
  31. appInfoLoading,
  32. appData,
  33. appChatListDataLoading,
  34. chatShouldReloadKey,
  35. handleNewConversation,
  36. themeBuilder,
  37. } = useEmbeddedChatbotContext()
  38. const { t } = useTranslation()
  39. const customConfig = appData?.custom_config
  40. const site = appData?.site
  41. const difyIcon = <LogoHeader />
  42. useEffect(() => {
  43. themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted)
  44. }, [site, customConfig, themeBuilder])
  45. useDocumentTitle(site?.title || 'Chat')
  46. if (appInfoLoading) {
  47. return (
  48. <>
  49. {!isMobile && <Loading type='app' />}
  50. {isMobile && (
  51. <div className={cn('relative')}>
  52. <div className={cn('flex h-[calc(100vh_-_60px)] flex-col rounded-2xl border-[0.5px] border-components-panel-border shadow-xs')}>
  53. <Loading type='app' />
  54. </div>
  55. </div>
  56. )}
  57. </>
  58. )
  59. }
  60. if (!userCanAccess)
  61. return <AppUnavailable code={403} unknownReason='no permission.' />
  62. if (appInfoError) {
  63. return (
  64. <>
  65. {!isMobile && <AppUnavailable />}
  66. {isMobile && (
  67. <div className={cn('relative')}>
  68. <div className={cn('flex h-[calc(100vh_-_60px)] flex-col rounded-2xl border-[0.5px] border-components-panel-border shadow-xs')}>
  69. <AppUnavailable />
  70. </div>
  71. </div>
  72. )}
  73. </>
  74. )
  75. }
  76. return (
  77. <div className='relative'>
  78. <div
  79. className={cn(
  80. 'flex flex-col rounded-2xl border border-components-panel-border-subtle',
  81. isMobile ? 'h-[calc(100vh_-_60px)] border-[0.5px] border-components-panel-border shadow-xs' : 'h-[100vh] bg-chatbot-bg',
  82. )}
  83. style={isMobile ? Object.assign({}, CssTransform(themeBuilder?.theme?.backgroundHeaderColorStyle ?? '')) : {}}
  84. >
  85. <Header
  86. isMobile={isMobile}
  87. allowResetChat={allowResetChat}
  88. title={site?.title || ''}
  89. customerIcon={isDify() ? difyIcon : ''}
  90. theme={themeBuilder?.theme}
  91. onCreateNewChat={handleNewConversation}
  92. />
  93. <div className={cn('flex grow flex-col overflow-y-auto', isMobile && '!h-[calc(100vh_-_3rem)] rounded-2xl bg-chatbot-bg')}>
  94. {appChatListDataLoading && (
  95. <Loading type='app' />
  96. )}
  97. {!appChatListDataLoading && (
  98. <ChatWrapper key={chatShouldReloadKey} />
  99. )}
  100. </div>
  101. </div>
  102. {/* powered by */}
  103. {isMobile && (
  104. <div className='flex h-[60px] shrink-0 items-center pl-2'>
  105. {!appData?.custom_config?.remove_webapp_brand && (
  106. <div className={cn(
  107. 'flex shrink-0 items-center gap-1.5 px-2',
  108. )}>
  109. <div className='system-2xs-medium-uppercase text-text-tertiary'>{t('share.chat.poweredBy')}</div>
  110. {appData?.custom_config?.replace_webapp_logo && (
  111. <img src={appData?.custom_config?.replace_webapp_logo} alt='logo' className='block h-5 w-auto' />
  112. )}
  113. {!appData?.custom_config?.replace_webapp_logo && (
  114. <DifyLogo size='small' />
  115. )}
  116. </div>
  117. )}
  118. </div>
  119. )}
  120. </div>
  121. )
  122. }
  123. const EmbeddedChatbotWrapper = () => {
  124. const media = useBreakpoints()
  125. const isMobile = media === MediaType.mobile
  126. const themeBuilder = useThemeContext()
  127. const {
  128. appInfoError,
  129. appInfoLoading,
  130. appData,
  131. accessMode,
  132. userCanAccess,
  133. appParams,
  134. appMeta,
  135. appChatListDataLoading,
  136. currentConversationId,
  137. currentConversationItem,
  138. appPrevChatList,
  139. pinnedConversationList,
  140. conversationList,
  141. newConversationInputs,
  142. newConversationInputsRef,
  143. handleNewConversationInputsChange,
  144. inputsForms,
  145. handleNewConversation,
  146. handleStartChat,
  147. handleChangeConversation,
  148. handleNewConversationCompleted,
  149. chatShouldReloadKey,
  150. isInstalledApp,
  151. allowResetChat,
  152. appId,
  153. handleFeedback,
  154. currentChatInstanceRef,
  155. clearChatList,
  156. setClearChatList,
  157. isResponding,
  158. setIsResponding,
  159. currentConversationInputs,
  160. setCurrentConversationInputs,
  161. } = useEmbeddedChatbot()
  162. return <EmbeddedChatbotContext.Provider value={{
  163. userCanAccess,
  164. accessMode,
  165. appInfoError,
  166. appInfoLoading,
  167. appData,
  168. appParams,
  169. appMeta,
  170. appChatListDataLoading,
  171. currentConversationId,
  172. currentConversationItem,
  173. appPrevChatList,
  174. pinnedConversationList,
  175. conversationList,
  176. newConversationInputs,
  177. newConversationInputsRef,
  178. handleNewConversationInputsChange,
  179. inputsForms,
  180. handleNewConversation,
  181. handleStartChat,
  182. handleChangeConversation,
  183. handleNewConversationCompleted,
  184. chatShouldReloadKey,
  185. isMobile,
  186. isInstalledApp,
  187. allowResetChat,
  188. appId,
  189. handleFeedback,
  190. currentChatInstanceRef,
  191. themeBuilder,
  192. clearChatList,
  193. setClearChatList,
  194. isResponding,
  195. setIsResponding,
  196. currentConversationInputs,
  197. setCurrentConversationInputs,
  198. }}>
  199. <Chatbot />
  200. </EmbeddedChatbotContext.Provider>
  201. }
  202. const EmbeddedChatbot = () => {
  203. const [initialized, setInitialized] = useState(false)
  204. const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
  205. const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
  206. useAsyncEffect(async () => {
  207. if (!initialized) {
  208. try {
  209. await checkOrSetAccessToken()
  210. }
  211. catch (e: any) {
  212. if (e.status === 404) {
  213. setAppUnavailable(true)
  214. }
  215. else {
  216. setIsUnknownReason(true)
  217. setAppUnavailable(true)
  218. }
  219. }
  220. setInitialized(true)
  221. }
  222. }, [])
  223. if (!initialized)
  224. return null
  225. if (appUnavailable)
  226. return <AppUnavailable isUnknownReason={isUnknownReason} />
  227. return <EmbeddedChatbotWrapper />
  228. }
  229. export default EmbeddedChatbot