김재형

Implement file upload

1 -import React, { useCallback } from "react"; 1 +import React, { useCallback, useState } from "react";
2 -import { Table, message, Button } from "antd"; 2 +import { Table, message, Button, Popover } from "antd";
3 import { ColumnsType } from "antd/lib/table"; 3 import { ColumnsType } from "antd/lib/table";
4 import filesize from "filesize"; 4 import filesize from "filesize";
5 5
...@@ -10,10 +10,12 @@ import { FileListItem } from "./FileListItem"; ...@@ -10,10 +10,12 @@ import { FileListItem } from "./FileListItem";
10 import { FileItemActions } from "./FileItemActions"; 10 import { FileItemActions } from "./FileItemActions";
11 11
12 import styles from "./FileList.module.scss"; 12 import styles from "./FileList.module.scss";
13 +import { FileUploadPopover } from "./FileUploadPopover";
13 14
14 export function FileList() { 15 export function FileList() {
15 const id = useParams<{ id: string }>().id; 16 const id = useParams<{ id: string }>().id;
16 const { data, reload } = useFileList(id); 17 const { data, reload } = useFileList(id);
18 + const [upload, setUpload] = useState<boolean>(false);
17 19
18 const api = useApi(); 20 const api = useApi();
19 21
...@@ -103,9 +105,16 @@ export function FileList() { ...@@ -103,9 +105,16 @@ export function FileList() {
103 <div className={styles.header}> 105 <div className={styles.header}>
104 <div>{data.parent !== null && <h3>{data.name}</h3>}</div> 106 <div>{data.parent !== null && <h3>{data.name}</h3>}</div>
105 <div> 107 <div>
106 - <Button type="link" size="small"> 108 + <Popover
107 - 파일 업로드 109 + content={<FileUploadPopover root={data.id} reload={reload} />}
108 - </Button> 110 + trigger="click"
111 + visible={upload}
112 + onVisibleChange={setUpload}
113 + >
114 + <Button type="link" size="small">
115 + 파일 업로드
116 + </Button>
117 + </Popover>
109 <Button type="link" size="small"> 118 <Button type="link" size="small">
110 새 폴더 119 새 폴더
111 </Button> 120 </Button>
......
1 +import React, { useCallback, useRef } from "react";
2 +import Dragger from "antd/lib/upload/Dragger";
3 +import { InboxOutlined } from "@ant-design/icons";
4 +import { useApi } from "util/useApi";
5 +
6 +export type FileUploadPopoverProps = {
7 + root: number;
8 + reload: () => void;
9 +};
10 +
11 +export function FileUploadPopover({ root, reload }: FileUploadPopoverProps) {
12 + const api = useApi();
13 + const fields = useRef<any>();
14 + const stateMap = useRef<Record<string, number>>({});
15 +
16 + const getS3Object = useCallback(
17 + async (file: File) => {
18 + const body = new URLSearchParams();
19 + body.set("name", file.name);
20 + body.set("size", file.size.toString());
21 +
22 + const response = await api
23 + .post(`/items/${root}/upload/`, { body })
24 + .json<any>();
25 +
26 + stateMap.current[file.name] = response.item.id;
27 + fields.current = response.signed_url.fields;
28 + return response.signed_url.url;
29 + },
30 + [api, root]
31 + );
32 +
33 + const setObjectStatus = useCallback(
34 + async (info) => {
35 + if (info.file.status === "done") {
36 + const id = stateMap.current[info.file.name];
37 + if (typeof id !== "undefined") {
38 + const body = new URLSearchParams();
39 + body.set("item_id", id.toString());
40 + await api.post(`/items/${id}/status/`, { body });
41 + reload();
42 + }
43 + }
44 + },
45 + [api, reload]
46 + );
47 +
48 + return (
49 + <Dragger
50 + name="file"
51 + multiple={true}
52 + action={getS3Object}
53 + data={() => fields.current}
54 + onChange={setObjectStatus}
55 + style={{ padding: 40 }}
56 + >
57 + <p className="ant-upload-drag-icon">
58 + <InboxOutlined />
59 + </p>
60 + <p className="ant-upload-text">
61 + 업로드할 파일을 선택하거나 드래그 하세요
62 + </p>
63 + <p className="ant-upload-hint"></p>
64 + </Dragger>
65 + );
66 +}