From 608f8dcc0fed91bc3a4c8f44f4bddf49b440e53e Mon Sep 17 00:00:00 2001
From: lawvs <18554747+lawvs@users.noreply.github.com>
Date: Wed, 4 Dec 2024 18:29:06 +0800
Subject: [PATCH] feat: support drag-and-drop when importing opml
---
apps/renderer/src/components/ui/drop-zone.tsx | 71 +++++++++++++++++++
apps/renderer/src/modules/discover/import.tsx | 10 +--
2 files changed, 74 insertions(+), 7 deletions(-)
create mode 100644 apps/renderer/src/components/ui/drop-zone.tsx
diff --git a/apps/renderer/src/components/ui/drop-zone.tsx b/apps/renderer/src/components/ui/drop-zone.tsx
new file mode 100644
index 0000000000..e0d8a46028
--- /dev/null
+++ b/apps/renderer/src/components/ui/drop-zone.tsx
@@ -0,0 +1,71 @@
+import type { DragEvent, ReactNode } from "react"
+import { useCallback, useRef, useState } from "react"
+
+// Ported from https://github.com/react-dropzone/react-dropzone/issues/753#issuecomment-774782919
+const useDragAndDrop = ({ callback }: { callback: (file: FileList) => void | Promise }) => {
+ const [isDragging, setIsDragging] = useState(false)
+ const dragCounter = useRef(0)
+
+ const onDrop = useCallback(
+ async (event: DragEvent) => {
+ event.preventDefault()
+ setIsDragging(false)
+ if (event.dataTransfer && event.dataTransfer.files && event.dataTransfer.files.length > 0) {
+ dragCounter.current = 0
+ await callback(event.dataTransfer.files)
+ event.dataTransfer.clearData()
+ }
+ },
+ [callback],
+ )
+
+ const onDragEnter = useCallback((event: DragEvent) => {
+ event.preventDefault()
+ dragCounter.current++
+ setIsDragging(true)
+ }, [])
+
+ const onDragOver = useCallback((event: DragEvent) => {
+ event.preventDefault()
+ }, [])
+
+ const onDragLeave = useCallback((event: DragEvent) => {
+ event.preventDefault()
+ dragCounter.current--
+ if (dragCounter.current > 0) return
+ setIsDragging(false)
+ }, [])
+
+ return {
+ isDragging,
+
+ dragHandlers: {
+ onDrop,
+ onDragOver,
+ onDragEnter,
+ onDragLeave,
+ },
+ }
+}
+
+export const DropZone = ({
+ onDrop,
+ children,
+}: {
+ onDrop: (file: FileList) => void | Promise
+ children?: ReactNode
+}) => {
+ const { isDragging, dragHandlers } = useDragAndDrop({ callback: onDrop })
+
+ return (
+
+ )
+}
diff --git a/apps/renderer/src/modules/discover/import.tsx b/apps/renderer/src/modules/discover/import.tsx
index 17c757f6e6..7a9d1788f3 100644
--- a/apps/renderer/src/modules/discover/import.tsx
+++ b/apps/renderer/src/modules/discover/import.tsx
@@ -17,6 +17,7 @@ import { useForm } from "react-hook-form"
import { Trans, useTranslation } from "react-i18next"
import { z } from "zod"
+import { DropZone } from "~/components/ui/drop-zone"
import { apiFetch } from "~/lib/api-fetch"
import { toastFetchError } from "~/lib/error-parser"
import { Queries } from "~/queries"
@@ -110,26 +111,21 @@ export function DiscoverImport({ isInit = false }: { isInit?: boolean }) {
{isInit ? t("discover.import.new_import_opml") : t("discover.import.opml")}
-
+