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 { podcastsService } from "../services/podcasts/podcasts.service";
|
||||
import { useQuery } from "../hooks/useQuery";
|
||||
|
||||
export function Home() {
|
||||
const { data } = useQuery(() => podcastsService.getTopPodcasts());
|
||||
|
||||
return (
|
||||
<main>
|
||||
<div className={"grid grid-cols-4"}>
|
||||
{podcasts.map((podcast) => (
|
||||
{data?.map((podcast) => (
|
||||
<PodcastPreviewCard
|
||||
key={podcast.id}
|
||||
title={podcast.title}
|
||||
author={podcast.author}
|
||||
imageUrl={podcast.imageSrc}
|
||||
imageUrl={podcast.imageUrl}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</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