chat-list.tsx 4.0 KB

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