You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.tsx 7.2KB

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