chat-list.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  1. import DeleteIcon from "../icons/delete.svg";
  2. import BotIcon from "../icons/bot.svg";
  3. import styles from "./home.module.scss";
  4. import {
  5. DragDropContext,
  6. Droppable,
  7. Draggable,
  8. OnDragEndResponder,
  9. } from "@hello-pangea/dnd";
  10. import { useChatStore } from "../store";
  11. import Locale from "../locales";
  12. import { Link, useNavigate } from "react-router-dom";
  13. import { Path } from "../constant";
  14. export function ChatItem(props: {
  15. onClick?: () => void;
  16. onDelete?: () => void;
  17. title: string;
  18. count: number;
  19. time: string;
  20. selected: boolean;
  21. id: number;
  22. index: number;
  23. narrow?: boolean;
  24. }) {
  25. return (
  26. <Draggable draggableId={`${props.id}`} index={props.index}>
  27. {(provided) => (
  28. <div
  29. className={`${styles["chat-item"]} ${
  30. props.selected && styles["chat-item-selected"]
  31. }`}
  32. onClick={props.onClick}
  33. ref={provided.innerRef}
  34. {...provided.draggableProps}
  35. {...provided.dragHandleProps}
  36. title={`${props.title}\n${Locale.ChatItem.ChatItemCount(
  37. props.count,
  38. )}`}
  39. >
  40. {props.narrow ? (
  41. <div className={styles["chat-item-narrow"]}>
  42. <div className={styles["chat-item-avatar"] + " no-dark"}>
  43. <BotIcon></BotIcon>
  44. </div>
  45. <div className={styles["chat-item-narrow-count"]}>
  46. {props.count}
  47. </div>
  48. </div>
  49. ) : (
  50. <>
  51. <div className={styles["chat-item-title"]}>{props.title}</div>
  52. <div className={styles["chat-item-info"]}>
  53. <div className={styles["chat-item-count"]}>
  54. {Locale.ChatItem.ChatItemCount(props.count)}
  55. </div>
  56. <div className={styles["chat-item-date"]}>
  57. {new Date(props.time).toLocaleString()}
  58. </div>
  59. </div>
  60. </>
  61. )}
  62. <div className={styles["chat-item-delete"]} onClick={props.onDelete}>
  63. <DeleteIcon />
  64. </div>
  65. </div>
  66. )}
  67. </Draggable>
  68. );
  69. }
  70. export function ChatList(props: { narrow?: boolean }) {
  71. const [sessions, selectedIndex, selectSession, removeSession, moveSession] =
  72. useChatStore((state) => [
  73. state.sessions,
  74. state.currentSessionIndex,
  75. state.selectSession,
  76. state.removeSession,
  77. state.moveSession,
  78. ]);
  79. const chatStore = useChatStore();
  80. const navigate = useNavigate();
  81. const onDragEnd: OnDragEndResponder = (result) => {
  82. const { destination, source } = result;
  83. if (!destination) {
  84. return;
  85. }
  86. if (
  87. destination.droppableId === source.droppableId &&
  88. destination.index === source.index
  89. ) {
  90. return;
  91. }
  92. moveSession(source.index, destination.index);
  93. };
  94. return (
  95. <DragDropContext onDragEnd={onDragEnd}>
  96. <Droppable droppableId="chat-list">
  97. {(provided) => (
  98. <div
  99. className={styles["chat-list"]}
  100. ref={provided.innerRef}
  101. {...provided.droppableProps}
  102. >
  103. {sessions.map((item, i) => (
  104. <ChatItem
  105. title={item.topic}
  106. time={new Date(item.lastUpdate).toLocaleString()}
  107. count={item.messages.length}
  108. key={item.id}
  109. id={item.id}
  110. index={i}
  111. selected={i === selectedIndex}
  112. onClick={() => {
  113. navigate(Path.Chat);
  114. selectSession(i);
  115. }}
  116. onDelete={() => {
  117. if (!props.narrow || confirm(Locale.Home.DeleteChat)) {
  118. chatStore.deleteSession(i);
  119. }
  120. }}
  121. narrow={props.narrow}
  122. />
  123. ))}
  124. {provided.placeholder}
  125. </div>
  126. )}
  127. </Droppable>
  128. </DragDropContext>
  129. );
  130. }