index.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import React, { useEffect, useRef } from 'react'
  2. import type { FC } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import {
  5. ChatBubbleOvalLeftEllipsisIcon,
  6. PencilSquareIcon
  7. } from '@heroicons/react/24/outline'
  8. import { ChatBubbleOvalLeftEllipsisIcon as ChatBubbleOvalLeftEllipsisSolidIcon, } from '@heroicons/react/24/solid'
  9. import Button from '../../../base/button'
  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 { useInfiniteScroll } from 'ahooks'
  14. import { fetchConversations } from '@/service/share'
  15. function classNames(...classes: any[]) {
  16. return classes.filter(Boolean).join(' ')
  17. }
  18. export type ISidebarProps = {
  19. copyRight: string
  20. currentId: string
  21. onCurrentIdChange: (id: string) => void
  22. list: ConversationItem[]
  23. isInstalledApp: boolean
  24. installedAppId?: string
  25. siteInfo: SiteInfo
  26. onMoreLoaded: (res: {data: ConversationItem[], has_more: boolean}) => void
  27. isNoMore: boolean
  28. }
  29. const Sidebar: FC<ISidebarProps> = ({
  30. copyRight,
  31. currentId,
  32. onCurrentIdChange,
  33. list,
  34. isInstalledApp,
  35. installedAppId,
  36. siteInfo,
  37. onMoreLoaded,
  38. isNoMore,
  39. }) => {
  40. const { t } = useTranslation()
  41. const listRef = useRef<HTMLDivElement>(null)
  42. useInfiniteScroll(
  43. async () => {
  44. if(!isNoMore) {
  45. const lastId = list[list.length - 1].id
  46. const { data: conversations, has_more }: any = await fetchConversations(isInstalledApp, installedAppId, lastId)
  47. onMoreLoaded({ data: conversations, has_more })
  48. }
  49. return {list: []}
  50. },
  51. {
  52. target: listRef,
  53. isNoMore: () => {
  54. return isNoMore
  55. },
  56. reloadDeps: [isNoMore]
  57. },
  58. )
  59. return (
  60. <div
  61. className={
  62. classNames(
  63. isInstalledApp ? 'tablet:h-[calc(100vh_-_74px)]' : 'tablet:h-[calc(100vh_-_3rem)]',
  64. "shrink-0 flex flex-col bg-white pc:w-[244px] tablet:w-[192px] mobile:w-[240px] border-r border-gray-200 mobile:h-screen"
  65. )
  66. }
  67. >
  68. {isInstalledApp && (
  69. <AppInfo
  70. className='my-4 px-4'
  71. name={siteInfo.title || ''}
  72. icon={siteInfo.icon || ''}
  73. icon_background={siteInfo.icon_background}
  74. />
  75. )}
  76. <div className="flex flex-shrink-0 p-4 !pb-0">
  77. <Button
  78. onClick={() => { onCurrentIdChange('-1') }}
  79. className="group block w-full flex-shrink-0 !justify-start !h-9 text-primary-600 items-center text-sm">
  80. <PencilSquareIcon className="mr-2 h-4 w-4" /> {t('share.chat.newChat')}
  81. </Button>
  82. </div>
  83. <nav
  84. ref={listRef}
  85. className="mt-4 flex-1 space-y-1 bg-white p-4 !pt-0 overflow-y-auto"
  86. >
  87. {list.map((item) => {
  88. const isCurrent = item.id === currentId
  89. const ItemIcon
  90. = isCurrent ? ChatBubbleOvalLeftEllipsisSolidIcon : ChatBubbleOvalLeftEllipsisIcon
  91. return (
  92. <div
  93. onClick={() => onCurrentIdChange(item.id)}
  94. key={item.id}
  95. className={classNames(
  96. isCurrent
  97. ? 'bg-primary-50 text-primary-600'
  98. : 'text-gray-700 hover:bg-gray-100 hover:text-gray-700',
  99. 'group flex items-center rounded-md px-2 py-2 text-sm font-medium cursor-pointer',
  100. )}
  101. >
  102. <ItemIcon
  103. className={classNames(
  104. isCurrent
  105. ? 'text-primary-600'
  106. : 'text-gray-400 group-hover:text-gray-500',
  107. 'mr-3 h-5 w-5 flex-shrink-0',
  108. )}
  109. aria-hidden="true"
  110. />
  111. {item.name}
  112. </div>
  113. )
  114. })}
  115. </nav>
  116. <div className="flex flex-shrink-0 pr-4 pb-4 pl-4">
  117. <div className="text-gray-400 font-normal text-xs">© {copyRight} {(new Date()).getFullYear()}</div>
  118. </div>
  119. </div>
  120. )
  121. }
  122. export default React.memo(Sidebar)