From b22faedd291f4b05f4f8f044a6c6e49c8e380dee Mon Sep 17 00:00:00 2001 From: Mandeep Singh Date: Wed, 13 Nov 2024 03:35:42 +0530 Subject: [PATCH] Replace the basic progress bar with a functional slider and revamp the podcast player --- components/LeftSidebar.tsx | 2 +- components/PodcastDetailPlayer.tsx | 12 +- components/PodcastPlayer.tsx | 169 +++++++++++++---- components/ProfileCard.tsx | 1 + components/RightSidebar.tsx | 13 +- components/ui/progress.tsx | 28 --- components/ui/slider.tsx | 28 +++ hooks/useIncrementPodcastViews.ts | 56 ++++++ package-lock.json | 279 ++++++++++++++++++++++++++--- package.json | 2 +- public/icons/close-circle.svg | 4 + types/index.ts | 1 + 12 files changed, 486 insertions(+), 109 deletions(-) delete mode 100644 components/ui/progress.tsx create mode 100644 components/ui/slider.tsx create mode 100644 hooks/useIncrementPodcastViews.ts create mode 100644 public/icons/close-circle.svg diff --git a/components/LeftSidebar.tsx b/components/LeftSidebar.tsx index c771a9a..d0c236a 100644 --- a/components/LeftSidebar.tsx +++ b/components/LeftSidebar.tsx @@ -18,7 +18,7 @@ const LeftSidebar = () => { return (
); diff --git a/components/ProfileCard.tsx b/components/ProfileCard.tsx index 2cc3628..6dfb880 100644 --- a/components/ProfileCard.tsx +++ b/components/ProfileCard.tsx @@ -42,6 +42,7 @@ const ProfileCard = ({ audioUrl: randomPodcast.audioUrl || "", imageUrl: randomPodcast.imageUrl || "", author: randomPodcast.author, + authorId: randomPodcast.authorId, podcastId: randomPodcast._id, }); } diff --git a/components/RightSidebar.tsx b/components/RightSidebar.tsx index 7dd6a25..83c767f 100644 --- a/components/RightSidebar.tsx +++ b/components/RightSidebar.tsx @@ -9,7 +9,6 @@ import Carousel from './Carousel'; import { useQuery } from 'convex/react'; import { api } from '@/convex/_generated/api'; import { useRouter } from 'next/navigation'; -import LoaderSpinner from './LoaderSpinner'; import { useAudio } from '@/providers/AudioProvider'; import { cn } from '@/lib/utils'; @@ -22,8 +21,8 @@ const RightSidebar = () => { return (
+ 'h-[calc(100vh-113px)]': audio?.audioUrl + })}> @@ -45,8 +44,12 @@ const RightSidebar = () => {
- {topPodcasters?.slice(0, 3).map((podcaster) => ( -
router.push(`/profile/${podcaster.clerkId}`)}> + {topPodcasters?.slice(0, audio?.audioUrl ? 2 : 3).map((podcaster) => ( +
router.push(`/profile/${podcaster.clerkId}`)} + >
, - React.ComponentPropsWithoutRef ->(({ className, value, ...props }, ref) => ( - - - -)) -Progress.displayName = ProgressPrimitive.Root.displayName - -export { Progress } diff --git a/components/ui/slider.tsx b/components/ui/slider.tsx new file mode 100644 index 0000000..7607c57 --- /dev/null +++ b/components/ui/slider.tsx @@ -0,0 +1,28 @@ +"use client"; + +import * as React from "react"; +import * as SliderPrimitive from "@radix-ui/react-slider"; + +import { cn } from "@/lib/utils"; + +const Slider = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + + + + +)); +Slider.displayName = SliderPrimitive.Root.displayName; + +export { Slider }; diff --git a/hooks/useIncrementPodcastViews.ts b/hooks/useIncrementPodcastViews.ts new file mode 100644 index 0000000..d31a459 --- /dev/null +++ b/hooks/useIncrementPodcastViews.ts @@ -0,0 +1,56 @@ +import { useState, useEffect } from "react"; + +type PodcastViewHookProps = { + targetTime: number; + isPlaying: boolean; + duration: number; +}; + +const useIncrementPodcastViews = ({ + targetTime, + isPlaying, + duration, +}: PodcastViewHookProps): { + isViewValid: boolean; + setResetTimer: () => void; +} => { + const [timePlayed, setTimePlayed] = useState(0); // Track total time the audio has been played + + useEffect(() => { + if (!isPlaying || timePlayed >= targetTime) { + return; + } + + // Function to update timePlayed based on actual audio playback time (using an internal timer with setInterval) + const updateTime = () => { + setTimePlayed((prevTime) => { + const newTime = prevTime + 0.1; + // Case 1: Stop the timer once the audio has played for the targetTime + if (newTime >= targetTime) { + return targetTime; // Cap the time at targetTime (e.g., 10 seconds) + } + // Case 2: Stop the timer once the audio has ended (*2 to account for some inaccuracy) + if (newTime * 2 >= duration) { + return targetTime; + } + return newTime; + }); + }; + + // Start updating time when audio is playing + const intervalId = setInterval(() => { + updateTime(); + }, 100); // Update every 100ms NOTE: (Lower values may cause performance issues or inaccuracy) + + return () => { + clearInterval(intervalId); // Clear the interval + }; + }, [duration, isPlaying, targetTime, timePlayed]); + + if (timePlayed >= targetTime) { + return { isViewValid: true, setResetTimer: () => setTimePlayed(0) }; + } + return { isViewValid: false, setResetTimer: () => setTimePlayed(0) }; +}; + +export default useIncrementPodcastViews; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index fd85667..500e390 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,8 +12,8 @@ "@hookform/resolvers": "^3.4.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@xixixao/uploadstuff": "^0.0.5", @@ -1281,30 +1281,6 @@ } } }, - "node_modules/@radix-ui/react-progress": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.0.3.tgz", - "integrity": "sha512-5G6Om/tYSxjSeEdrb1VfKkfZfn/1IlPWd731h2RfPuSbIfNUgfqAwbKfJCg/PP6nuUCTrYzalwHSpSinoWoCag==", - "dependencies": { - "@babel/runtime": "^7.13.10", - "@radix-ui/react-context": "1.0.1", - "@radix-ui/react-primitive": "1.0.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0", - "react-dom": "^16.8 || ^17.0 || ^18.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-select": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.0.0.tgz", @@ -1348,6 +1324,259 @@ } } }, + "node_modules/@radix-ui/react-slider": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.2.1.tgz", + "integrity": "sha512-bEzQoDW0XP+h/oGbutF5VMWJPAl/UU8IJjr7h02SOHDIIIxq+cep8nItVNoBV+OMmahCdqdF38FTpmXoqQUGvw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/number": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", + "integrity": "sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-context": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", + "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", + "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-primitive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", + "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", + "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz", diff --git a/package.json b/package.json index a10603a..46ca108 100644 --- a/package.json +++ b/package.json @@ -15,8 +15,8 @@ "@hookform/resolvers": "^3.4.2", "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-label": "^2.0.2", - "@radix-ui/react-progress": "^1.0.3", "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-slider": "^1.2.1", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-toast": "^1.1.5", "@xixixao/uploadstuff": "^0.0.5", diff --git a/public/icons/close-circle.svg b/public/icons/close-circle.svg new file mode 100644 index 0000000..f618426 --- /dev/null +++ b/public/icons/close-circle.svg @@ -0,0 +1,4 @@ + + + + diff --git a/types/index.ts b/types/index.ts index 8a2d6f9..6048786 100644 --- a/types/index.ts +++ b/types/index.ts @@ -96,6 +96,7 @@ export interface AudioProps { title: string; audioUrl: string; author: string; + authorId: string; imageUrl: string; podcastId: string; }