index.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import {
  2. useCallback,
  3. useState,
  4. } from 'react'
  5. import { useTranslation } from 'react-i18next'
  6. import { useChatWithHistoryContext } from '../context'
  7. import List from './list'
  8. import AppIcon from '@/app/components/base/app-icon'
  9. import Button from '@/app/components/base/button'
  10. import { Edit05 } from '@/app/components/base/icons/src/vender/line/general'
  11. import type { ConversationItem } from '@/models/share'
  12. import Confirm from '@/app/components/base/confirm'
  13. import RenameModal from '@/app/components/share/chat/sidebar/rename-modal'
  14. const Sidebar = () => {
  15. const { t } = useTranslation()
  16. const {
  17. appData,
  18. pinnedConversationList,
  19. conversationList,
  20. handleNewConversation,
  21. currentConversationId,
  22. handleChangeConversation,
  23. handlePinConversation,
  24. handleUnpinConversation,
  25. conversationRenaming,
  26. handleRenameConversation,
  27. handleDeleteConversation,
  28. isMobile,
  29. } = useChatWithHistoryContext()
  30. const [showConfirm, setShowConfirm] = useState<ConversationItem | null>(null)
  31. const [showRename, setShowRename] = useState<ConversationItem | null>(null)
  32. const handleOperate = useCallback((type: string, item: ConversationItem) => {
  33. if (type === 'pin')
  34. handlePinConversation(item.id)
  35. if (type === 'unpin')
  36. handleUnpinConversation(item.id)
  37. if (type === 'delete')
  38. setShowConfirm(item)
  39. if (type === 'rename')
  40. setShowRename(item)
  41. }, [handlePinConversation, handleUnpinConversation])
  42. const handleCancelConfirm = useCallback(() => {
  43. setShowConfirm(null)
  44. }, [])
  45. const handleDelete = useCallback(() => {
  46. if (showConfirm)
  47. handleDeleteConversation(showConfirm.id, { onSuccess: handleCancelConfirm })
  48. }, [showConfirm, handleDeleteConversation, handleCancelConfirm])
  49. const handleCancelRename = useCallback(() => {
  50. setShowRename(null)
  51. }, [])
  52. const handleRename = useCallback((newName: string) => {
  53. if (showRename)
  54. handleRenameConversation(showRename.id, newName, { onSuccess: handleCancelRename })
  55. }, [showRename, handleRenameConversation, handleCancelRename])
  56. return (
  57. <div className='shrink-0 h-full flex flex-col w-[240px] border-r border-r-gray-100'>
  58. {
  59. !isMobile && (
  60. <div className='shrink-0 flex p-4'>
  61. <AppIcon
  62. className='mr-3'
  63. size='small'
  64. icon={appData?.site.icon}
  65. background={appData?.site.icon_background}
  66. />
  67. <div className='py-1 text-base font-semibold text-gray-800'>
  68. {appData?.site.title}
  69. </div>
  70. </div>
  71. )
  72. }
  73. <div className='shrink-0 p-4'>
  74. <Button
  75. className='justify-start px-3 py-0 w-full h-9 text-sm font-medium text-primary-600'
  76. onClick={handleNewConversation}
  77. >
  78. <Edit05 className='mr-2 w-4 h-4' />
  79. {t('share.chat.newChat')}
  80. </Button>
  81. </div>
  82. <div className='grow px-4 py-2 overflow-y-auto'>
  83. {
  84. !!pinnedConversationList.length && (
  85. <div className='mb-4'>
  86. <List
  87. isPin
  88. title={t('share.chat.pinnedTitle') || ''}
  89. list={pinnedConversationList}
  90. onChangeConversation={handleChangeConversation}
  91. onOperate={handleOperate}
  92. currentConversationId={currentConversationId}
  93. />
  94. </div>
  95. )
  96. }
  97. {
  98. !!conversationList.length && (
  99. <List
  100. title={(pinnedConversationList.length && t('share.chat.unpinnedTitle')) || ''}
  101. list={conversationList}
  102. onChangeConversation={handleChangeConversation}
  103. onOperate={handleOperate}
  104. currentConversationId={currentConversationId}
  105. />
  106. )
  107. }
  108. </div>
  109. <div className='px-4 pb-4 text-xs text-gray-400'>
  110. © {appData?.site.copyright || appData?.site.title} {(new Date()).getFullYear()}
  111. </div>
  112. {!!showConfirm && (
  113. <Confirm
  114. title={t('share.chat.deleteConversation.title')}
  115. content={t('share.chat.deleteConversation.content') || ''}
  116. isShow
  117. onClose={handleCancelConfirm}
  118. onCancel={handleCancelConfirm}
  119. onConfirm={handleDelete}
  120. />
  121. )}
  122. {showRename && (
  123. <RenameModal
  124. isShow
  125. onClose={handleCancelRename}
  126. saveLoading={conversationRenaming}
  127. name={showRename?.name || ''}
  128. onSave={handleRename}
  129. />
  130. )}
  131. </div>
  132. )
  133. }
  134. export default Sidebar