mirror of
				https://github.com/ChatGPTNextWeb/ChatGPT-Next-Web.git
				synced 2025-11-04 16:23:41 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import DeleteIcon from "../icons/delete.svg";
 | 
						|
 | 
						|
import styles from "./home.module.scss";
 | 
						|
import {
 | 
						|
  DragDropContext,
 | 
						|
  Droppable,
 | 
						|
  Draggable,
 | 
						|
  OnDragEndResponder,
 | 
						|
} from "@hello-pangea/dnd";
 | 
						|
 | 
						|
import { useChatStore } from "../store";
 | 
						|
 | 
						|
import Locale from "../locales";
 | 
						|
import { useLocation, useNavigate } from "react-router-dom";
 | 
						|
import { Path } from "../constant";
 | 
						|
import { MaskAvatar } from "./mask";
 | 
						|
import { Mask } from "../store/mask";
 | 
						|
import { useRef, useEffect } from "react";
 | 
						|
import { showConfirm } from "./ui-lib";
 | 
						|
import { useMobileScreen } from "../utils";
 | 
						|
 | 
						|
export function ChatItem(props: {
 | 
						|
  onClick?: () => void;
 | 
						|
  onDelete?: () => void;
 | 
						|
  title: string;
 | 
						|
  count: number;
 | 
						|
  time: string;
 | 
						|
  selected: boolean;
 | 
						|
  id: string;
 | 
						|
  index: number;
 | 
						|
  narrow?: boolean;
 | 
						|
  mask: Mask;
 | 
						|
}) {
 | 
						|
  const draggableRef = useRef<HTMLDivElement | null>(null);
 | 
						|
  useEffect(() => {
 | 
						|
    if (props.selected && draggableRef.current) {
 | 
						|
      draggableRef.current?.scrollIntoView({
 | 
						|
        block: "center",
 | 
						|
      });
 | 
						|
    }
 | 
						|
  }, [props.selected]);
 | 
						|
 | 
						|
  const { pathname: currentPath } = useLocation();
 | 
						|
  return (
 | 
						|
    <Draggable draggableId={`${props.id}`} index={props.index}>
 | 
						|
      {(provided) => (
 | 
						|
        <div
 | 
						|
          className={`${styles["chat-item"]} ${
 | 
						|
            props.selected &&
 | 
						|
            (currentPath === Path.Chat || currentPath === Path.Home) &&
 | 
						|
            styles["chat-item-selected"]
 | 
						|
          }`}
 | 
						|
          onClick={props.onClick}
 | 
						|
          ref={(ele) => {
 | 
						|
            draggableRef.current = ele;
 | 
						|
            provided.innerRef(ele);
 | 
						|
          }}
 | 
						|
          {...provided.draggableProps}
 | 
						|
          {...provided.dragHandleProps}
 | 
						|
          title={`${props.title}\n${Locale.ChatItem.ChatItemCount(
 | 
						|
            props.count,
 | 
						|
          )}`}
 | 
						|
        >
 | 
						|
          {props.narrow ? (
 | 
						|
            <div className={styles["chat-item-narrow"]}>
 | 
						|
              <div className={styles["chat-item-avatar"] + " no-dark"}>
 | 
						|
                <MaskAvatar
 | 
						|
                  avatar={props.mask.avatar}
 | 
						|
                  model={props.mask.modelConfig.model}
 | 
						|
                />
 | 
						|
              </div>
 | 
						|
              <div className={styles["chat-item-narrow-count"]}>
 | 
						|
                {props.count}
 | 
						|
              </div>
 | 
						|
            </div>
 | 
						|
          ) : (
 | 
						|
            <>
 | 
						|
              <div className={styles["chat-item-title"]}>{props.title}</div>
 | 
						|
              <div className={styles["chat-item-info"]}>
 | 
						|
                <div className={styles["chat-item-count"]}>
 | 
						|
                  {Locale.ChatItem.ChatItemCount(props.count)}
 | 
						|
                </div>
 | 
						|
                <div className={styles["chat-item-date"]}>{props.time}</div>
 | 
						|
              </div>
 | 
						|
            </>
 | 
						|
          )}
 | 
						|
 | 
						|
          <div
 | 
						|
            className={styles["chat-item-delete"]}
 | 
						|
            onClickCapture={(e) => {
 | 
						|
              props.onDelete?.();
 | 
						|
              e.preventDefault();
 | 
						|
              e.stopPropagation();
 | 
						|
            }}
 | 
						|
          >
 | 
						|
            <DeleteIcon />
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      )}
 | 
						|
    </Draggable>
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
export function ChatList(props: { narrow?: boolean }) {
 | 
						|
  const [sessions, selectedIndex, selectSession, moveSession] = useChatStore(
 | 
						|
    (state) => [
 | 
						|
      state.sessions,
 | 
						|
      state.currentSessionIndex,
 | 
						|
      state.selectSession,
 | 
						|
      state.moveSession,
 | 
						|
    ],
 | 
						|
  );
 | 
						|
  const chatStore = useChatStore();
 | 
						|
  const navigate = useNavigate();
 | 
						|
  const isMobileScreen = useMobileScreen();
 | 
						|
 | 
						|
  const onDragEnd: OnDragEndResponder = (result) => {
 | 
						|
    const { destination, source } = result;
 | 
						|
    if (!destination) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    if (
 | 
						|
      destination.droppableId === source.droppableId &&
 | 
						|
      destination.index === source.index
 | 
						|
    ) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    moveSession(source.index, destination.index);
 | 
						|
  };
 | 
						|
 | 
						|
  return (
 | 
						|
    <DragDropContext onDragEnd={onDragEnd}>
 | 
						|
      <Droppable droppableId="chat-list">
 | 
						|
        {(provided) => (
 | 
						|
          <div
 | 
						|
            className={styles["chat-list"]}
 | 
						|
            ref={provided.innerRef}
 | 
						|
            {...provided.droppableProps}
 | 
						|
          >
 | 
						|
            {sessions.map((item, i) => (
 | 
						|
              <ChatItem
 | 
						|
                title={item.topic}
 | 
						|
                time={new Date(item.lastUpdate).toLocaleString()}
 | 
						|
                count={item.messages.length}
 | 
						|
                key={item.id}
 | 
						|
                id={item.id}
 | 
						|
                index={i}
 | 
						|
                selected={i === selectedIndex}
 | 
						|
                onClick={() => {
 | 
						|
                  navigate(Path.Chat);
 | 
						|
                  selectSession(i);
 | 
						|
                }}
 | 
						|
                onDelete={async () => {
 | 
						|
                  if (
 | 
						|
                    (!props.narrow && !isMobileScreen) ||
 | 
						|
                    (await showConfirm(Locale.Home.DeleteChat))
 | 
						|
                  ) {
 | 
						|
                    chatStore.deleteSession(i);
 | 
						|
                  }
 | 
						|
                }}
 | 
						|
                narrow={props.narrow}
 | 
						|
                mask={item.mask}
 | 
						|
              />
 | 
						|
            ))}
 | 
						|
            {provided.placeholder}
 | 
						|
          </div>
 | 
						|
        )}
 | 
						|
      </Droppable>
 | 
						|
    </DragDropContext>
 | 
						|
  );
 | 
						|
}
 |