11import type { VideoMetadata } from "@cap/database/types" ;
22import {
3- Button ,
43 DropdownMenu ,
54 DropdownMenuContent ,
65 DropdownMenuItem ,
@@ -11,7 +10,9 @@ import { HttpClient } from "@effect/platform";
1110import {
1211 faCheck ,
1312 faCopy ,
13+ faDownload ,
1414 faEllipsis ,
15+ faLink ,
1516 faLock ,
1617 faTrash ,
1718 faUnlock ,
@@ -25,20 +26,18 @@ import Link from "next/link";
2526import { useRouter } from "next/navigation" ;
2627import { type PropsWithChildren , useState } from "react" ;
2728import { toast } from "sonner" ;
28- import { downloadVideo } from "@/actions/videos/download" ;
2929import { ConfirmationDialog } from "@/app/(org)/dashboard/_components/ConfirmationDialog" ;
3030import { useDashboardContext } from "@/app/(org)/dashboard/Contexts" ;
3131import ProgressCircle , {
3232 useUploadProgress ,
3333} from "@/app/s/[videoId]/_components/ProgressCircle" ;
34- import { Tooltip } from "@/components/Tooltip" ;
3534import { VideoThumbnail } from "@/components/VideoThumbnail" ;
3635import { useEffectMutation } from "@/lib/EffectRuntime" ;
3736import { withRpc } from "@/lib/Rpcs" ;
3837import { PasswordDialog } from "../PasswordDialog" ;
3938import { SharingDialog } from "../SharingDialog" ;
4039import { CapCardAnalytics } from "./CapCardAnalytics" ;
41- import { CapCardButtons } from "./CapCardButtons " ;
40+ import { CapCardButton } from "./CapCardButton " ;
4241import { CapCardContent } from "./CapCardContent" ;
4342
4443export interface CapCardProps extends PropsWithChildren {
@@ -305,51 +304,102 @@ export const CapCard = ({
305304 { anyCapSelected && ! sharedCapCard && (
306305 < div className = "absolute inset-0 z-10" onClick = { handleCardClick } />
307306 ) }
308- { ! sharedCapCard && (
309- < div
310- className = { clsx (
311- "flex absolute duration-200" ,
312- anyCapSelected
313- ? "opacity-0"
314- : isDropdownOpen
315- ? "opacity-100"
316- : "opacity-0 group-hover:opacity-100" ,
317- "top-2 right-2 flex-col gap-2 z-[20]" ,
318- ) }
319- >
320- < CapCardButtons
321- capId = { cap . id }
322- copyPressed = { copyPressed }
323- isDownloading = { downloadMutation . isPending }
324- customDomain = { customDomain }
325- domainVerified = { domainVerified }
326- handleCopy = { handleCopy }
327- handleDownload = { handleDownload }
328- />
329307
330- < DropdownMenu modal = { false } onOpenChange = { setIsDropdownOpen } >
331- < Tooltip content = "More options" >
332- < DropdownMenuTrigger asChild >
333- < Button
334- onClick = { ( e ) => {
335- e . stopPropagation ( ) ;
336- } }
337- className = { clsx (
338- "!size-8 hover:bg-gray-5 hover:border-gray-7 rounded-full min-w-fit !p-0 delay-75" ,
339- isDropdownOpen ? "bg-gray-5 border-gray-7" : "" ,
340- ) }
341- variant = "white"
342- size = "sm"
343- aria-label = "More options"
308+ < div
309+ className = { clsx (
310+ "flex absolute duration-200" ,
311+ anyCapSelected
312+ ? "opacity-0"
313+ : isDropdownOpen
314+ ? "opacity-100"
315+ : "opacity-0 group-hover:opacity-100" ,
316+ "top-2 right-2 flex-col gap-2 z-[20]" ,
317+ ) }
318+ >
319+ < CapCardButton
320+ tooltipContent = "Copy link"
321+ onClick = { ( e ) => {
322+ e . stopPropagation ( ) ;
323+ handleCopy ( cap . id ) ;
324+ } }
325+ className = "delay-0"
326+ icon = { ( ) => {
327+ return ! copyPressed ? (
328+ < FontAwesomeIcon
329+ className = "text-gray-12 size-4"
330+ icon = { faLink }
331+ />
332+ ) : (
333+ < svg
334+ xmlns = "http://www.w3.org/2000/svg"
335+ width = "24"
336+ height = "24"
337+ viewBox = "0 0 24 24"
338+ fill = "none"
339+ stroke = "currentColor"
340+ strokeWidth = "2"
341+ strokeLinecap = "round"
342+ strokeLinejoin = "round"
343+ className = "text-gray-12 size-5 svgpathanimation"
344+ >
345+ < path d = "M20 6 9 17l-5-5" />
346+ </ svg >
347+ ) ;
348+ } }
349+ />
350+ < CapCardButton
351+ tooltipContent = "Download Cap"
352+ onClick = { ( e ) => {
353+ e . stopPropagation ( ) ;
354+ handleDownload ( ) ;
355+ } }
356+ disabled = { downloadMutation . isPending }
357+ className = "delay-25"
358+ icon = { ( ) => {
359+ return downloadMutation . isPending ? (
360+ < div className = "animate-spin size-3" >
361+ < svg
362+ className = "size-3"
363+ xmlns = "http://www.w3.org/2000/svg"
364+ fill = "none"
365+ viewBox = "0 0 24 24"
366+ aria-hidden = "true"
344367 >
345- < FontAwesomeIcon
346- className = "text-gray-12 size-4"
347- icon = { faEllipsis }
348- />
349- </ Button >
350- </ DropdownMenuTrigger >
351- </ Tooltip >
368+ < circle
369+ className = "opacity-25"
370+ cx = "12"
371+ cy = "12"
372+ r = "10"
373+ stroke = "currentColor"
374+ strokeWidth = "4"
375+ > </ circle >
376+ < path
377+ className = "opacity-75"
378+ fill = "currentColor"
379+ d = "m2 12c0-5.523 4.477-10 10-10v3c-3.866 0-7 3.134-7 7s3.134 7 7 7 7-3.134 7-7c0-1.457-.447-2.808-1.208-3.926l2.4-1.6c1.131 1.671 1.808 3.677 1.808 5.526 0 5.523-4.477 10-10 10s-10-4.477-10-10z"
380+ > </ path >
381+ </ svg >
382+ </ div >
383+ ) : (
384+ < FontAwesomeIcon
385+ className = "text-gray-12 size-3"
386+ icon = { faDownload }
387+ />
388+ ) ;
389+ } }
390+ />
352391
392+ { isOwner && (
393+ < DropdownMenu modal = { false } onOpenChange = { setIsDropdownOpen } >
394+ < DropdownMenuTrigger >
395+ < CapCardButton
396+ tooltipContent = "More options"
397+ className = "delay-75"
398+ icon = { ( ) => (
399+ < FontAwesomeIcon className = "size-4" icon = { faEllipsis } />
400+ ) }
401+ />
402+ </ DropdownMenuTrigger >
353403 < DropdownMenuContent align = "end" sideOffset = { 5 } >
354404 < DropdownMenuItem
355405 onClick = { ( ) => {
@@ -392,19 +442,21 @@ export const CapCard = ({
392442 </ DropdownMenuItem >
393443 </ DropdownMenuContent >
394444 </ DropdownMenu >
395- < ConfirmationDialog
396- open = { confirmOpen }
397- icon = { < FontAwesomeIcon icon = { faVideo } /> }
398- title = "Delete Cap"
399- description = { `Are you sure you want to delete the cap "${ cap . name } "? This action cannot be undone.` }
400- confirmLabel = { deleteMutation . isPending ? "Deleting..." : "Delete" }
401- cancelLabel = "Cancel"
402- loading = { deleteMutation . isPending }
403- onConfirm = { ( ) => deleteMutation . mutate ( ) }
404- onCancel = { ( ) => setConfirmOpen ( false ) }
405- />
406- </ div >
407- ) }
445+ ) }
446+
447+ < ConfirmationDialog
448+ open = { confirmOpen }
449+ icon = { < FontAwesomeIcon icon = { faVideo } /> }
450+ title = "Delete Cap"
451+ description = { `Are you sure you want to delete the cap "${ cap . name } "? This action cannot be undone.` }
452+ confirmLabel = { deleteMutation . isPending ? "Deleting..." : "Delete" }
453+ cancelLabel = "Cancel"
454+ loading = { deleteMutation . isPending }
455+ onConfirm = { ( ) => deleteMutation . mutate ( ) }
456+ onCancel = { ( ) => setConfirmOpen ( false ) }
457+ />
458+ </ div >
459+
408460 { ! sharedCapCard && onSelectToggle && (
409461 < div
410462 className = { clsx (
0 commit comments