diff --git a/src/renderer/src/components/follow/import.tsx b/src/renderer/src/components/follow/import.tsx new file mode 100644 index 000000000..89638c526 --- /dev/null +++ b/src/renderer/src/components/follow/import.tsx @@ -0,0 +1,175 @@ +import { z } from "zod" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { Button } from "@renderer/components/ui/button" +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@renderer/components/ui/form" +import { Input } from "@renderer/components/ui/input" +import { useEffect } from "react" +import { useMutation } from "@tanstack/react-query" +import { Image } from "@renderer/components/ui/image" +import { FeedResponse, EntriesResponse } from "@renderer/lib/types" +import { + Card, + CardContent, + CardFooter, + CardHeader, +} from "@renderer/components/ui/card" +import { FollowButton } from "./button" +import { FollowSummary } from "../feed-summary" +import { apiFetch } from "@renderer/lib/queries/api-fetch" + +type FeedResponseList = { + id: string + url: string + title: string | null +}[] + +const formSchema = z.object({ + file: z.instanceof(File).refine((file) => file.size < 500000, { + message: "Your OPML file must be less than 500KB.", + }), +}) + +export function FollowImport() { + const form = useForm>({ + resolver: zodResolver(formSchema), + }) + const mutation = useMutation({ + mutationFn: async (file: File) => { + const formData = new FormData() + formData.append("file", file) + const { data } = await apiFetch<{ + data: { + successfulItems: FeedResponseList + conflictItems: FeedResponseList + parsedErrorItems: FeedResponseList + } + }>("/import", { + method: "POST", + body: formData, + }) + return data + }, + }) + + function onSubmit(values: z.infer) { + mutation.mutate(values.file) + } + + return ( + <> +
+ + ( + + OPML file + + + onChange(event.target.files && event.target.files[0]) + } + /> + + + + )} + /> + + + + {mutation.isSuccess && ( +
+
+ {mutation.data?.successfulItems.length || 0} feeds were successfully + imported, {mutation.data?.conflictItems.length || 0} were already + subscribed to, and {mutation.data?.parsedErrorItems.length || 0}{" "} + failed to import. +
+
+ {/* {mutation.data?.map((item) => ( + + + + + {item.docs ? ( + + + + + + ) : ( + <> + + {!!item.entries?.length && ( +
+ {item.entries + .filter((e) => !!e) + .map((entry) => ( + + {entry!.images?.[0] ? ( + + ) : ( +
+ {entry!.title} +
+ )} +
+ {entry!.title} +
+
+ ))} +
+ )} +
+ + {item.isSubscribed ? ( + + ) : ( + + )} +
+ + {item.subscriptionCount} + {" "} + Followers +
+
+ + )} +
+ ))} */} +
+
+ )} + + ) +} diff --git a/src/renderer/src/pages/(main)/follow/index.tsx b/src/renderer/src/pages/(main)/follow/index.tsx index 5b2c1f1ef..0368b8ed7 100644 --- a/src/renderer/src/pages/(main)/follow/index.tsx +++ b/src/renderer/src/pages/(main)/follow/index.tsx @@ -6,6 +6,7 @@ import { } from "@renderer/components/ui/tabs" import { Recommendations } from "@renderer/components/follow/recommendations" import { FollowForm } from "@renderer/components/follow/form" +import { FollowImport } from "@renderer/components/follow/import" export function Component() { const tabs = [ @@ -36,6 +37,10 @@ export function Component() { value: "email", disabled: true, }, + { + name: "Import", + value: "import", + }, ] return ( @@ -55,7 +60,11 @@ export function Component() { {tabs.map((tab) => ( - + {tab.value === "import" ? ( + + ) : ( + + )} ))}