feat: add podcasts service

This commit is contained in:
2024-04-18 16:48:22 +02:00
parent 1396249544
commit de9eafdaa8
5 changed files with 231 additions and 15 deletions

28
src/hooks/useQuery.ts Normal file
View File

@@ -0,0 +1,28 @@
import { useCallback, useEffect, useState } from "react";
export function useQuery<T, E = Error>(
callback: () => Promise<T>,
/* eslint-disable-next-line @typescript-eslint/no-explicit-any*/
dependencies: any[] = [],
) {
const [loading, setLoading] = useState(true);
const [error, setError] = useState<E | undefined>();
const [data, setData] = useState<T | undefined>();
const callbackMemoized = useCallback(() => {
setLoading(true);
setError(undefined);
setData(undefined);
callback()
.then(setData)
.catch(setError)
.finally(() => setLoading(false));
/* eslint-disable-next-line react-hooks/exhaustive-deps*/
}, dependencies);
useEffect(() => {
callbackMemoized();
}, [callbackMemoized]);
return { loading, error, data };
}

View File

@@ -1,30 +1,22 @@
import { PodcastPreviewCard } from "../components/podcast-preview-card"; import { PodcastPreviewCard } from "../components/podcast-preview-card";
import { podcastsService } from "../services/podcasts/podcasts.service";
import { useQuery } from "../hooks/useQuery";
export function Home() { export function Home() {
const { data } = useQuery(() => podcastsService.getTopPodcasts());
return ( return (
<main> <main>
<div className={"grid grid-cols-4"}> <div className={"grid grid-cols-4"}>
{podcasts.map((podcast) => ( {data?.map((podcast) => (
<PodcastPreviewCard <PodcastPreviewCard
key={podcast.id}
title={podcast.title} title={podcast.title}
author={podcast.author} author={podcast.author}
imageUrl={podcast.imageSrc} imageUrl={podcast.imageUrl}
/> />
))} ))}
</div> </div>
</main> </main>
); );
} }
const podcasts = [
{
imageSrc: "https://picsum.photos/200/200",
title: "Podcast 1",
author: "Author 1",
},
{
imageSrc: "https://picsum.photos/200/200",
title: "Podcast 2",
author: "Author 2",
},
];

View File

@@ -0,0 +1,15 @@
import { Entry } from "./podcasts.types";
export class PodcastDTO {
id: string;
title: string;
author: string;
imageUrl: string;
constructor(podcast: Entry) {
this.id = podcast.id.attributes["im:id"];
this.title = podcast["im:name"].label;
this.author = podcast["im:artist"].label;
this.imageUrl = podcast["im:image"][0].label;
}
}

View File

@@ -0,0 +1,17 @@
import { TopPodcastsResponse } from "./podcasts.types";
import { PodcastDTO } from "./podcast.dto";
class PodcastsService {
baseUrl = "https://itunes.apple.com";
async getTopPodcasts(): Promise<PodcastDTO[]> {
const response = await fetch(
`${this.baseUrl}/us/rss/toppodcasts/limit=100/genre=1310/json`,
);
const data: TopPodcastsResponse = await response.json();
return data.feed.entry.map((podcast) => new PodcastDTO(podcast));
}
}
export const podcastsService = new PodcastsService();

View File

@@ -0,0 +1,164 @@
export interface TopPodcastsResponse {
feed: Feed;
}
export interface Feed {
author: Author;
entry: Entry[];
updated: Updated;
rights: Rights2;
title: Title2;
icon: Icon;
link: Link2[];
id: Id2;
}
export interface Author {
name: Name;
uri: Uri;
}
export interface Name {
label: string;
}
export interface Uri {
label: string;
}
export interface Entry {
"im:name": ImName;
"im:image": ImImage[];
summary: Summary;
"im:price": ImPrice;
"im:contentType": ImContentType;
rights?: Rights;
title: Title;
link: Link;
id: Id;
"im:artist": ImArtist;
category: Category;
"im:releaseDate": ImReleaseDate;
}
export interface ImName {
label: string;
}
export interface ImImage {
label: string;
attributes: Attributes;
}
export interface Attributes {
height: string;
}
export interface Summary {
label: string;
}
export interface ImPrice {
label: string;
attributes: Attributes2;
}
export interface Attributes2 {
amount: string;
currency: string;
}
export interface ImContentType {
attributes: Attributes3;
}
export interface Attributes3 {
term: string;
label: string;
}
export interface Rights {
label: string;
}
export interface Title {
label: string;
}
export interface Link {
attributes: Attributes4;
}
export interface Attributes4 {
rel: string;
type: string;
href: string;
}
export interface Id {
label: string;
attributes: Attributes5;
}
export interface Attributes5 {
"im:id": string;
}
export interface ImArtist {
label: string;
attributes?: Attributes6;
}
export interface Attributes6 {
href: string;
}
export interface Category {
attributes: Attributes7;
}
export interface Attributes7 {
"im:id": string;
term: string;
scheme: string;
label: string;
}
export interface ImReleaseDate {
label: string;
attributes: Attributes8;
}
export interface Attributes8 {
label: string;
}
export interface Updated {
label: string;
}
export interface Rights2 {
label: string;
}
export interface Title2 {
label: string;
}
export interface Icon {
label: string;
}
export interface Link2 {
attributes: Attributes9;
}
export interface Attributes9 {
rel: string;
type?: string;
href: string;
}
export interface Id2 {
label: string;
}