index.tsx 5.8 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-embeded-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. selector={'embed-scene-restart-button'}
  86. htmlContent={t('share.chat.resetChat')}
  87. position='top'
  88. >
  89. <div className='p-1.5 bg-white border-[0.5px] border-gray-100 rounded-lg shadow-md cursor-pointer' onClick={handleNewConversation}>
  90. <RiLoopLeftLine className="h-4 w-4 text-gray-500"/>
  91. </div>
  92. </Tooltip>
  93. </div>
  94. )}
  95. <ChatWrapper />
  96. </div>
  97. )}
  98. </div>
  99. </div>
  100. </div>
  101. )
  102. }
  103. const EmbeddedChatbotWrapper = () => {
  104. const media = useBreakpoints()
  105. const isMobile = media === MediaType.mobile
  106. const themeBuilder = useThemeContext()
  107. const {
  108. appInfoError,
  109. appInfoLoading,
  110. appData,
  111. appParams,
  112. appMeta,
  113. appChatListDataLoading,
  114. currentConversationId,
  115. currentConversationItem,
  116. appPrevChatList,
  117. pinnedConversationList,
  118. conversationList,
  119. showConfigPanelBeforeChat,
  120. newConversationInputs,
  121. handleNewConversationInputsChange,
  122. inputsForms,
  123. handleNewConversation,
  124. handleStartChat,
  125. handleChangeConversation,
  126. handleNewConversationCompleted,
  127. chatShouldReloadKey,
  128. isInstalledApp,
  129. appId,
  130. handleFeedback,
  131. currentChatInstanceRef,
  132. } = useEmbeddedChatbot()
  133. return <EmbeddedChatbotContext.Provider value={{
  134. appInfoError,
  135. appInfoLoading,
  136. appData,
  137. appParams,
  138. appMeta,
  139. appChatListDataLoading,
  140. currentConversationId,
  141. currentConversationItem,
  142. appPrevChatList,
  143. pinnedConversationList,
  144. conversationList,
  145. showConfigPanelBeforeChat,
  146. newConversationInputs,
  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