diff --git a/apps/web/app/(org)/dashboard/caps/Caps.tsx b/apps/web/app/(org)/dashboard/caps/Caps.tsx index b294f513a7..e83dd11aa3 100644 --- a/apps/web/app/(org)/dashboard/caps/Caps.tsx +++ b/apps/web/app/(org)/dashboard/caps/Caps.tsx @@ -24,7 +24,7 @@ import { CapPagination } from "./components/CapPagination"; import { EmptyCapState } from "./components/EmptyCapState"; import type { FolderDataType } from "./components/Folder"; import Folder from "./components/Folder"; -import { useUploadingContext } from "./UploadingContext"; +import { useUploadingContext, useUploadingStatus } from "./UploadingContext"; export type VideoData = { id: Video.VideoId; @@ -74,7 +74,6 @@ export const Caps = ({ const previousCountRef = useRef(0); const [selectedCaps, setSelectedCaps] = useState([]); const [isDraggingCap, setIsDraggingCap] = useState(false); - const { uploadStatus } = useUploadingContext(); const anyCapSelected = selectedCaps.length > 0; @@ -258,10 +257,7 @@ export const Caps = ({ onError: () => toast.error("Failed to delete cap"), }); - const isUploading = uploadStatus !== undefined; - const uploadingCapId = - uploadStatus && "capId" in uploadStatus ? uploadStatus.capId : undefined; - + const [isUploading, uploadingCapId] = useUploadingStatus(); const visibleVideos = useMemo( () => isUploading && uploadingCapId diff --git a/apps/web/app/(org)/dashboard/caps/UploadingContext.tsx b/apps/web/app/(org)/dashboard/caps/UploadingContext.tsx index 18def19ad0..a24220c151 100644 --- a/apps/web/app/(org)/dashboard/caps/UploadingContext.tsx +++ b/apps/web/app/(org)/dashboard/caps/UploadingContext.tsx @@ -1,13 +1,10 @@ "use client"; +import { useStore } from "@tanstack/react-store"; +import { Store } from "@tanstack/store"; import type React from "react"; import { createContext, useContext, useEffect, useState } from "react"; -interface UploadingContextType { - uploadStatus: UploadStatus | undefined; - setUploadStatus: (state: UploadStatus | undefined) => void; -} - export type UploadStatus = | { status: "parsing"; @@ -32,6 +29,11 @@ export type UploadStatus = thumbnailUrl: string | undefined; }; +interface UploadingContextType { + uploadingStore: Store<{ uploadStatus?: UploadStatus }>; + setUploadStatus: (state: UploadStatus | undefined) => void; +} + const UploadingContext = createContext( undefined, ); @@ -45,13 +47,52 @@ export function useUploadingContext() { return context; } +export function useUploadingStatus() { + const { uploadingStore } = useUploadingContext(); + return useStore( + uploadingStore, + (s) => + [ + s.uploadStatus !== undefined, + s.uploadStatus && "capId" in s.uploadStatus + ? s.uploadStatus.capId + : null, + ] as const, + ); +} + export function UploadingProvider({ children }: { children: React.ReactNode }) { - const [state, setState] = useState(); + const [uploadingStore] = useState>( + () => new Store({}), + ); + + return ( + { + uploadingStore.setState((state) => ({ + ...state, + uploadStatus: status, + })); + }, + }} + > + {children} + + + + ); +} + +// Separated to prevent rerendering whole tree +function ForbidLeaveWhenUploading() { + const { uploadingStore } = useUploadingContext(); + const uploadStatus = useStore(uploadingStore, (state) => state.uploadStatus); - // Prevent the user closing the tab while uploading useEffect(() => { const handleBeforeUnload = (e: BeforeUnloadEvent) => { - if (state?.status) { + if (uploadStatus?.status) { e.preventDefault(); // Chrome requires returnValue to be set e.returnValue = ""; @@ -61,16 +102,7 @@ export function UploadingProvider({ children }: { children: React.ReactNode }) { window.addEventListener("beforeunload", handleBeforeUnload); return () => window.removeEventListener("beforeunload", handleBeforeUnload); - }, [state]); + }, [uploadStatus]); - return ( - - {children} - - ); + return null; } diff --git a/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx b/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx index 8969b420f1..160bbd9708 100644 --- a/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx @@ -452,7 +452,6 @@ export const CapCard = ({ "transition-opacity duration-200", uploadProgress && "opacity-30", )} - userId={cap.ownerId} videoId={cap.id} alt={`${cap.name} Thumbnail`} /> diff --git a/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx b/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx index 5ca9cc12f0..80931b4c38 100644 --- a/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/UploadCapButton.tsx @@ -5,6 +5,8 @@ import { userIsPro } from "@cap/utils"; import type { Folder } from "@cap/web-domain"; import { faUpload } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { type QueryClient, useQueryClient } from "@tanstack/react-query"; +import { useStore } from "@tanstack/react-store"; import { useRouter } from "next/navigation"; import { useRef, useState } from "react"; import { toast } from "sonner"; @@ -15,6 +17,7 @@ import { useUploadingContext, } from "@/app/(org)/dashboard/caps/UploadingContext"; import { UpgradeModal } from "@/components/UpgradeModal"; +import { imageUrlQuery } from "@/components/VideoThumbnail"; export const UploadCapButton = ({ size = "md", @@ -26,9 +29,11 @@ export const UploadCapButton = ({ }) => { const { user } = useDashboardContext(); const inputRef = useRef(null); - const { uploadStatus, setUploadStatus } = useUploadingContext(); + const { uploadingStore, setUploadStatus } = useUploadingContext(); + const isUploading = useStore(uploadingStore, (s) => !!s.uploadStatus); const [upgradeModalOpen, setUpgradeModalOpen] = useState(false); const router = useRouter(); + const queryClient = useQueryClient(); const handleClick = () => { if (!user) return; @@ -47,13 +52,16 @@ export const UploadCapButton = ({ const file = e.target.files?.[0]; if (!file || !user) return; - const ok = await legacyUploadCap(file, folderId, setUploadStatus); + const ok = await legacyUploadCap( + file, + folderId, + setUploadStatus, + queryClient, + ); if (ok) router.refresh(); if (inputRef.current) inputRef.current.value = ""; }; - const isUploading = !!uploadStatus; - return ( <>