|
1 | 1 | "use client"; |
2 | 2 |
|
3 | 3 | import { LogoSpinner } from "@cap/ui"; |
4 | | -import { |
5 | | - calculateStrokeDashoffset, |
6 | | - getProgressCircleConfig, |
7 | | - getUploadStatus, |
8 | | -} from "@cap/utils"; |
| 4 | +import { getUploadStatus } from "@cap/utils"; |
| 5 | +import type { Video } from "@cap/web-domain"; |
| 6 | +import { faVideo } from "@fortawesome/free-solid-svg-icons"; |
| 7 | +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; |
| 8 | +import ProgressCircle, { |
| 9 | + useUploadProgress, |
| 10 | +} from "@/app/s/[videoId]/_components/ProgressCircle"; |
9 | 11 | import { useUploadingContext } from "../UploadingContext"; |
10 | 12 |
|
11 | 13 | export const UploadPlaceholderCard = () => { |
12 | | - const { uploadingThumbnailUrl, uploadProgress } = useUploadingContext(); |
13 | | - const { circumference } = getProgressCircleConfig(); |
| 14 | + const { uploadingCapId, uploadingThumbnailUrl, uploadProgress } = |
| 15 | + useUploadingContext(); |
14 | 16 | const status = getUploadStatus(uploadProgress); |
15 | | - const strokeDashoffset = calculateStrokeDashoffset( |
16 | | - uploadProgress, |
17 | | - circumference, |
| 17 | + |
| 18 | + // Always call useUploadProgress hook, but pass empty string when no capId |
| 19 | + const progressStatus = useUploadProgress( |
| 20 | + (uploadingCapId as Video.VideoId) || ("" as Video.VideoId), |
| 21 | + !!uploadingCapId, |
18 | 22 | ); |
19 | 23 |
|
20 | 24 | return ( |
21 | | - <div className="flex flex-col gap-4 w-full h-full rounded-xl bg-gray-1 border-gray-3 border-[1px]"> |
22 | | - <div className="overflow-hidden relative w-full bg-black rounded-t-xl border-b border-gray-3 aspect-video group"> |
23 | | - {uploadingThumbnailUrl ? ( |
24 | | - <img |
25 | | - src={uploadingThumbnailUrl} |
26 | | - alt="Uploading thumbnail" |
27 | | - className="object-cover w-full h-full" |
28 | | - /> |
29 | | - ) : ( |
30 | | - <div className="flex justify-center items-center w-full h-full"> |
31 | | - <LogoSpinner className="w-8 h-8 animate-spin" /> |
32 | | - </div> |
33 | | - )} |
34 | | - |
35 | | - <div className="absolute inset-0 transition-all duration-300 bg-black/60"></div> |
36 | | - |
37 | | - <div className="flex absolute bottom-3 left-3 gap-2 items-center"> |
38 | | - <span className="text-sm font-semibold text-white">{status}</span> |
39 | | - <svg className="w-4 h-4 transform -rotate-90" viewBox="0 0 20 20"> |
40 | | - <circle |
41 | | - cx="10" |
42 | | - cy="10" |
43 | | - r="8" |
44 | | - stroke="currentColor" |
45 | | - strokeWidth="3" |
46 | | - fill="none" |
47 | | - className="text-white/30" |
48 | | - /> |
49 | | - <circle |
50 | | - cx="10" |
51 | | - cy="10" |
52 | | - r="8" |
53 | | - stroke="currentColor" |
54 | | - strokeWidth="3" |
55 | | - fill="none" |
56 | | - strokeLinecap="round" |
57 | | - className="text-white transition-all duration-200 ease-out" |
58 | | - style={{ |
59 | | - strokeDasharray: `${circumference} ${circumference}`, |
60 | | - strokeDashoffset: `${strokeDashoffset}`, |
61 | | - }} |
| 25 | + <div className="flex relative transition-colors duration-200 flex-col gap-4 w-full h-full rounded-xl cursor-default bg-gray-1 border border-gray-3 group border-px"> |
| 26 | + <div className="relative"> |
| 27 | + <div className="overflow-hidden relative w-full bg-black rounded-t-xl aspect-video"> |
| 28 | + {uploadingThumbnailUrl ? ( |
| 29 | + // eslint-disable-next-line @next/next/no-img-element |
| 30 | + <img |
| 31 | + src={uploadingThumbnailUrl} |
| 32 | + alt="Uploading thumbnail" |
| 33 | + className="object-cover w-full h-full opacity-30 transition-opacity duration-200" |
62 | 34 | /> |
63 | | - </svg> |
| 35 | + ) : ( |
| 36 | + <div className="flex justify-center items-center w-full h-full"> |
| 37 | + <LogoSpinner className="w-8 h-8 animate-spin" /> |
| 38 | + </div> |
| 39 | + )} |
| 40 | + </div> |
| 41 | + |
| 42 | + <div className="flex absolute inset-0 z-50 justify-center items-center bg-black rounded-t-xl"> |
| 43 | + {progressStatus && uploadingCapId ? ( |
| 44 | + <UploadCircleWithProgress progress={progressStatus} /> |
| 45 | + ) : status === "Processing" ? ( |
| 46 | + <div className="relative size-20 md:size-16"> |
| 47 | + <ProgressCircle |
| 48 | + progressTextClassName="md:!text-[11px]" |
| 49 | + subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1" |
| 50 | + className="md:scale-[1.5] scale-[1.2]" |
| 51 | + progress={0} |
| 52 | + subText="Processing..." |
| 53 | + /> |
| 54 | + </div> |
| 55 | + ) : status === "Uploading" ? ( |
| 56 | + <div className="relative size-20 md:size-16"> |
| 57 | + <ProgressCircle |
| 58 | + progressTextClassName="md:!text-[11px]" |
| 59 | + subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1" |
| 60 | + className="md:scale-[1.5] scale-[1.2]" |
| 61 | + progress={0} |
| 62 | + /> |
| 63 | + </div> |
| 64 | + ) : ( |
| 65 | + <div className="relative size-20 md:size-16"> |
| 66 | + <ProgressCircle |
| 67 | + progressTextClassName="md:!text-[11px]" |
| 68 | + subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1" |
| 69 | + className="md:scale-[1.5] scale-[1.2]" |
| 70 | + progress={0} |
| 71 | + /> |
| 72 | + </div> |
| 73 | + )} |
64 | 74 | </div> |
65 | 75 | </div> |
| 76 | + |
66 | 77 | <div className="flex flex-col flex-grow gap-3 px-4 pb-4 w-full"> |
67 | 78 | <div> |
68 | 79 | <div className="h-[1.25rem] mb-1"> |
69 | | - <div className="h-4 rounded animate-pulse bg-gray-3"></div> |
| 80 | + <div className="h-4 rounded animate-pulse bg-gray-3" /> |
70 | 81 | </div> |
71 | 82 | <div className="mb-1 h-[1.25rem]"> |
72 | | - <div className="w-24 h-3 rounded animate-pulse bg-gray-3"></div> |
| 83 | + <div className="w-24 h-3 rounded animate-pulse bg-gray-3" /> |
73 | 84 | </div> |
74 | 85 | <div className="mb-1 h-[1.5rem]"> |
75 | | - <div className="w-20 h-3 rounded animate-pulse bg-gray-3"></div> |
| 86 | + <div className="w-20 h-3 rounded animate-pulse bg-gray-3" /> |
76 | 87 | </div> |
77 | 88 | </div> |
78 | 89 | <div className="flex gap-4 items-center text-sm text-gray-10"> |
79 | | - <div className="w-16 h-3 rounded animate-pulse bg-gray-3"></div> |
| 90 | + <div className="w-16 h-3 rounded animate-pulse bg-gray-3" /> |
80 | 91 | </div> |
81 | 92 | </div> |
82 | 93 | </div> |
83 | 94 | ); |
84 | 95 | }; |
| 96 | + |
| 97 | +function UploadCircleWithProgress(props: { |
| 98 | + progress: ReturnType<typeof useUploadProgress>; |
| 99 | +}) { |
| 100 | + const { progress } = props; |
| 101 | + |
| 102 | + if (!progress) { |
| 103 | + return ( |
| 104 | + <div className="relative size-20 md:size-16"> |
| 105 | + <ProgressCircle |
| 106 | + progressTextClassName="md:!text-[11px]" |
| 107 | + subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1" |
| 108 | + className="md:scale-[1.5] scale-[1.2]" |
| 109 | + progress={0} |
| 110 | + /> |
| 111 | + </div> |
| 112 | + ); |
| 113 | + } |
| 114 | + |
| 115 | + if (progress.status === "failed") { |
| 116 | + return ( |
| 117 | + <div className="flex flex-col items-center"> |
| 118 | + <div className="flex justify-center items-center mb-2 w-8 h-8 bg-red-500 rounded-full"> |
| 119 | + <FontAwesomeIcon icon={faVideo} className="text-white size-3" /> |
| 120 | + </div> |
| 121 | + <p className="text-xs text-center text-white">Upload failed</p> |
| 122 | + </div> |
| 123 | + ); |
| 124 | + } |
| 125 | + |
| 126 | + return ( |
| 127 | + <div className="relative size-20 md:size-16"> |
| 128 | + <ProgressCircle |
| 129 | + progressTextClassName="md:!text-[11px]" |
| 130 | + subTextClassName="!mt-0 md:!text-[7px] !text-[10px] mb-1" |
| 131 | + className="md:scale-[1.5] scale-[1.2]" |
| 132 | + progress={progress.status === "uploading" ? progress.progress : 0} |
| 133 | + /> |
| 134 | + </div> |
| 135 | + ); |
| 136 | +} |
0 commit comments