index.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import {
  2. useEffect,
  3. useState,
  4. } from 'react'
  5. import { useAsyncEffect } from 'ahooks'
  6. import { useTranslation } from 'react-i18next'
  7. import { RiLoopLeftLine } from '@remixicon/react'
  8. import {
  9. EmbeddedChatbotContext,
  10. useEmbeddedChatbotContext,
  11. } from './context'
  12. import { useEmbeddedChatbot } from './hooks'
  13. import { isDify } from './utils'
  14. import { useThemeContext } from './theme/theme-context'
  15. import cn from '@/utils/classnames'
  16. import { checkOrSetAccessToken } from '@/app/components/share/utils'
  17. import AppUnavailable from '@/app/components/base/app-unavailable'
  18. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  19. import Loading from '@/app/components/base/loading'
  20. import LogoHeader from '@/app/components/base/logo/logo-embedded-chat-header'
  21. import Header from '@/app/components/base/chat/embedded-chatbot/header'
  22. import ConfigPanel from '@/app/components/base/chat/embedded-chatbot/config-panel'
  23. import ChatWrapper from '@/app/components/base/chat/embedded-chatbot/chat-wrapper'
  24. import Tooltip from '@/app/components/base/tooltip'
  25. const Chatbot = () => {
  26. const { t } = useTranslation()
  27. const {
  28. isMobile,
  29. appInfoError,
  30. appInfoLoading,
  31. appData,
  32. appPrevChatList,
  33. showConfigPanelBeforeChat,
  34. appChatListDataLoading,
  35. handleNewConversation,
  36. themeBuilder,
  37. } = useEmbeddedChatbotContext()
  38. const chatReady = (!showConfigPanelBeforeChat || !!appPrevChatList.length)
  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. if (site) {
  45. if (customConfig)
  46. document.title = `${site.title}`
  47. else
  48. document.title = `${site.title} - Powered by Dify`
  49. }
  50. }, [site, customConfig, themeBuilder])
  51. if (appInfoLoading) {
  52. return (
  53. <Loading type='app' />
  54. )
  55. }
  56. if (appInfoError) {
  57. return (
  58. <AppUnavailable />
  59. )
  60. }
  61. return (
  62. <div>
  63. <Header
  64. isMobile={isMobile}
  65. title={site?.title || ''}
  66. customerIcon={isDify() ? difyIcon : ''}
  67. theme={themeBuilder?.theme}
  68. onCreateNewChat={handleNewConversation}
  69. />
  70. <div className='flex bg-white overflow-hidden'>
  71. <div className={cn('h-[100vh] grow flex flex-col overflow-y-auto', isMobile && '!h-[calc(100vh_-_3rem)]')}>
  72. {showConfigPanelBeforeChat && !appChatListDataLoading && !appPrevChatList.length && (
  73. <div className={cn('flex w-full items-center justify-center h-full tablet:px-4', isMobile && 'px-4')}>
  74. <ConfigPanel />
  75. </div>
  76. )}
  77. {appChatListDataLoading && chatReady && (
  78. <Loading type='app' />
  79. )}
  80. {chatReady && !appChatListDataLoading && (
  81. <div className='relative h-full pt-8 mx-auto w-full max-w-[720px]'>
  82. {!isMobile && (
  83. <div className='absolute top-2.5 right-3 z-20'>
  84. <Tooltip
  85. popupContent={t('share.chat.resetChat')}
  86. >
  87. <div className='p-1.5 bg-white border-[0.5px] border-gray-100 rounded-lg shadow-md cursor-pointer' onClick={handleNewConversation}>
  88. <RiLoopLeftLine className="h-4 w-4 text-gray-500"/>
  89. </div>
  90. </Tooltip>
  91. </div>
  92. )}
  93. <ChatWrapper />
  94. </div>
  95. )}
  96. </div>
  97. </div>
  98. </div>
  99. )
  100. }
  101. const EmbeddedChatbotWrapper = () => {
  102. const media = useBreakpoints()
  103. const isMobile = media === MediaType.mobile
  104. const themeBuilder = useThemeContext()
  105. const {
  106. appInfoError,
  107. appInfoLoading,
  108. appData,
  109. appParams,
  110. appMeta,
  111. appChatListDataLoading,
  112. currentConversationId,
  113. currentConversationItem,
  114. appPrevChatList,
  115. pinnedConversationList,
  116. conversationList,
  117. showConfigPanelBeforeChat,
  118. newConversationInputs,
  119. newConversationInputsRef,
  120. handleNewConversationInputsChange,
  121. inputsForms,
  122. handleNewConversation,
  123. handleStartChat,
  124. handleChangeConversation,
  125. handleNewConversationCompleted,
  126. chatShouldReloadKey,
  127. isInstalledApp,
  128. appId,
  129. handleFeedback,
  130. currentChatInstanceRef,
  131. } = useEmbeddedChatbot()
  132. return <EmbeddedChatbotContext.Provider value={{
  133. appInfoError,
  134. appInfoLoading,
  135. appData,
  136. appParams,
  137. appMeta,
  138. appChatListDataLoading,
  139. currentConversationId,
  140. currentConversationItem,
  141. appPrevChatList,
  142. pinnedConversationList,
  143. conversationList,
  144. showConfigPanelBeforeChat,
  145. newConversationInputs,
  146. newConversationInputsRef,
  147. handleNewConversationInputsChange,
  148. inputsForms,
  149. handleNewConversation,
  150. handleStartChat,
  151. handleChangeConversation,
  152. handleNewConversationCompleted,
  153. chatShouldReloadKey,
  154. isMobile,
  155. isInstalledApp,
  156. appId,
  157. handleFeedback,
  158. currentChatInstanceRef,
  159. themeBuilder,
  160. }}>
  161. <Chatbot />
  162. </EmbeddedChatbotContext.Provider>
  163. }
  164. const EmbeddedChatbot = () => {
  165. const [initialized, setInitialized] = useState(false)
  166. const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
  167. const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
  168. useAsyncEffect(async () => {
  169. if (!initialized) {
  170. try {
  171. await checkOrSetAccessToken()
  172. }
  173. catch (e: any) {
  174. if (e.status === 404) {
  175. setAppUnavailable(true)
  176. }
  177. else {
  178. setIsUnknownReason(true)
  179. setAppUnavailable(true)
  180. }
  181. }
  182. setInitialized(true)
  183. }
  184. }, [])
  185. if (!initialized)
  186. return null
  187. if (appUnavailable)
  188. return <AppUnavailable isUnknownReason={isUnknownReason} />
  189. return <EmbeddedChatbotWrapper />
  190. }
  191. export default EmbeddedChatbot