mirror of
https://github.com/ershisan99/podcaster.git
synced 2025-12-17 05:09:27 +00:00
feat: add podcasts service
This commit is contained in:
28
src/hooks/useQuery.ts
Normal file
28
src/hooks/useQuery.ts
Normal 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 };
|
||||||
|
}
|
||||||
@@ -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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|||||||
15
src/services/podcasts/podcast.dto.ts
Normal file
15
src/services/podcasts/podcast.dto.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/services/podcasts/podcasts.service.ts
Normal file
17
src/services/podcasts/podcasts.service.ts
Normal 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();
|
||||||
164
src/services/podcasts/podcasts.types.ts
Normal file
164
src/services/podcasts/podcasts.types.ts
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user