diff --git a/public/icons/download.svg b/public/icons/download.svg new file mode 100644 index 0000000..7946531 --- /dev/null +++ b/public/icons/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/[stream]/podcasts/podcastCard.tsx b/src/app/[stream]/podcasts/podcastCard.tsx index a0aeb79..58f695e 100644 --- a/src/app/[stream]/podcasts/podcastCard.tsx +++ b/src/app/[stream]/podcasts/podcastCard.tsx @@ -1,8 +1,9 @@ "use client"; import Image from 'next/image'; -import { PodcastDto } from '../../../common/dtos/podcastDto'; +import { PodcastDto } from '@/common/dtos/podcastDto'; import { useState } from 'react'; +import DownloadButton from '@/app/common/components/buttons/DownloadButton'; export default function PodcastCard({ imageUrl = "https://placehold.co/400", @@ -40,9 +41,22 @@ export default function PodcastCard({

By: {author}

Uploaded: {uploadDate}

- - Listen Now - +
+ + Listen Now + + {url && + + } +
); diff --git a/src/app/[stream]/publish/publishForm.tsx b/src/app/[stream]/publish/publishForm.tsx index 15befd0..bc1d68a 100644 --- a/src/app/[stream]/publish/publishForm.tsx +++ b/src/app/[stream]/publish/publishForm.tsx @@ -54,7 +54,6 @@ export default function StreamPublishForm({ stream }: { stream?: string | string }; xhr.send(formData); - } return ( diff --git a/src/app/common/components/buttons/DownloadButton.tsx b/src/app/common/components/buttons/DownloadButton.tsx new file mode 100644 index 0000000..5bfb31f --- /dev/null +++ b/src/app/common/components/buttons/DownloadButton.tsx @@ -0,0 +1,50 @@ +"use client"; + +import IconExpandTextButton from "./IconExpandTextButton"; + +type DownloadButtonProps = { + href: string; + iconSrc: string; + iconAlt?: string; + text: string; + download?: boolean | string; + className?: string; + debounceMs?: number; + disabled?: boolean; +}; + +export default function DownloadButton({ + href, + iconSrc, + iconAlt = "icon", + text, + download = false, + className = "", + debounceMs, + disabled = false +}: DownloadButtonProps) { + const onClick = (e: React.MouseEvent) => { + e.preventDefault(); + if (disabled) return; + + const link = document.createElement("a"); + link.href = href; + if (download) { + link.setAttribute('download', typeof download === 'string' ? download : ''); + } + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + }; + return ( + + ); +} \ No newline at end of file diff --git a/src/app/common/components/buttons/IconExpandTextButton.tsx b/src/app/common/components/buttons/IconExpandTextButton.tsx new file mode 100644 index 0000000..9b6f483 --- /dev/null +++ b/src/app/common/components/buttons/IconExpandTextButton.tsx @@ -0,0 +1,53 @@ +"use client"; + +import { useState } from "react"; +import Image from "next/image"; + +type IconExpandTextButtonProps = { + iconSrc: string; + iconAlt?: string; + text: string; + download?: boolean | string; + className?: string; + debounceMs?: number; + onClick?: (e: React.MouseEvent) => void; + disabled?: boolean; +}; + +export default function IconExpandTextButton({ + iconSrc, + iconAlt = "icon", + text, + download = false, + className = "", + debounceMs, + onClick, + disabled = false +}: IconExpandTextButtonProps) { + const [debounced, setDebounced] = useState(false), + isDisabled = disabled || debounced, + handleClick = (e: React.MouseEvent) => { + e.preventDefault(); + if (disabled || debounced) return; + if (debounceMs != null) { + setDebounced(true); + setTimeout(() => setDebounced(false), debounceMs); + } + onClick?.(e); + }; + + return ( + + {iconAlt} + + {text} + + + ); +} \ No newline at end of file