index.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. import { useCallback, useState } from 'react'
  2. import {
  3. RiEditBoxLine,
  4. RiLayoutRight2Line,
  5. RiResetLeftLine,
  6. } from '@remixicon/react'
  7. import { useTranslation } from 'react-i18next'
  8. import {
  9. useChatWithHistoryContext,
  10. } from '../context'
  11. import Operation from './operation'
  12. import ActionButton, { ActionButtonState } from '@/app/components/base/action-button'
  13. import AppIcon from '@/app/components/base/app-icon'
  14. import Tooltip from '@/app/components/base/tooltip'
  15. import ViewFormDropdown from '@/app/components/base/chat/chat-with-history/inputs-form/view-form-dropdown'
  16. import Confirm from '@/app/components/base/confirm'
  17. import RenameModal from '@/app/components/base/chat/chat-with-history/sidebar/rename-modal'
  18. import type { ConversationItem } from '@/models/share'
  19. import cn from '@/utils/classnames'
  20. const Header = () => {
  21. const {
  22. appData,
  23. currentConversationId,
  24. currentConversationItem,
  25. inputsForms,
  26. pinnedConversationList,
  27. handlePinConversation,
  28. handleUnpinConversation,
  29. conversationRenaming,
  30. handleRenameConversation,
  31. handleDeleteConversation,
  32. handleNewConversation,
  33. sidebarCollapseState,
  34. handleSidebarCollapse,
  35. isResponding,
  36. } = useChatWithHistoryContext()
  37. const { t } = useTranslation()
  38. const isSidebarCollapsed = sidebarCollapseState
  39. const isPin = pinnedConversationList.some(item => item.id === currentConversationId)
  40. const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
  41. const [showRename, setShowRename] = useState<ConversationItem | null>(null)
  42. const handleOperate = useCallback((type: string) => {
  43. if (type === 'pin')
  44. handlePinConversation(currentConversationId)
  45. if (type === 'unpin')
  46. handleUnpinConversation(currentConversationId)
  47. if (type === 'delete')
  48. setShowConfirm(currentConversationItem as any)
  49. if (type === 'rename')
  50. setShowRename(currentConversationItem as any)
  51. }, [currentConversationId, currentConversationItem, handlePinConversation, handleUnpinConversation])
  52. const handleCancelConfirm = useCallback(() => {
  53. setShowConfirm(null)
  54. }, [])
  55. const handleDelete = useCallback(() => {
  56. if (showConfirm)
  57. handleDeleteConversation(showConfirm.id, { onSuccess: handleCancelConfirm })
  58. }, [showConfirm, handleDeleteConversation, handleCancelConfirm])
  59. const handleCancelRename = useCallback(() => {
  60. setShowRename(null)
  61. }, [])
  62. const handleRename = useCallback((newName: string) => {
  63. if (showRename)
  64. handleRenameConversation(showRename.id, newName, { onSuccess: handleCancelRename })
  65. }, [showRename, handleRenameConversation, handleCancelRename])
  66. return (
  67. <>
  68. <div className='shrink-0 h-14 p-3 flex items-center justify-between'>
  69. <div className={cn('flex items-center gap-1 transition-all duration-200 ease-in-out', !isSidebarCollapsed && 'opacity-0 user-select-none')}>
  70. <ActionButton className={cn(!isSidebarCollapsed && 'cursor-default')} size='l' onClick={() => handleSidebarCollapse(false)}>
  71. <RiLayoutRight2Line className='w-[18px] h-[18px]' />
  72. </ActionButton>
  73. <div className='shrink-0 mr-1'>
  74. <AppIcon
  75. size='large'
  76. iconType={appData?.site.icon_type}
  77. icon={appData?.site.icon}
  78. background={appData?.site.icon_background}
  79. imageUrl={appData?.site.icon_url}
  80. />
  81. </div>
  82. {!currentConversationId && (
  83. <div className={cn('grow text-text-secondary system-md-semibold truncate')}>{appData?.site.title}</div>
  84. )}
  85. {currentConversationId && currentConversationItem && isSidebarCollapsed && (
  86. <>
  87. <div className='p-1 text-divider-deep'>/</div>
  88. <Operation
  89. title={currentConversationItem?.name || ''}
  90. isPinned={!!isPin}
  91. togglePin={() => handleOperate(isPin ? 'unpin' : 'pin')}
  92. isShowDelete
  93. isShowRenameConversation
  94. onRenameConversation={() => handleOperate('rename')}
  95. onDelete={() => handleOperate('delete')}
  96. />
  97. </>
  98. )}
  99. <div className='px-1 flex items-center'>
  100. <div className='h-[14px] w-px bg-divider-regular'></div>
  101. </div>
  102. {isSidebarCollapsed && (
  103. <Tooltip
  104. disabled={!!currentConversationId}
  105. popupContent={t('share.chat.newChatTip')}
  106. >
  107. <div>
  108. <ActionButton
  109. size='l'
  110. state={(!currentConversationId || isResponding) ? ActionButtonState.Disabled : ActionButtonState.Default}
  111. disabled={!currentConversationId || isResponding}
  112. onClick={handleNewConversation}
  113. >
  114. <RiEditBoxLine className='w-[18px] h-[18px]' />
  115. </ActionButton>
  116. </div>
  117. </Tooltip>
  118. )}
  119. </div>
  120. <div className='flex items-center gap-1'>
  121. {currentConversationId && (
  122. <Tooltip
  123. popupContent={t('share.chat.resetChat')}
  124. >
  125. <ActionButton size='l' onClick={handleNewConversation}>
  126. <RiResetLeftLine className='w-[18px] h-[18px]' />
  127. </ActionButton>
  128. </Tooltip>
  129. )}
  130. {currentConversationId && inputsForms.length > 0 && (
  131. <ViewFormDropdown />
  132. )}
  133. </div>
  134. </div>
  135. {!!showConfirm && (
  136. <Confirm
  137. title={t('share.chat.deleteConversation.title')}
  138. content={t('share.chat.deleteConversation.content') || ''}
  139. isShow
  140. onCancel={handleCancelConfirm}
  141. onConfirm={handleDelete}
  142. />
  143. )}
  144. {showRename && (
  145. <RenameModal
  146. isShow
  147. onClose={handleCancelRename}
  148. saveLoading={conversationRenaming}
  149. name={showRename?.name || ''}
  150. onSave={handleRename}
  151. />
  152. )}
  153. </>
  154. )
  155. }
  156. export default Header