index.tsx 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. import React, { useEffect, useState } from 'react'
  2. import type { FC } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. PencilSquareIcon,
  6. } from '@heroicons/react/24/outline'
  7. import cn from 'classnames'
  8. import Button from '../../../base/button'
  9. import List from './list'
  10. import AppInfo from '@/app/components/share/chat/sidebar/app-info'
  11. // import Card from './card'
  12. import type { ConversationItem, SiteInfo } from '@/models/share'
  13. import { fetchConversations } from '@/service/share'
  14. export type ISidebarProps = {
  15. copyRight: string
  16. currentId: string
  17. onCurrentIdChange: (id: string) => void
  18. list: ConversationItem[]
  19. pinnedList: ConversationItem[]
  20. isInstalledApp: boolean
  21. installedAppId?: string
  22. siteInfo: SiteInfo
  23. onMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
  24. onPinnedMoreLoaded: (res: { data: ConversationItem[]; has_more: boolean }) => void
  25. isNoMore: boolean
  26. isPinnedNoMore: boolean
  27. onPin: (id: string) => void
  28. onUnpin: (id: string) => void
  29. controlUpdateList: number
  30. onDelete: (id: string) => void
  31. }
  32. const Sidebar: FC<ISidebarProps> = ({
  33. copyRight,
  34. currentId,
  35. onCurrentIdChange,
  36. list,
  37. pinnedList,
  38. isInstalledApp,
  39. installedAppId,
  40. siteInfo,
  41. onMoreLoaded,
  42. onPinnedMoreLoaded,
  43. isNoMore,
  44. isPinnedNoMore,
  45. onPin,
  46. onUnpin,
  47. controlUpdateList,
  48. onDelete,
  49. }) => {
  50. const { t } = useTranslation()
  51. const [hasPinned, setHasPinned] = useState(false)
  52. const checkHasPinned = async () => {
  53. const { data }: any = await fetchConversations(isInstalledApp, installedAppId, undefined, true)
  54. setHasPinned(data.length > 0)
  55. }
  56. useEffect(() => {
  57. checkHasPinned()
  58. }, [])
  59. useEffect(() => {
  60. if (controlUpdateList !== 0)
  61. checkHasPinned()
  62. }, [controlUpdateList])
  63. const maxListHeight = isInstalledApp ? 'max-h-[30vh]' : 'max-h-[40vh]'
  64. return (
  65. <div
  66. className={
  67. cn(
  68. isInstalledApp ? 'tablet:h-[calc(100vh_-_74px)]' : 'tablet:h-[calc(100vh_-_3rem)]',
  69. 'shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 mobile:h-screen',
  70. )
  71. }
  72. >
  73. {isInstalledApp && (
  74. <AppInfo
  75. className='my-4 px-4'
  76. name={siteInfo.title || ''}
  77. icon={siteInfo.icon || ''}
  78. icon_background={siteInfo.icon_background}
  79. />
  80. )}
  81. <div className="flex flex-shrink-0 p-4 !pb-0">
  82. <Button
  83. onClick={() => { onCurrentIdChange('-1') }}
  84. className="group block w-full flex-shrink-0 !justify-start !h-9 text-primary-600 items-center text-sm">
  85. <PencilSquareIcon className="mr-2 h-4 w-4" /> {t('share.chat.newChat')}
  86. </Button>
  87. </div>
  88. <div className='flex-grow h-0 overflow-y-auto overflow-x-hidden'>
  89. {/* pinned list */}
  90. {hasPinned && (
  91. <div className='mt-4 px-4'>
  92. <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.pinnedTitle')}</div>
  93. <List
  94. className={maxListHeight}
  95. currentId={currentId}
  96. onCurrentIdChange={onCurrentIdChange}
  97. list={pinnedList}
  98. isInstalledApp={isInstalledApp}
  99. installedAppId={installedAppId}
  100. onMoreLoaded={onPinnedMoreLoaded}
  101. isNoMore={isPinnedNoMore}
  102. isPinned={true}
  103. onPinChanged={id => onUnpin(id)}
  104. controlUpdate={controlUpdateList + 1}
  105. onDelete={onDelete}
  106. />
  107. </div>
  108. )}
  109. {/* unpinned list */}
  110. <div className='mt-4 px-4'>
  111. {hasPinned && (
  112. <div className='mb-1.5 leading-[18px] text-xs text-gray-500 font-medium uppercase'>{t('share.chat.unpinnedTitle')}</div>
  113. )}
  114. <List
  115. className={cn(hasPinned ? maxListHeight : 'flex-grow')}
  116. currentId={currentId}
  117. onCurrentIdChange={onCurrentIdChange}
  118. list={list}
  119. isInstalledApp={isInstalledApp}
  120. installedAppId={installedAppId}
  121. onMoreLoaded={onMoreLoaded}
  122. isNoMore={isNoMore}
  123. isPinned={false}
  124. onPinChanged={id => onPin(id)}
  125. controlUpdate={controlUpdateList + 1}
  126. onDelete={onDelete}
  127. />
  128. </div>
  129. </div>
  130. <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
  131. <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
  132. </div>
  133. </div>
  134. )
  135. }
  136. export default React.memo(Sidebar)