From d1ad84be48bb3b6914c0d478d13f92861889c466 Mon Sep 17 00:00:00 2001 From: kamtschatka Date: Sun, 19 May 2024 13:13:54 +0200 Subject: feature(web): Allow adding multiple URLs at once #158 (#167) Added a reusable dialog opening a dialog that allows you to decide if you want to import multiple URLs at once if you provide only that Co-authored-by: kamtschatka --- .../components/dashboard/bookmarks/EditorCard.tsx | 79 ++++++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) (limited to 'apps/web/components/dashboard/bookmarks') diff --git a/apps/web/components/dashboard/bookmarks/EditorCard.tsx b/apps/web/components/dashboard/bookmarks/EditorCard.tsx index 7c036c04..8425f669 100644 --- a/apps/web/components/dashboard/bookmarks/EditorCard.tsx +++ b/apps/web/components/dashboard/bookmarks/EditorCard.tsx @@ -1,9 +1,10 @@ import type { SubmitErrorHandler, SubmitHandler } from "react-hook-form"; -import { useEffect, useImperativeHandle, useRef } from "react"; +import React, { useEffect, useImperativeHandle, useRef } from "react"; import Link from "next/link"; import { ActionButton } from "@/components/ui/action-button"; import { Form, FormControl, FormItem } from "@/components/ui/form"; import InfoTooltip from "@/components/ui/info-tooltip"; +import MultipleChoiceDialog from "@/components/ui/multiple-choice-dialog"; import { Separator } from "@/components/ui/separator"; import { Textarea } from "@/components/ui/textarea"; import { toast } from "@/components/ui/use-toast"; @@ -34,9 +35,17 @@ function useFocusOnKeyPress(inputRef: React.RefObject) { }, [inputRef]); } +interface MultiUrlImportState { + urls: URL[]; + text: string; +} + export default function EditorCard({ className }: { className?: string }) { const inputRef = useRef(null); + const [multiUrlImportState, setMultiUrlImportState] = + React.useState(null); + const demoMode = !!useClientConfig().demoMode; const formSchema = z.object({ text: z.string(), @@ -76,14 +85,31 @@ export default function EditorCard({ className }: { className?: string }) { }, }); - const onSubmit: SubmitHandler> = (data) => { - const text = data.text.trim(); - try { - const url = new URL(text); + function tryToImportUrls(text: string): void { + const lines = text.split("\n"); + const urls: URL[] = []; + for (const line of lines) { + // parsing can also throw an exception, but will be caught outside + const url = new URL(line); if (url.protocol != "http:" && url.protocol != "https:") { throw new Error("Invalid URL"); } + urls.push(url); + } + + if (urls.length === 1) { + // Only 1 url in the textfield --> simply import it mutate({ type: "link", url: text }); + return; + } + // multiple urls found --> ask the user if it should be imported as multiple URLs or as a text bookmark + setMultiUrlImportState({ urls, text }); + } + + const onSubmit: SubmitHandler> = (data) => { + const text = data.text.trim(); + try { + tryToImportUrls(text); } catch (e) { // Not a URL mutate({ type: "text", text }); @@ -150,6 +176,49 @@ export default function EditorCard({ className }: { className?: string }) { : "Press ⌘ + Enter to Save" : "Save"} + + {multiUrlImportState && ( + { + if (!open) { + setMultiUrlImportState(null); + } + }} + actionButtons={[ + () => ( + { + mutate({ type: "text", text: multiUrlImportState.text }); + setMultiUrlImportState(null); + }} + > + Import as Text Bookmark + + ), + () => ( + { + multiUrlImportState.urls.forEach((url) => + mutate({ type: "link", url: url.toString() }), + ); + setMultiUrlImportState(null); + }} + > + Import as separate Bookmarks + + ), + ]} + > + )} ); -- cgit v1.2.3-70-g09d2