Skip to content

Commit bad7be9

Browse files
authored
feat: support drag-and-drop when importing opml (#2001)
1 parent 564270b commit bad7be9

File tree

2 files changed

+74
-7
lines changed

2 files changed

+74
-7
lines changed
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import type { DragEvent, ReactNode } from "react"
2+
import { useCallback, useRef, useState } from "react"
3+
4+
// Ported from https://github.com/react-dropzone/react-dropzone/issues/753#issuecomment-774782919
5+
const useDragAndDrop = ({ callback }: { callback: (file: FileList) => void | Promise<void> }) => {
6+
const [isDragging, setIsDragging] = useState(false)
7+
const dragCounter = useRef(0)
8+
9+
const onDrop = useCallback(
10+
async (event: DragEvent<HTMLLabelElement>) => {
11+
event.preventDefault()
12+
setIsDragging(false)
13+
if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
14+
dragCounter.current = 0
15+
await callback(event.dataTransfer.files)
16+
event.dataTransfer.clearData()
17+
}
18+
},
19+
[callback],
20+
)
21+
22+
const onDragEnter = useCallback((event: DragEvent) => {
23+
event.preventDefault()
24+
dragCounter.current++
25+
setIsDragging(true)
26+
}, [])
27+
28+
const onDragOver = useCallback((event: DragEvent) => {
29+
event.preventDefault()
30+
}, [])
31+
32+
const onDragLeave = useCallback((event: DragEvent) => {
33+
event.preventDefault()
34+
dragCounter.current--
35+
if (dragCounter.current > 0) return
36+
setIsDragging(false)
37+
}, [])
38+
39+
return {
40+
isDragging,
41+
42+
dragHandlers: {
43+
onDrop,
44+
onDragOver,
45+
onDragEnter,
46+
onDragLeave,
47+
},
48+
}
49+
}
50+
51+
export const DropZone = ({
52+
onDrop,
53+
children,
54+
}: {
55+
onDrop: (file: FileList) => void | Promise<void>
56+
children?: ReactNode
57+
}) => {
58+
const { isDragging, dragHandlers } = useDragAndDrop({ callback: onDrop })
59+
60+
return (
61+
<label
62+
className={`center flex h-[100px] w-full rounded-md border border-dashed ${
63+
isDragging ? "border-blue-500 bg-blue-100" : ""
64+
}`}
65+
htmlFor="upload-file"
66+
{...dragHandlers}
67+
>
68+
{children}
69+
</label>
70+
)
71+
}

apps/renderer/src/modules/discover/import.tsx

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { useForm } from "react-hook-form"
1717
import { Trans, useTranslation } from "react-i18next"
1818
import { z } from "zod"
1919

20+
import { DropZone } from "~/components/ui/drop-zone"
2021
import { apiFetch } from "~/lib/api-fetch"
2122
import { toastFetchError } from "~/lib/error-parser"
2223
import { Queries } from "~/queries"
@@ -110,26 +111,21 @@ export function DiscoverImport({ isInit = false }: { isInit?: boolean }) {
110111
{isInit ? t("discover.import.new_import_opml") : t("discover.import.opml")}
111112
</FormLabel>
112113
<FormControl>
113-
<label
114-
className="center flex h-[100px] w-full rounded-md border border-dashed"
115-
htmlFor="upload-file"
116-
>
114+
<DropZone onDrop={(fileList) => onChange(fileList[0])}>
117115
{form.formState.dirtyFields.file ? (
118116
<Fragment>
119117
<i className="i-mgc-file-upload-cute-re size-5" />
120-
121118
<span className="ml-2 text-sm font-semibold opacity-80">{value.name}</span>
122119
</Fragment>
123120
) : (
124121
<Fragment>
125122
<i className="i-mgc-file-upload-cute-re size-5" />
126-
127123
<span className="ml-2 text-sm opacity-80">
128124
{t("discover.import.click_to_upload")}
129125
</span>
130126
</Fragment>
131127
)}
132-
</label>
128+
</DropZone>
133129
</FormControl>
134130
<Input
135131
{...fieldProps}

0 commit comments

Comments
 (0)