index.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  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. handleNewConversationInputsChange,
  120. inputsForms,
  121. handleNewConversation,
  122. handleStartChat,
  123. handleChangeConversation,
  124. handleNewConversationCompleted,
  125. chatShouldReloadKey,
  126. isInstalledApp,
  127. appId,
  128. handleFeedback,
  129. currentChatInstanceRef,
  130. } = useEmbeddedChatbot()
  131. return <EmbeddedChatbotContext.Provider value={{
  132. appInfoError,
  133. appInfoLoading,
  134. appData,
  135. appParams,
  136. appMeta,
  137. appChatListDataLoading,
  138. currentConversationId,
  139. currentConversationItem,
  140. appPrevChatList,
  141. pinnedConversationList,
  142. conversationList,
  143. showConfigPanelBeforeChat,
  144. newConversationInputs,
  145. handleNewConversationInputsChange,
  146. inputsForms,
  147. handleNewConversation,
  148. handleStartChat,
  149. handleChangeConversation,
  150. handleNewConversationCompleted,
  151. chatShouldReloadKey,
  152. isMobile,
  153. isInstalledApp,
  154. appId,
  155. handleFeedback,
  156. currentChatInstanceRef,
  157. themeBuilder,
  158. }}>
  159. <Chatbot />
  160. </EmbeddedChatbotContext.Provider>
  161. }
  162. const EmbeddedChatbot = () => {
  163. const [initialized, setInitialized] = useState(false)
  164. const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
  165. const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
  166. useAsyncEffect(async () => {
  167. if (!initialized) {
  168. try {
  169. await checkOrSetAccessToken()
  170. }
  171. catch (e: any) {
  172. if (e.status === 404) {
  173. setAppUnavailable(true)
  174. }
  175. else {
  176. setIsUnknownReason(true)
  177. setAppUnavailable(true)
  178. }
  179. }
  180. setInitialized(true)
  181. }
  182. }, [])
  183. if (!initialized)
  184. return null
  185. if (appUnavailable)
  186. return <AppUnavailable isUnknownReason={isUnknownReason} />
  187. return <EmbeddedChatbotWrapper />
  188. }
  189. export default EmbeddedChatbot