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 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. 'use client'
  2. import type { FC } from 'react'
  3. import {
  4. useEffect,
  5. useState,
  6. } from 'react'
  7. import { useAsyncEffect } from 'ahooks'
  8. import { useThemeContext } from '../embedded-chatbot/theme/theme-context'
  9. import {
  10. ChatWithHistoryContext,
  11. useChatWithHistoryContext,
  12. } from './context'
  13. import { useChatWithHistory } from './hooks'
  14. import Sidebar from './sidebar'
  15. import Header from './header'
  16. import HeaderInMobile from './header-in-mobile'
  17. import ChatWrapper from './chat-wrapper'
  18. import type { InstalledApp } from '@/models/explore'
  19. import Loading from '@/app/components/base/loading'
  20. import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
  21. import { checkOrSetAccessToken } from '@/app/components/share/utils'
  22. import AppUnavailable from '@/app/components/base/app-unavailable'
  23. import cn from '@/utils/classnames'
  24. import useDocumentTitle from '@/hooks/use-document-title'
  25. type ChatWithHistoryProps = {
  26. className?: string
  27. }
  28. const ChatWithHistory: FC<ChatWithHistoryProps> = ({
  29. className,
  30. }) => {
  31. const {
  32. appData,
  33. appChatListDataLoading,
  34. chatShouldReloadKey,
  35. isMobile,
  36. themeBuilder,
  37. sidebarCollapseState,
  38. } = useChatWithHistoryContext()
  39. const isSidebarCollapsed = sidebarCollapseState
  40. const customConfig = appData?.custom_config
  41. const site = appData?.site
  42. const [showSidePanel, setShowSidePanel] = useState(false)
  43. useEffect(() => {
  44. themeBuilder?.buildTheme(site?.chat_color_theme, site?.chat_color_theme_inverted)
  45. }, [site, customConfig, themeBuilder])
  46. useDocumentTitle(site?.title || 'Chat')
  47. return (
  48. <div className={cn(
  49. 'flex h-full bg-background-default-burn',
  50. isMobile && 'flex-col',
  51. className,
  52. )}>
  53. {!isMobile && (
  54. <div className={cn(
  55. 'flex w-[236px] flex-col p-1 pr-0 transition-all duration-200 ease-in-out',
  56. isSidebarCollapsed && 'w-0 overflow-hidden !p-0',
  57. )}>
  58. <Sidebar />
  59. </div>
  60. )}
  61. {isMobile && (
  62. <HeaderInMobile />
  63. )}
  64. <div className={cn('relative grow p-2', isMobile && 'h-[calc(100%_-_56px)] p-0')}>
  65. {isSidebarCollapsed && (
  66. <div
  67. className={cn(
  68. 'absolute top-0 z-20 flex h-full w-[256px] flex-col p-2 transition-all duration-500 ease-in-out',
  69. showSidePanel ? 'left-0' : 'left-[-248px]',
  70. )}
  71. onMouseEnter={() => setShowSidePanel(true)}
  72. onMouseLeave={() => setShowSidePanel(false)}
  73. >
  74. <Sidebar isPanel />
  75. </div>
  76. )}
  77. <div className={cn('flex h-full flex-col overflow-hidden border-[0,5px] border-components-panel-border-subtle bg-chatbot-bg', isMobile ? 'rounded-t-2xl' : 'rounded-2xl')}>
  78. {!isMobile && <Header />}
  79. {appChatListDataLoading && (
  80. <Loading type='app' />
  81. )}
  82. {!appChatListDataLoading && (
  83. <ChatWrapper key={chatShouldReloadKey} />
  84. )}
  85. </div>
  86. </div>
  87. </div>
  88. )
  89. }
  90. export type ChatWithHistoryWrapProps = {
  91. installedAppInfo?: InstalledApp
  92. className?: string
  93. }
  94. const ChatWithHistoryWrap: FC<ChatWithHistoryWrapProps> = ({
  95. installedAppInfo,
  96. className,
  97. }) => {
  98. const media = useBreakpoints()
  99. const isMobile = media === MediaType.mobile
  100. const themeBuilder = useThemeContext()
  101. const {
  102. appData,
  103. appParams,
  104. appMeta,
  105. appChatListDataLoading,
  106. currentConversationId,
  107. currentConversationItem,
  108. appPrevChatTree,
  109. pinnedConversationList,
  110. conversationList,
  111. newConversationInputs,
  112. newConversationInputsRef,
  113. handleNewConversationInputsChange,
  114. inputsForms,
  115. handleNewConversation,
  116. handleStartChat,
  117. handleChangeConversation,
  118. handlePinConversation,
  119. handleUnpinConversation,
  120. handleDeleteConversation,
  121. conversationRenaming,
  122. handleRenameConversation,
  123. handleNewConversationCompleted,
  124. chatShouldReloadKey,
  125. isInstalledApp,
  126. appId,
  127. handleFeedback,
  128. currentChatInstanceRef,
  129. sidebarCollapseState,
  130. handleSidebarCollapse,
  131. clearChatList,
  132. setClearChatList,
  133. isResponding,
  134. setIsResponding,
  135. currentConversationInputs,
  136. setCurrentConversationInputs,
  137. allInputsHidden,
  138. initUserVariables,
  139. } = useChatWithHistory(installedAppInfo)
  140. return (
  141. <ChatWithHistoryContext.Provider value={{
  142. appData,
  143. appParams,
  144. appMeta,
  145. appChatListDataLoading,
  146. currentConversationId,
  147. currentConversationItem,
  148. appPrevChatTree,
  149. pinnedConversationList,
  150. conversationList,
  151. newConversationInputs,
  152. newConversationInputsRef,
  153. handleNewConversationInputsChange,
  154. inputsForms,
  155. handleNewConversation,
  156. handleStartChat,
  157. handleChangeConversation,
  158. handlePinConversation,
  159. handleUnpinConversation,
  160. handleDeleteConversation,
  161. conversationRenaming,
  162. handleRenameConversation,
  163. handleNewConversationCompleted,
  164. chatShouldReloadKey,
  165. isMobile,
  166. isInstalledApp,
  167. appId,
  168. handleFeedback,
  169. currentChatInstanceRef,
  170. themeBuilder,
  171. sidebarCollapseState,
  172. handleSidebarCollapse,
  173. clearChatList,
  174. setClearChatList,
  175. isResponding,
  176. setIsResponding,
  177. currentConversationInputs,
  178. setCurrentConversationInputs,
  179. allInputsHidden,
  180. initUserVariables,
  181. }}>
  182. <ChatWithHistory className={className} />
  183. </ChatWithHistoryContext.Provider>
  184. )
  185. }
  186. const ChatWithHistoryWrapWithCheckToken: FC<ChatWithHistoryWrapProps> = ({
  187. installedAppInfo,
  188. className,
  189. }) => {
  190. const [initialized, setInitialized] = useState(false)
  191. const [appUnavailable, setAppUnavailable] = useState<boolean>(false)
  192. const [isUnknownReason, setIsUnknownReason] = useState<boolean>(false)
  193. useAsyncEffect(async () => {
  194. if (!initialized) {
  195. if (!installedAppInfo) {
  196. try {
  197. await checkOrSetAccessToken()
  198. }
  199. catch (e: any) {
  200. if (e.status === 404) {
  201. setAppUnavailable(true)
  202. }
  203. else {
  204. setIsUnknownReason(true)
  205. setAppUnavailable(true)
  206. }
  207. }
  208. }
  209. setInitialized(true)
  210. }
  211. }, [])
  212. if (!initialized)
  213. return null
  214. if (appUnavailable)
  215. return <AppUnavailable isUnknownReason={isUnknownReason} />
  216. return (
  217. <ChatWithHistoryWrap
  218. installedAppInfo={installedAppInfo}
  219. className={className}
  220. />
  221. )
  222. }
  223. export default ChatWithHistoryWrapWithCheckToken