From 8888210dc2bee9ffe8c43daf2498d7945ccf7280 Mon Sep 17 00:00:00 2001 From: Sandipan Dey Date: Sun, 22 Sep 2024 08:07:27 +0000 Subject: [PATCH] feat: slider and download --- frontend/app/details/[process-id]/page.tsx | 103 +++++++++++++++++++-- frontend/app/globals.css | 57 ++++++++++++ frontend/components/submit-process.tsx | 1 + frontend/lib/graphql/generated/index.ts | 3 +- 4 files changed, 156 insertions(+), 8 deletions(-) diff --git a/frontend/app/details/[process-id]/page.tsx b/frontend/app/details/[process-id]/page.tsx index 2a98c97..43b6718 100644 --- a/frontend/app/details/[process-id]/page.tsx +++ b/frontend/app/details/[process-id]/page.tsx @@ -12,12 +12,16 @@ type Props = { const ProcessDetail: React.FC = ({ params }) => { const processId = params["process-id"] - const { data } = useGetProcessQuery({ + const { data, loading: processLoading } = useGetProcessQuery({ variables: { id: processId, }, pollInterval: 1000, }) + const [preloadingAudio, setPreloadingAudio] = useState(true) + const loading = processLoading && preloadingAudio + + const [, setRenderCount] = useState(0) const [currentSemitone, setCurrentSemitone] = useState(0) const [isPlaying, setIsPlaying] = useState(false) @@ -45,8 +49,10 @@ const ProcessDetail: React.FC = ({ params }) => { audioRefs.current[semitone] = audio } }) + + setPreloadingAudio(true) } - }, [data, processId]) + }, [data, processId, setPreloadingAudio]) // Update currentTime when the audio is playing useEffect(() => { @@ -95,6 +101,50 @@ const ProcessDetail: React.FC = ({ params }) => { } } + useEffect(() => { + const audioRefsCurrent = audioRefs.current + return () => { + Object.values(audioRefsCurrent).forEach((audio) => { + audio.pause() + }) + setIsPlaying(false) + } + }, []) + + const handleSeek = (e: React.ChangeEvent) => { + const audio = audioRefs.current[currentSemitone] + if (audio) { + const time = parseFloat(e.target.value) + audio.currentTime = time + setCurrentTime(time) + } + } + + const handleDownload = () => { + const semitoneLabel = + currentSemitone >= 0 ? `+${currentSemitone}` : `${currentSemitone}` + const fileName = + currentSemitone === 0 ? `${processId}.mp3` : `${processId}_${semitoneLabel}_ST.mp3` + const fileUrl = `/media/${fileName}` + + // Create a temporary anchor element to initiate download + const link = document.createElement("a") + link.href = fileUrl + link.download = `${data?.getProcess.name}_${semitoneLabel}.mp3` + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + } + + useEffect(() => { + const i = setInterval(() => { + setRenderCount((prev) => (prev + 1) % 100) + }, 1000) + return () => clearInterval(i) + }, []) + + if (loading) return
Loading...
+ if (data?.getProcess.status !== "DONE") return ( <> @@ -106,25 +156,34 @@ const ProcessDetail: React.FC = ({ params }) => { ) + const playingAudio = audioRefs.current[currentSemitone] + const playTime = (playingAudio && playingAudio.currentTime) || 0 + const duration = (playingAudio && playingAudio.duration) || 0 + return (
-
{data?.getProcess.name}
+
{data?.getProcess.name}
+
{data?.getProcess.youtubeUrl}
+
- Current Semitone: {currentSemitone} + + Current Semitone:{" "} + {currentSemitone > 0 ? `+${currentSemitone}` : currentSemitone} +
@@ -132,14 +191,44 @@ const ProcessDetail: React.FC = ({ params }) => {
+
+ +
+ {formatTime(playTime)} + {formatTime(duration)} +
+
+
+ +
) } export default ProcessDetail + +// Format time in mm:ss +const formatTime = (time: number) => { + if (isNaN(time)) return "0:00" + const minutes = Math.floor(time / 60) + const seconds = Math.floor(time % 60) + return `${minutes}:${seconds < 10 ? "0" : ""}${seconds}` +} diff --git a/frontend/app/globals.css b/frontend/app/globals.css index 70ba0c1..8f72749 100644 --- a/frontend/app/globals.css +++ b/frontend/app/globals.css @@ -27,3 +27,60 @@ body { background-color: var(--color-dark); } + +@layer components { + .range-input { + @apply w-full; /* Use Tailwind's width utility */ + -webkit-appearance: none; /* Remove default styling */ + appearance: none; /* Remove default styling */ + background-color: transparent; /* Make the background transparent */ + } + + /* Track Styles */ + .range-input::-webkit-slider-runnable-track { + @apply h-1 bg-zinc-500 rounded-full; /* Tailwind utilities for height, background color, and border radius */ + } + + .range-input::-moz-range-track { + @apply h-1 bg-zinc-500 rounded-full; + } + + .range-input::-ms-track { + @apply h-1 bg-zinc-500 rounded-full; + } + + /* Thumb Styles */ + .range-input::-webkit-slider-thumb { + -webkit-appearance: none; + @apply h-4 w-4 bg-white rounded-full; + margin-top: -5px; + } + + .range-input::-moz-range-thumb { + @apply h-4 w-4 bg-white rounded-full; + margin-top: -5px; + } + + .range-input::-ms-thumb { + @apply h-4 w-4 bg-white rounded-full; + margin-top: -5px; + } + + /* Focus Styles */ + .range-input:focus { + outline: none; + } + + /* Hover Effects */ + .range-input:hover::-webkit-slider-runnable-track { + @apply bg-blue-600; /* Darken track on hover */ + } + + .range-input:hover::-moz-range-track { + @apply bg-blue-600; + } + + .range-input:hover::-ms-track { + @apply bg-blue-600; + } +} diff --git a/frontend/components/submit-process.tsx b/frontend/components/submit-process.tsx index 0532d6b..1e76e47 100644 --- a/frontend/components/submit-process.tsx +++ b/frontend/components/submit-process.tsx @@ -18,6 +18,7 @@ gql` getProcess(id: $id) { id name + youtubeUrl status } } diff --git a/frontend/lib/graphql/generated/index.ts b/frontend/lib/graphql/generated/index.ts index d6a799e..a883917 100644 --- a/frontend/lib/graphql/generated/index.ts +++ b/frontend/lib/graphql/generated/index.ts @@ -77,7 +77,7 @@ export type GetProcessQueryVariables = Exact<{ }>; -export type GetProcessQuery = { __typename?: 'Query', getProcess: { __typename?: 'Process', id: any, name: string, status: ProcessStatus } }; +export type GetProcessQuery = { __typename?: 'Query', getProcess: { __typename?: 'Process', id: any, name: string, youtubeUrl: string, status: ProcessStatus } }; export const AllProcessesDocument = gql` @@ -157,6 +157,7 @@ export const GetProcessDocument = gql` getProcess(id: $id) { id name + youtubeUrl status } }