({
jobStatus,
jobId,
error,
videoRef,
}: GourceVideoProps)
| 15 | } |
| 16 | |
| 17 | export default function GourceVideo({ |
| 18 | jobStatus, |
| 19 | jobId, |
| 20 | error, |
| 21 | videoRef, |
| 22 | }: GourceVideoProps) { |
| 23 | const videoUrl = jobId ? `/api/gource/video/${jobId}` : null; |
| 24 | |
| 25 | const handleDownload = () => { |
| 26 | if (videoUrl) { |
| 27 | window.open(videoUrl, "_blank"); |
| 28 | } |
| 29 | }; |
| 30 | |
| 31 | return ( |
| 32 | <div className="w-full"> |
| 33 | {jobStatus && ( |
| 34 | <div> |
| 35 | {jobStatus.video_url === null && !jobStatus.error && ( |
| 36 | <div className="w-full max-w-xl mx-auto"> |
| 37 | <GourceProgress currentStep={jobStatus.step} /> |
| 38 | </div> |
| 39 | )} |
| 40 | {jobStatus.error && ( |
| 41 | <div className="flex justify-center mt-2"> |
| 42 | <p className="text-red-500 text-center">{jobStatus.error}</p> |
| 43 | </div> |
| 44 | )} |
| 45 | {jobStatus.step === ProgressStep.GeneratingVisualization && |
| 46 | jobStatus.video_url && ( |
| 47 | <div ref={videoRef} className="py-6 max-w-7xl p-8 mx-auto"> |
| 48 | <div className="rounded-xl border-[1.5px] border-white/10 bg-black overflow-hidden relative aspect-[16/10]"> |
| 49 | <button |
| 50 | onClick={handleDownload} |
| 51 | className="absolute top-3 right-3 |
| 52 | bg-gradient-to-b from-gray-400/20 to-gray-700/20 |
| 53 | hover:from-gray-400/30 hover:to-gray-700/30 |
| 54 | rounded-lg p-2 |
| 55 | transition-all duration-200 ease-in-out |
| 56 | backdrop-blur-sm" |
| 57 | aria-label="Download video" |
| 58 | > |
| 59 | <ArrowDownTrayIcon className="h-6 w-6 text-white/70" /> |
| 60 | </button> |
| 61 | |
| 62 | <video |
| 63 | className="w-full h-full object-cover" |
| 64 | controls |
| 65 | src={videoUrl || undefined} |
| 66 | /> |
| 67 | </div> |
| 68 | </div> |
| 69 | )} |
| 70 | </div> |
| 71 | )} |
| 72 | |
| 73 | {error && ( |
| 74 | <div className="flex justify-center mt-2"> |
nothing calls this directly
no outgoing calls
no test coverage detected