From 1d06b7d0bc1ae7e89cee8d51d14bd3729b80db04 Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:54:22 +0300 Subject: [PATCH 1/2] Fix deleting caps within folders and duplicate refresh route --- apps/web/app/(org)/dashboard/caps/Caps.tsx | 60 ++++++++------- .../caps/components/CapCard/CapCard.tsx | 9 ++- .../[id]/components/FolderVideosSection.tsx | 73 ++++++++++++------- 3 files changed, 86 insertions(+), 56 deletions(-) diff --git a/apps/web/app/(org)/dashboard/caps/Caps.tsx b/apps/web/app/(org)/dashboard/caps/Caps.tsx index 161f451dac..411c4ef503 100644 --- a/apps/web/app/(org)/dashboard/caps/Caps.tsx +++ b/apps/web/app/(org)/dashboard/caps/Caps.tsx @@ -8,7 +8,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { useQuery } from "@tanstack/react-query"; import { Effect, Exit } from "effect"; import { useRouter, useSearchParams } from "next/navigation"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { useEffectMutation } from "@/lib/EffectRuntime"; import { Rpc, withRpc } from "@/lib/Rpcs"; @@ -266,6 +266,14 @@ export const Caps = ({ }, }); + const visibleVideos = useMemo( + () => + isUploading && uploadingCapId + ? data.filter((video) => video.id !== uploadingCapId) + : data, + [data, isUploading, uploadingCapId], + ); + if (count === 0) return ; return ( @@ -312,7 +320,7 @@ export const Caps = ({ )} - {data.length > 0 && ( + {visibleVideos.length > 0 && ( <>

Videos

@@ -322,31 +330,29 @@ export const Caps = ({ {isUploading && ( )} - {data - .filter((cap) => !isUploading || cap.id !== uploadingCapId) - .map((cap) => { - return ( - { - if (selectedCaps.length > 0) { - deleteCaps(selectedCaps); - } else { - deleteCap(cap.id); - } - }} - userId={user?.id} - customDomain={customDomain} - isLoadingAnalytics={isLoadingAnalytics} - domainVerified={domainVerified} - isSelected={selectedCaps.includes(cap.id)} - anyCapSelected={anyCapSelected} - onSelectToggle={() => handleCapSelection(cap.id)} - /> - ); - })} + {visibleVideos.map((video) => { + return ( + { + if (selectedCaps.length > 0) { + deleteCaps(selectedCaps); + } else { + deleteCap(video.id); + } + }} + userId={user?.id} + customDomain={customDomain} + isLoadingAnalytics={isLoadingAnalytics} + domainVerified={domainVerified} + isSelected={selectedCaps.includes(video.id)} + anyCapSelected={anyCapSelected} + onSelectToggle={() => handleCapSelection(video.id)} + /> + ); + })}
)} 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 98374c03cd..efb1b4b6f2 100644 --- a/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx +++ b/apps/web/app/(org)/dashboard/caps/components/CapCard/CapCard.tsx @@ -144,6 +144,9 @@ export const CapCard = ({ const duplicateMutation = useEffectMutation({ mutationFn: () => withRpc((r) => r.VideoDuplicate(cap.id)), + onSuccess: () => { + router.refresh(); + }, }); const handleSharingUpdated = () => { @@ -336,13 +339,13 @@ export const CapCard = ({ + onClick={() => { toast.promise(duplicateMutation.mutateAsync(), { loading: "Duplicating cap...", success: "Cap duplicated successfully", error: "Failed to duplicate cap", - }) - } + }); + }} disabled={duplicateMutation.isPending} className="flex gap-2 items-center rounded-lg" > diff --git a/apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx b/apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx index 2ad39372b6..1228636d20 100644 --- a/apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx +++ b/apps/web/app/(org)/dashboard/folder/[id]/components/FolderVideosSection.tsx @@ -4,11 +4,11 @@ import type { Video } from "@cap/web-domain"; import { useQuery } from "@tanstack/react-query"; import { Effect, Exit } from "effect"; import { useRouter } from "next/navigation"; -import { useRef, useState } from "react"; +import { useMemo, useRef, useState } from "react"; import { toast } from "sonner"; import { useDashboardContext } from "@/app/(org)/dashboard/Contexts"; import { useEffectMutation } from "@/lib/EffectRuntime"; -import { Rpc } from "@/lib/Rpcs"; +import { Rpc, withRpc } from "@/lib/Rpcs"; import type { VideoData } from "../../../caps/Caps"; import { CapCard } from "../../../caps/components/CapCard/CapCard"; import { SelectedCapsBar } from "../../../caps/components/SelectedCapsBar"; @@ -28,12 +28,12 @@ export default function FolderVideosSection({ }: FolderVideosSectionProps) { const router = useRouter(); const { isUploading, uploadingCapId } = useUploadingContext(); - const { activeOrganization, user } = useDashboardContext(); + const { user } = useDashboardContext(); const [selectedCaps, setSelectedCaps] = useState([]); const previousCountRef = useRef(0); - const deleteCaps = useEffectMutation({ + const { mutate: deleteCaps, isPending: isDeletingCaps } = useEffectMutation({ mutationFn: Effect.fn(function* (ids: Video.VideoId[]) { if (ids.length === 0) return; @@ -63,9 +63,7 @@ export default function FolderVideosSection({ }).pipe(Effect.fork); toast.promise(Effect.runPromise(fiber.await.pipe(Effect.flatten)), { - loading: `Deleting ${selectedCaps.length} cap${ - selectedCaps.length === 1 ? "" : "s" - }...`, + loading: `Deleting ${ids.length} cap${ids.length === 1 ? "" : "s"}...`, success: (data) => { if (data.error) { return `Successfully deleted ${data.success} cap${ @@ -90,6 +88,17 @@ export default function FolderVideosSection({ }, }); + const { mutate: deleteCap, isPending: isDeletingCap } = useEffectMutation({ + mutationFn: (id: Video.VideoId) => withRpc((r) => r.VideoDelete(id)), + onSuccess: () => { + toast.success("Cap deleted successfully"); + router.refresh(); + }, + onError: () => { + toast.error("Failed to delete cap"); + }, + }); + const handleCapSelection = (capId: Video.VideoId) => { setSelectedCaps((prev) => { const newSelection = prev.includes(capId) @@ -146,6 +155,14 @@ export default function FolderVideosSection({ refetchOnMount: true, }); + const visibleVideos = useMemo( + () => + isUploading && uploadingCapId + ? initialVideos.filter((video) => video.id !== uploadingCapId) + : initialVideos, + [initialVideos, isUploading, uploadingCapId], + ); + const analytics = analyticsData || {}; return ( @@ -154,7 +171,7 @@ export default function FolderVideosSection({

Videos

- {initialVideos.length === 0 && !isUploading ? ( + {visibleVideos.length === 0 && !isUploading ? (

No videos in this folder yet. Drag and drop into the folder or upload. @@ -164,30 +181,34 @@ export default function FolderVideosSection({ {isUploading && ( )} - {initialVideos - .filter((cap) => !isUploading || cap.id !== uploadingCapId) - .map((video) => ( - 0} - isDeleting={deleteCaps.isPending} - onSelectToggle={() => handleCapSelection(video.id)} - onDelete={() => deleteCaps.mutateAsync(selectedCaps)} - /> - ))} + {visibleVideos.map((video) => ( + 0} + isDeleting={isDeletingCaps || isDeletingCap} + onSelectToggle={() => handleCapSelection(video.id)} + onDelete={() => { + if (selectedCaps.length > 0) { + deleteCaps(selectedCaps); + } else { + deleteCap(video.id); + } + }} + /> + ))} )}

deleteCaps.mutateAsync(selectedCaps)} - isDeleting={deleteCaps.isPending} + deleteSelectedCaps={() => deleteCaps(selectedCaps)} + isDeleting={isDeletingCaps || isDeletingCap} /> ); From 98f5e88ff2c8d87281a4cc9973f0bf3e6caf9ade Mon Sep 17 00:00:00 2001 From: ameer2468 <33054370+ameer2468@users.noreply.github.com> Date: Wed, 10 Sep 2025 11:56:47 +0300 Subject: [PATCH 2/2] Update schema.ts --- packages/database/schema.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/database/schema.ts b/packages/database/schema.ts index 8e6f908dab..41c4df63e4 100644 --- a/packages/database/schema.ts +++ b/packages/database/schema.ts @@ -72,8 +72,8 @@ export const users = mysqlTable( pauseViews: boolean; pauseReactions: boolean; }; - // For analytics. - // Adding in preferences so we don't have to + // For analytics. + // Adding in preferences so we don't have to // add a new column and can be dynamic going forward. trackedEvents?: { user_signed_up?: boolean;