FileList.tsx 4.4 KB
import React, { useCallback, useState } from "react";
import { Table, message, Button, Popover } from "antd";
import { ColumnsType } from "antd/lib/table";
import filesize from "filesize";

import { useParams } from "react-router-dom";
import { useFileList, FileItem } from "./useFileList";
import { useApi } from "util/useApi";
import { FileListItem } from "./FileListItem";
import { FileItemActions } from "./FileItemActions";

import styles from "./FileList.module.scss";
import { FileUploadPopover } from "./FileUploadPopover";

export function FileList() {
  const id = useParams<{ id: string }>().id;
  const { data, reload } = useFileList(id);
  const [upload, setUpload] = useState<boolean>(false);

  const api = useApi();

  const handleRename = useCallback(
    async (id: number, name: string) => {
      try {
        const body = new URLSearchParams();
        body.set("name", name);

        await api.post(`/items/${id}/move/`, { body });
        await reload();

        message.info("이름이 변경되었습니다");
      } catch {
        message.error("이름 변경에 실패했습니다");
      }
    },
    [api, reload]
  );

  const handleMove = useCallback(
    async (id: number, to: number) => {
      try {
        const body = new URLSearchParams();
        body.set("parent", to.toString(10));

        await api.post(`/items/${id}/move/`, { body });
        await reload();

        message.info("이동되었습니다");
      } catch {
        message.error("파일 이동에 실패했습니다");
      }
    },
    [api, reload]
  );

  const handleCopy = useCallback(
    async (id: number, to: number) => {
      try {
        const body = new URLSearchParams();
        body.set("parent", to.toString(10));

        await api.post(`/items/${id}/copy/`, { body });
        await reload();

        message.info("복사되었습니다");
      } catch {
        message.error("파일 복사에 실패했습니다");
      }
    },
    [api, reload]
  );

  const handleDelete = useCallback(
    async (id: number) => {
      try {
        await api.delete(`/items/${id}/`);
        await reload();
        message.info("삭제되었습니다");
      } catch {
        message.error("파일 삭제에 실패했습니다");
      }
    },
    [api, reload]
  );

  if (!data) {
    return null;
  }

  const list = [...data.list].sort((itemA, itemB) =>
    itemA.is_folder === itemB.is_folder ? 0 : itemA.is_folder ? -1 : 1
  );

  if (data.parent !== null) {
    list.unshift(({
      id: data.parent,
      is_folder: true,
      name: "..",
      file_type: "folder",
    } as unknown) as FileItem);
  }

  return (
    <div>
      <div className={styles.header}>
        <div>{data.parent !== null && <h3>{data.name}</h3>}</div>
        <div>
          <Popover
            content={<FileUploadPopover root={data.id} reload={reload} />}
            trigger="click"
            visible={upload}
            onVisibleChange={setUpload}
          >
            <Button type="link" size="small">
              파일 업로드
            </Button>
          </Popover>
          <Button type="link" size="small">
            새 폴더
          </Button>
        </div>
      </div>
      <Table
        rowKey="id"
        columns={getColumns({
          handleRename,
          handleMove,
          handleCopy,
          handleDelete,
        })}
        dataSource={list}
        pagination={false}
      />
    </div>
  );
}

type GetColumnsParams = {
  handleRename: (id: number, name: string) => void;
  handleMove: (id: number, to: number) => void;
  handleCopy: (id: number, to: number) => void;
  handleDelete: (id: number) => void;
};

function getColumns({
  handleRename,
  handleMove,
  handleCopy,
  handleDelete,
}: GetColumnsParams): ColumnsType<FileItem> {
  return [
    {
      title: "이름",
      key: "name",
      dataIndex: "name",
      render: (_name: string, item) => <FileListItem item={item} />,
    },
    {
      title: "크기",
      key: "size",
      dataIndex: "size",
      width: 120,
      render: (bytes: number, item) =>
        item.is_folder ? "-" : filesize(bytes, { round: 0 }),
    },
    {
      title: "",
      key: "action",
      dataIndex: "",
      width: 300,
      render: (__: any, item) => (
        <FileItemActions
          item={item}
          onRename={handleRename}
          onMove={handleMove}
          onCopy={handleCopy}
          onDelete={handleDelete}
        />
      ),
    },
  ];
}