Skip to content

Commit 4b25024

Browse files
feat: add delete img button (#88)
* chore: remove unique property from models * feat: remove isLoading state * feat: add filesAtom to store * feat: add deleteFile function * refactor: move getFiles to separate file
1 parent 8f9dec0 commit 4b25024

File tree

7 files changed

+97
-45
lines changed

7 files changed

+97
-45
lines changed

models/docModel.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import "gorm.io/gorm"
55
type Doc struct {
66
gorm.Model
77

8-
FileName string `json:"file_name" gorm:"unique"`
8+
FileName string `json:"file_name"`
99
Checksum []byte `json:"checksum"`
1010
}

models/imageModel.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ import "gorm.io/gorm"
55
type Image struct {
66
gorm.Model
77

8-
FileName string `json:"file_name" gorm:"unique"`
8+
FileName string `json:"file_name"`
99
Checksum []byte `json:"checksum"`
1010
}

ui/src/actions/getFiles.ts

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import axios from "axios";
2+
import { SetStateAction } from "jotai";
3+
import toast from "react-hot-toast";
4+
import File from "../types/file";
5+
6+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7+
type SetAtom<Args extends any[], Result> = (...args: Args) => Result;
8+
9+
export const getFiles = (
10+
type: "images" | "documents",
11+
setFiles: SetAtom<[SetStateAction<File[]>], void>,
12+
) => {
13+
toast.loading("Loading files...");
14+
axios
15+
.get(`/api/cdn/${type === "images" ? "image" : "doc"}/all`)
16+
.then((res) => res.data != null && setFiles(res.data))
17+
.catch((err: Error) => {
18+
toast.dismiss();
19+
toast.error(err.message);
20+
console.log(err);
21+
})
22+
.finally(() => toast.dismiss());
23+
}
24+

ui/src/actions/getSize.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ type SetAtom<Args extends any[], Result> = (...args: Args) => Result;
77

88
export const getSize = (
99
setSize: SetAtom<[SetStateAction<number>], void>,
10-
setLoading: React.Dispatch<React.SetStateAction<boolean>>
10+
setLoading?: React.Dispatch<React.SetStateAction<boolean>>
1111
) => {
12-
setLoading(true);
12+
setLoading && setLoading(true);
1313
axios
1414
.get<{ cdn_size_bytes: number }>("/api/cdn/size")
1515
.then((res) => {
@@ -20,6 +20,6 @@ export const getSize = (
2020
console.log(err);
2121
})
2222
.finally(() => {
23-
setLoading(false);
23+
setLoading && setLoading(false);
2424
});
2525
};

ui/src/components/content-card.tsx

+60-23
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
import { DownloadCloud, FileText, Files } from "lucide-react";
1+
import axios from "axios";
2+
import { DownloadCloud, FileText, Files, Trash2 } from "lucide-react";
23
import { toast } from "react-hot-toast";
4+
import { getFiles } from "../actions/getFiles";
5+
import { useAtom } from "jotai";
6+
import { filesAtom, sizeAtom } from "../store";
7+
import { getSize } from "../actions/getSize";
38

49
type TContentCardProps = {
510
file_name?: string;
@@ -21,6 +26,23 @@ const ContentCard: React.FC<TContentCardProps> = ({
2126
const url = `${window.location.protocol}//${
2227
window.location.host
2328
}/api/cdn/download/${type === "documents" ? "docs" : "images"}/${file_name}`;
29+
const [_, setFiles] = useAtom(filesAtom)
30+
const [__, setSize] = useAtom(sizeAtom)
31+
32+
const deleteFile = () => {
33+
toast.loading("Deleting file...");
34+
axios.delete(`/api/cdn/delete/${type === "documents" ? "doc" : "image"}/${file_name}`).then((res) => {
35+
if (res.status === 200) {
36+
toast.dismiss()
37+
toast.success("Deleted file!")
38+
getFiles(type, setFiles)
39+
getSize(setSize);
40+
}
41+
}).catch((err: Error) => {
42+
toast.dismiss();
43+
toast.error(err.message)
44+
})
45+
}
2446

2547
return (
2648
<div className="border rounded-lg shadow-lg flex flex-col min-h-[264px] w-64 max-w-[256px] justify-center items-center gap-4 p-4">
@@ -36,29 +58,44 @@ const ContentCard: React.FC<TContentCardProps> = ({
3658
<FileText size="128" />
3759
)}
3860
<p className="truncate w-64 px-4">{file_name}</p>
39-
<div className={`flex gap-2 w-full ${disabled && "sr-only"}`}>
61+
<div className={`flex w-full justify-between ${disabled && "sr-only"}`}>
62+
{/* Non-destructive buttons */}
63+
<div className="flex gap-2">
64+
<button
65+
className="flex justify-center items-center text-sky-600 tooltip"
66+
onClick={() => {
67+
navigator.clipboard.writeText(url);
68+
toast.success("clipboard saved");
69+
}}
70+
aria-label="Copy Link"
71+
aria-labelledby="Copy Link"
72+
>
73+
<span className="tooltiptext">Copy Link</span>
74+
<Files className="inline" size="24" />
75+
</button>
76+
<a
77+
className="flex justify-center items-center text-sky-600 tooltip"
78+
aria-label="Download file"
79+
aria-labelledby="Download file"
80+
href={url}
81+
download
82+
>
83+
<span className="tooltiptext">Download file</span>
84+
<DownloadCloud className="inline" size="24" />
85+
</a>
86+
</div>
87+
{/* Destructive buttons */}
88+
<div className="flex gap-2">
4089
<button
41-
className="flex justify-center items-center text-sky-600 tooltip"
42-
onClick={() => {
43-
navigator.clipboard.writeText(url);
44-
toast.success("clipboard saved");
45-
}}
46-
aria-label="Copy Link"
47-
aria-labelledby="Copy Link"
48-
>
49-
<span className="tooltiptext">Copy Link</span>
50-
<Files className="inline" size="24" />
51-
</button>
52-
<a
53-
className="flex justify-center items-center text-sky-600 tooltip"
54-
aria-label="Download file"
55-
aria-labelledby="Download file"
56-
href={url}
57-
download
58-
>
59-
<span className="tooltiptext">Download file</span>
60-
<DownloadCloud className="inline" size="24" />
61-
</a>
90+
className="flex justify-center items-center text-red-600 tooltip"
91+
onClick={() => file_name && deleteFile()}
92+
aria-label="Delete file"
93+
aria-labelledby="Delete file"
94+
>
95+
<span className="tooltiptext">Delete file</span>
96+
<Trash2 className="inline" size="24" />
97+
</button>
98+
</div>
6299
</div>
63100
</div>
64101
);

ui/src/components/files.tsx

+6-17
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,21 @@
1-
import { useEffect, useState } from "react";
2-
import axios from "axios";
3-
import File from "../types/file";
1+
import { useEffect } from "react";
42
import ContentCard from "./content-card";
53
import Seperator from "./seperator";
4+
import { useAtom } from "jotai";
5+
import { filesAtom } from "../store";
6+
import { getFiles } from "../actions/getFiles";
67

78
type TFilesProps = {
89
type: "images" | "documents";
910
};
1011

1112
const Files: React.FC<TFilesProps> = ({ type }) => {
12-
const [files, setFiles] = useState<File[]>([]);
13-
const [isLoading, setIsLoading] = useState(false);
13+
const [files, setFiles] = useAtom(filesAtom);
1414

1515
useEffect(() => {
16-
setIsLoading(true);
17-
axios
18-
.get(`/api/cdn/${type === "images" ? "image" : "doc"}/all`)
19-
.then((res) => res.data != null && setFiles(res.data))
20-
.catch((err: undefined) => {
21-
console.log(err);
22-
})
23-
.finally(() => setIsLoading(false));
16+
getFiles(type, setFiles)
2417
}, [type]);
2518

26-
if (isLoading) {
27-
return <div>Loading...</div>;
28-
}
29-
3019
return (
3120
<div className="w-full">
3221
<h2 className="text-2xl capitalize mb-8">{type}</h2>

ui/src/store.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { atom } from "jotai";
2+
import File from "./types/file";
23

34
export const sizeAtom = atom(0);
45
export const sizeLoadingAtom = atom(false);
6+
export const filesAtom = atom<File[]>([]);

0 commit comments

Comments
 (0)