Various changes
* #30 key update on map function * #26 Formatted dates on the podcast cards * #33 Listen now button is a styled link button * Moved Add button to buttons component * #34 Now sort the episodes by default in reverse order by timestamp.
This commit is contained in:
parent
f99c04f005
commit
857455fa6d
@ -3,7 +3,8 @@
|
|||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { PodcastDto } from '@/common/dtos/podcastDto';
|
import { PodcastDto } from '@/common/dtos/podcastDto';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import DownloadButton from '@/app/common/components/buttons/DownloadButton';
|
import DownloadIconButton from '@/app/common/components/buttons/downloadIconButton';
|
||||||
|
import LinkIconButton from '@/app/common/components/buttons/linkIconButton';
|
||||||
|
|
||||||
export default function PodcastCard({
|
export default function PodcastCard({
|
||||||
imageUrl,
|
imageUrl,
|
||||||
@ -39,20 +40,34 @@ export default function PodcastCard({
|
|||||||
</button>
|
</button>
|
||||||
<div className="mt-2 text-sm text-gray-500">
|
<div className="mt-2 text-sm text-gray-500">
|
||||||
<p>By: {author}</p>
|
<p>By: {author}</p>
|
||||||
<p>Uploaded: {uploadDate}</p>
|
<p>Uploaded: {uploadDate ?
|
||||||
|
new Date(Number(uploadDate)).toLocaleDateString("en-US", {
|
||||||
|
year: "numeric",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric"
|
||||||
|
}) : "Unknown"}</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
|
{url &&
|
||||||
|
<LinkIconButton
|
||||||
|
href={url}
|
||||||
|
iconSrc={`${process.env.NEXT_PUBLIC_API_BASE_URL}/icons/play.svg`}
|
||||||
|
iconAlt="Listen Now"
|
||||||
|
text="Listen Now"
|
||||||
|
className="mt-4"
|
||||||
|
/>
|
||||||
|
}
|
||||||
<a href={url} target="_blank" rel="noopener noreferrer" className="mt-4 text-blue-500 hover:underline text-sm">
|
<a href={url} target="_blank" rel="noopener noreferrer" className="mt-4 text-blue-500 hover:underline text-sm">
|
||||||
Listen Now
|
|
||||||
</a>
|
</a>
|
||||||
{url &&
|
{url &&
|
||||||
<DownloadButton
|
<DownloadIconButton
|
||||||
href={url}
|
href={url}
|
||||||
iconSrc={`${process.env.NEXT_PUBLIC_API_BASE_URL}/icons/download.svg`}
|
iconSrc={`${process.env.NEXT_PUBLIC_API_BASE_URL}/icons/download.svg`}
|
||||||
iconAlt="Download"
|
iconAlt="Download"
|
||||||
text="Download"
|
text="Download"
|
||||||
download={`${title}-${url.split('/').pop()}`}
|
download={`${title}-${url.split('/').pop()}`}
|
||||||
className="mt-4 ml-2"
|
className="mt-4"
|
||||||
debounceMs={2000}
|
debounceMs={2000}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
import PodcastCard from './podcastCard';
|
import PodcastCard from './podcastCard';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { PodcastDto } from '../../../common/dtos/podcastDto';
|
import { PodcastDto } from '@/common/dtos/podcastDto';
|
||||||
import AddPodcastCard from './addPodcastCard';
|
import AddButton from '@/app/common/components/buttons/addButton';
|
||||||
|
|
||||||
type PodcastListProps = {
|
type PodcastListProps = {
|
||||||
stream?: string | string[];
|
stream?: string | string[];
|
||||||
@ -20,7 +20,7 @@ export default function PodcastList({ stream }: PodcastListProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-[800px] mx-auto w-full px-4">
|
<div className="max-w-[800px] mx-auto w-full px-4">
|
||||||
<AddPodcastCard stream={stream}/>
|
<AddButton stream={stream} />
|
||||||
{
|
{
|
||||||
podcastData.map((podcast, index) => (
|
podcastData.map((podcast, index) => (
|
||||||
<div key={index} className="flex justify-center">
|
<div key={index} className="flex justify-center">
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import { useRouter } from 'next/navigation';
|
import { useRouter } from 'next/navigation';
|
||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
|
|
||||||
export default function AddPodcastCard({ stream }: { stream?: string | string[] }) {
|
export default function AddButton({ stream }: { stream?: string | string[] }) {
|
||||||
const router = useRouter(),
|
const router = useRouter(),
|
||||||
defaultBottomOffset = 16,
|
defaultBottomOffset = 16,
|
||||||
[bottomOffset, setBottomOffset] = useState(defaultBottomOffset);
|
[bottomOffset, setBottomOffset] = useState(defaultBottomOffset);
|
||||||
@ -1,8 +1,8 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import IconExpandTextButton from "./IconExpandTextButton";
|
import IconExpandTextButton from "./iconExpandTextButton";
|
||||||
|
|
||||||
type DownloadButtonProps = {
|
type DownloadIconButtonProps = {
|
||||||
href: string;
|
href: string;
|
||||||
iconSrc: string;
|
iconSrc: string;
|
||||||
iconAlt?: string;
|
iconAlt?: string;
|
||||||
@ -13,7 +13,7 @@ type DownloadButtonProps = {
|
|||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function DownloadButton({
|
export default function DownloadIconButton({
|
||||||
href,
|
href,
|
||||||
iconSrc,
|
iconSrc,
|
||||||
iconAlt = "icon",
|
iconAlt = "icon",
|
||||||
@ -22,7 +22,7 @@ export default function DownloadButton({
|
|||||||
className = "",
|
className = "",
|
||||||
debounceMs,
|
debounceMs,
|
||||||
disabled = false
|
disabled = false
|
||||||
}: DownloadButtonProps) {
|
}: DownloadIconButtonProps) {
|
||||||
const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
const onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (disabled) return;
|
if (disabled) return;
|
||||||
42
src/app/common/components/buttons/linkIconButton.tsx
Normal file
42
src/app/common/components/buttons/linkIconButton.tsx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import IconExpandTextButton from "./iconExpandTextButton";
|
||||||
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
|
type LinkIconButtonProps = {
|
||||||
|
href: string;
|
||||||
|
iconSrc: string;
|
||||||
|
iconAlt?: string;
|
||||||
|
text: string;
|
||||||
|
className?: string;
|
||||||
|
debounceMs?: number;
|
||||||
|
disabled?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function LinkIconButton({
|
||||||
|
href,
|
||||||
|
iconSrc,
|
||||||
|
iconAlt = "icon",
|
||||||
|
text,
|
||||||
|
className = "",
|
||||||
|
debounceMs,
|
||||||
|
disabled = false
|
||||||
|
}: LinkIconButtonProps) {
|
||||||
|
const router = useRouter(),
|
||||||
|
onClick = (e: React.MouseEvent<HTMLAnchorElement>) => {
|
||||||
|
e.preventDefault();
|
||||||
|
if (disabled || href == null) return;
|
||||||
|
router.push(href);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<IconExpandTextButton
|
||||||
|
iconSrc={iconSrc}
|
||||||
|
iconAlt={iconAlt}
|
||||||
|
text={text}
|
||||||
|
className={className}
|
||||||
|
debounceMs={debounceMs}
|
||||||
|
disabled={disabled}
|
||||||
|
onClick={onClick}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -79,7 +79,7 @@ export default function Home() {
|
|||||||
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-6">
|
<div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-6">
|
||||||
{podcasts.map(podcast => (
|
{podcasts.map(podcast => (
|
||||||
<button
|
<button
|
||||||
key={podcast.streamId}
|
key={podcast.podcastId}
|
||||||
type='button'
|
type='button'
|
||||||
onClick={() => handlePodcastClick(podcast)}
|
onClick={() => handlePodcastClick(podcast)}
|
||||||
className="aspect-square bg-purple-100 rounded-lg flex items-center justify-center overflow-hidden shadow focus:outline-none focus:ring-2 focus:ring-purple-400 transition"
|
className="aspect-square bg-purple-100 rounded-lg flex items-center justify-center overflow-hidden shadow focus:outline-none focus:ring-2 focus:ring-purple-400 transition"
|
||||||
|
|||||||
@ -9,7 +9,9 @@ import { preparePodcastItem } from '../../../common/helpers/data';
|
|||||||
|
|
||||||
const get = async (req: NextApiRequest, res: NextApiResponse<PodcastDto[]>) => {
|
const get = async (req: NextApiRequest, res: NextApiResponse<PodcastDto[]>) => {
|
||||||
const stream = req.query.stream as string,
|
const stream = req.query.stream as string,
|
||||||
podcasts = (await getPodcasts(stream as string)).map(preparePodcastItem);
|
podcasts = (await getPodcasts(stream as string))
|
||||||
|
.map(preparePodcastItem)
|
||||||
|
.sort((a, b) => Number(b.uploadDate) - Number(a.uploadDate));
|
||||||
|
|
||||||
res.status(200).json(podcasts);
|
res.status(200).json(podcasts);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user