diff options
| author | Mohamed Bassem <me@mbassem.com> | 2025-10-04 13:40:24 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-04 13:40:24 +0100 |
| commit | 4a580d713621f99abb8baabc9b847ce039d44842 (patch) | |
| tree | a2aa6f3ae8045ad50a9316624e2a7028dd098c6b /apps/web/lib/hooks | |
| parent | 5e331a7d5b8d9666812170547574804d8b6da741 (diff) | |
| download | karakeep-4a580d713621f99abb8baabc9b847ce039d44842.tar.zst | |
feat: Revamp import experience (#2001)
* WIP: import v2
* remove new session button
* don't redirect after import
* store and lint to root list
* models + tests
* redesign the progress
* simplify the import session for ow
* drop status from session schema
* split the import session page
* i18n
* fix test
* remove pagination
* fix some colors in darkmode
* one last fix
* add privacy filter
* privacy check
* fix interactivity of import progress
* fix test
Diffstat (limited to 'apps/web/lib/hooks')
| -rw-r--r-- | apps/web/lib/hooks/useBookmarkImport.ts | 21 | ||||
| -rw-r--r-- | apps/web/lib/hooks/useImportSessions.ts | 62 |
2 files changed, 75 insertions, 8 deletions
diff --git a/apps/web/lib/hooks/useBookmarkImport.ts b/apps/web/lib/hooks/useBookmarkImport.ts index de515677..a4ebdd9c 100644 --- a/apps/web/lib/hooks/useBookmarkImport.ts +++ b/apps/web/lib/hooks/useBookmarkImport.ts @@ -1,7 +1,6 @@ "use client"; import { useState } from "react"; -import { useRouter } from "next/navigation"; import { toast } from "@/components/ui/use-toast"; import { useTranslation } from "@/lib/i18n/client"; import { useMutation } from "@tanstack/react-query"; @@ -24,6 +23,8 @@ import { MAX_BOOKMARK_TITLE_LENGTH, } from "@karakeep/shared/types/bookmarks"; +import { useCreateImportSession } from "./useImportSessions"; + export interface ImportProgress { done: number; total: number; @@ -31,12 +32,12 @@ export interface ImportProgress { export function useBookmarkImport() { const { t } = useTranslation(); - const router = useRouter(); const [importProgress, setImportProgress] = useState<ImportProgress | null>( null, ); + const { mutateAsync: createImportSession } = useCreateImportSession(); const { mutateAsync: createBookmark } = useCreateBookmarkWithPostHook(); const { mutateAsync: createList } = useCreateBookmarkList(); const { mutateAsync: addToList } = useAddBookmarkToList(); @@ -56,8 +57,12 @@ export function useBookmarkImport() { source, rootListName: t("settings.import.imported_bookmarks"), deps: { - createList: createList, - createBookmark: async (bookmark: ParsedBookmark) => { + createImportSession, + createList, + createBookmark: async ( + bookmark: ParsedBookmark, + sessionId: string, + ) => { if (bookmark.content === undefined) { throw new Error("Content is undefined"); } @@ -69,6 +74,7 @@ export function useBookmarkImport() { : undefined, note: bookmark.notes, archived: bookmark.archived, + importSessionId: sessionId, ...(bookmark.content.type === BookmarkTypes.LINK ? { type: BookmarkTypes.LINK, @@ -120,6 +126,8 @@ export function useBookmarkImport() { return result; }, onSuccess: async (result) => { + setImportProgress(null); + if (result.counts.total === 0) { toast({ description: "No bookmarks found in the file." }); return; @@ -127,7 +135,7 @@ export function useBookmarkImport() { const { successes, failures, alreadyExisted } = result.counts; if (successes > 0 || alreadyExisted > 0) { toast({ - description: `Imported ${successes} bookmarks and skipped ${alreadyExisted} bookmarks that already existed`, + description: `Imported ${successes} bookmarks into import session. Background processing will start automatically.`, variant: "default", }); } @@ -137,9 +145,6 @@ export function useBookmarkImport() { variant: "destructive", }); } - - if (result.rootListId) - router.push(`/dashboard/lists/${result.rootListId}`); }, onError: (error) => { setImportProgress(null); diff --git a/apps/web/lib/hooks/useImportSessions.ts b/apps/web/lib/hooks/useImportSessions.ts new file mode 100644 index 00000000..cee99bbc --- /dev/null +++ b/apps/web/lib/hooks/useImportSessions.ts @@ -0,0 +1,62 @@ +"use client"; + +import { toast } from "@/components/ui/use-toast"; + +import { api } from "@karakeep/shared-react/trpc"; + +export function useCreateImportSession() { + const apiUtils = api.useUtils(); + + return api.importSessions.createImportSession.useMutation({ + onSuccess: () => { + apiUtils.importSessions.listImportSessions.invalidate(); + }, + onError: (error) => { + toast({ + description: error.message || "Failed to create import session", + variant: "destructive", + }); + }, + }); +} + +export function useListImportSessions() { + return api.importSessions.listImportSessions.useQuery( + {}, + { + select: (data) => data.sessions, + }, + ); +} + +export function useImportSessionStats(importSessionId: string) { + return api.importSessions.getImportSessionStats.useQuery( + { + importSessionId, + }, + { + refetchInterval: 5000, // Refetch every 5 seconds to show progress + enabled: !!importSessionId, + }, + ); +} + +export function useDeleteImportSession() { + const apiUtils = api.useUtils(); + + return api.importSessions.deleteImportSession.useMutation({ + onSuccess: () => { + apiUtils.importSessions.listImportSessions.invalidate(); + toast({ + description: "Import session deleted successfully", + variant: "default", + }); + }, + onError: (error) => { + toast({ + description: error.message || "Failed to delete import session", + variant: "destructive", + }); + }, + }); +} |
