From ca92ec4eecd40883a26bcd5287d453684304cc47 Mon Sep 17 00:00:00 2001 From: mbret Date: Sun, 19 Jan 2025 13:54:16 +0100 Subject: [PATCH] fleanup dropbox upload --- .../web/src/plugins/dropbox/UploadBook.tsx | 86 ++++++++----------- .../plugins/dropbox/lib/useDropboxChoose.ts | 37 ++++++++ 2 files changed, 74 insertions(+), 49 deletions(-) create mode 100644 packages/web/src/plugins/dropbox/lib/useDropboxChoose.ts diff --git a/packages/web/src/plugins/dropbox/UploadBook.tsx b/packages/web/src/plugins/dropbox/UploadBook.tsx index 6fb5571f..d869fe1a 100644 --- a/packages/web/src/plugins/dropbox/UploadBook.tsx +++ b/packages/web/src/plugins/dropbox/UploadBook.tsx @@ -2,71 +2,59 @@ * @see https://www.dropbox.com/developers/chooser * @see https://www.dropbox.com/lp/developers/reference/oauth-guide */ -import { FC, memo, useEffect, useRef } from "react" -import { Report } from "../../debug/report.shared" +import { FC, memo } from "react" import { BlockingScreen } from "../../common/BlockingBackdrop" import { useAddBook } from "../../books/helpers" import { useDataSourceHelpers } from "../../dataSources/helpers" import { UNIQUE_RESOURCE_IDENTIFIER } from "./constants" -import { useIsMounted } from "./lib/useIsMounted" -import { READER_ACCEPTED_EXTENSIONS } from "@oboku/shared" +import { useDropboxChoose } from "./lib/useDropboxChoose" +import { useMutation$ } from "reactjrx" +import { defaultIfEmpty, from } from "rxjs" +import { useMountOnce } from "../../common/useMountOnce" export const UploadBook: FC<{ onClose: () => void }> = memo(({ onClose }) => { const [addBook] = useAddBook() - const isMounted = useIsMounted() - const isOpened = useRef(false) const { generateResourceId } = useDataSourceHelpers( UNIQUE_RESOURCE_IDENTIFIER ) - useEffect(() => { - if (isOpened.current) return + const { mutateAsync: addBooks } = useMutation$({ + mutationFn: (files: readonly Dropbox.ChooserFile[]) => { + const promises = files.map((doc) => + addBook({ + book: { + metadata: [ + { + type: "link", + title: doc.name + } + ] + }, + link: { + book: null, + data: null, + resourceId: generateResourceId(doc.id), + type: `dropbox`, + createdAt: new Date().toISOString(), + modifiedAt: null + } + }) + ) - if (window.Dropbox) { - isOpened.current = true - - window.Dropbox.choose({ - multiselect: true, - extensions: READER_ACCEPTED_EXTENSIONS, - linkType: "direct", - cancel: function () { - if (!isMounted()) return + return from(Promise.all(promises)).pipe(defaultIfEmpty(null)) + } + }) - onClose() - }, - success: (files) => { - if (!isMounted()) return + const { choose } = useDropboxChoose({ + onSettled: onClose, + onSuccess: addBooks + }) - Promise.all( - files.map((doc) => - addBook({ - book: { - metadata: [ - { - type: "link", - title: doc.name - } - ] - }, - link: { - book: null, - data: null, - resourceId: generateResourceId(doc.id), - type: `dropbox`, - createdAt: new Date().toISOString(), - modifiedAt: null - } - }).catch(Report.error) - ) - ).then(() => { - onClose() - }) - } - }) - } - }, [onClose, generateResourceId, addBook, isOpened, isMounted]) + useMountOnce(() => { + choose() + }) return ( <> diff --git a/packages/web/src/plugins/dropbox/lib/useDropboxChoose.ts b/packages/web/src/plugins/dropbox/lib/useDropboxChoose.ts new file mode 100644 index 00000000..81e9e9a4 --- /dev/null +++ b/packages/web/src/plugins/dropbox/lib/useDropboxChoose.ts @@ -0,0 +1,37 @@ +import { READER_ACCEPTED_EXTENSIONS } from "@oboku/shared" +import { useCallback } from "react" +import { useLiveRef } from "reactjrx" + +export const useDropboxChoose = (options: { + onCancel?: () => void + onSuccess: (files: readonly Dropbox.ChooserFile[]) => Promise + onSettled: () => void +}) => { + const optionsRef = useLiveRef(options) + + const choose = useCallback(() => { + if (!window.Dropbox) { + throw new Error("Dropbox is not available") + } + + window.Dropbox.choose({ + multiselect: true, + extensions: READER_ACCEPTED_EXTENSIONS, + linkType: "direct", + cancel: () => { + optionsRef.current?.onCancel?.() + optionsRef.current?.onSettled?.() + }, + success: (files) => { + const promise = + optionsRef.current?.onSuccess(files) ?? Promise.resolve(null) + + promise.finally(() => { + optionsRef.current?.onSettled?.() + }) + } + }) + }, [optionsRef]) + + return { choose } +}