chore: refactor imports/exports and some semantics

This commit is contained in:
2024-04-21 23:54:45 +02:00
parent df359883fc
commit e8ed8d745f
35 changed files with 127 additions and 55 deletions

View File

@@ -1,7 +1,7 @@
import { Router } from "./router";
import { QueryClient } from "@tanstack/react-query";
import { createIDBPersister } from "./services/infrastructure/persisters/create-idb-persister";
import { PersistQueryClientProvider } from "@tanstack/react-query-persist-client";
import { Router } from "./router";
import { createIDBPersister } from "./services/infrastructure";
const queryClient = new QueryClient({
defaultOptions: {

View File

@@ -1,6 +1,6 @@
import { useParams } from "react-router-dom";
import { usePodcastQuery } from "../../services/podcasts/podcast.hooks";
import { useTitle } from "../../hooks/use-title";
import { usePodcastQuery } from "../../services";
import { useTitle } from "../../hooks";
export function Episode() {
const { podcastId, episodeId } = useParams<{

3
src/components/index.ts Normal file
View File

@@ -0,0 +1,3 @@
export * from "./podcast";
export * from "./ui";
export * from "./episode";

View File

@@ -0,0 +1,4 @@
export * from "./podcast-episodes-list";
export * from "./podcast-episodes-table";
export * from "./podcast-info-card";
export * from "./podcast-preview-card";

View File

@@ -1,6 +1,6 @@
import { useParams } from "react-router-dom";
import { usePodcastQuery } from "../../services/podcasts/podcast.hooks";
import { useTitle } from "../../hooks/use-title";
import { usePodcastQuery } from "../../services";
import { useTitle } from "../../hooks";
import { PodcastEpisodesTable } from "./podcast-episodes-table";
export function PodcastEpisodesList() {

View File

@@ -6,13 +6,13 @@ import {
TableHead,
TableHeadCell,
TableRow,
} from "../ui/table/table";
} from "../ui";
import { formatDate, formatDuration } from "../../utils";
import { Link } from "react-router-dom";
import { Episode } from "../../services/rss-parser";
import { RssEpisode } from "../../services/rss-parser";
type Props = {
episodes: Episode[] | undefined;
episodes: RssEpisode[] | undefined;
};
export const PodcastEpisodesTable = memo(({ episodes }: Props) => {

View File

@@ -1,5 +1,5 @@
import { Link, useParams } from "react-router-dom";
import { Wrap } from "../utils/wrap";
import { Wrap } from "../utils";
type Props = {
title: string;

View File

@@ -14,7 +14,7 @@ export function PodcastPreviewCard({
detailUrl,
}: Props) {
return (
<Link to={detailUrl} className={"w-full"}>
<Link to={detailUrl} className={"w-full"} role={"listitem"}>
<div className={"relative -mb-12 mt-12 h-full p-3 pt-12 shadow-md"}>
<img
src={imageUrl}

View File

@@ -0,0 +1,4 @@
export * from "./input";
export * from "./spinner";
export * from "./layout";
export * from "./table";

View File

@@ -0,0 +1 @@
export * from "./input";

View File

@@ -1,16 +1,30 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import { clsx } from "clsx";
import {
ChangeEvent,
ComponentPropsWithoutRef,
ElementRef,
forwardRef,
} from "react";
import { cn } from "../../../utils";
type InputProps = ComponentPropsWithoutRef<"input">;
type InputProps = ComponentPropsWithoutRef<"input"> & {
onValueChange?: (value: string) => void;
};
type InputRef = ElementRef<"input">;
export const Input = forwardRef<InputRef, InputProps>(
({ className, ...props }, ref) => {
const classes = clsx(
"w-1/3 rounded-md border border-gray-300 p-2",
({ className, onChange, onValueChange, ...props }, ref) => {
const classes = cn(
"w-full rounded-md border border-gray-300 p-2",
className,
);
return <input ref={ref} className={classes} {...props} />;
function handleChange(e: ChangeEvent<HTMLInputElement>) {
onChange?.(e);
onValueChange?.(e.target.value);
}
return (
<input ref={ref} className={classes} onChange={handleChange} {...props} />
);
},
);

View File

@@ -0,0 +1 @@
export * from "./layout";

View File

@@ -1,5 +1,5 @@
import { Link, Outlet } from "react-router-dom";
import { Spinner } from "../spinner/spinner";
import { Spinner } from "../spinner";
import { useIsFetching } from "@tanstack/react-query";
export function Layout() {

View File

@@ -0,0 +1 @@
export * from "./spinner";

View File

@@ -0,0 +1 @@
export * from "./table";

View File

@@ -1,11 +1,11 @@
import { ComponentPropsWithoutRef, ElementRef, forwardRef } from "react";
import { clsx } from "clsx";
import { cn } from "../../../utils";
export const Table = forwardRef<
HTMLTableElement,
ComponentPropsWithoutRef<"table">
>(({ className, ...rest }, ref) => {
const classes = clsx(className, "w-full border-collapse");
const classes = cn(className, "w-full border-collapse");
return <table className={classes} {...rest} ref={ref} />;
});
@@ -27,7 +27,7 @@ export const TableRow = forwardRef<
ElementRef<"tr">,
ComponentPropsWithoutRef<"tr">
>(({ className, ...rest }, ref) => {
const classes = clsx(className, "odd:bg-gray-100");
const classes = cn(className, "odd:bg-gray-100");
return <tr className={classes} {...rest} ref={ref} />;
});
@@ -35,7 +35,7 @@ export const TableHeadCell = forwardRef<
ElementRef<"th">,
ComponentPropsWithoutRef<"th">
>(({ children, className, ...rest }, ref) => {
const classes = clsx(className, "py-3 px-4 text-start");
const classes = cn(className, "py-3 px-4 text-start");
return (
<th className={classes} {...rest} ref={ref}>
@@ -48,7 +48,7 @@ export const TableCell = forwardRef<
ElementRef<"td">,
ComponentPropsWithoutRef<"td">
>(({ className, ...rest }, ref) => {
const classes = clsx(className, "py-3 px-4 border-t border-slate-200");
const classes = cn(className, "py-3 px-4 border-t border-slate-200");
return <td className={classes} {...rest} ref={ref} />;
});

View File

@@ -0,0 +1 @@
export * from "./wrap";

1
src/hooks/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./use-title";

View File

@@ -1,7 +1,7 @@
import { useState } from "react";
import { PodcastPreviewCard } from "../components/podcast/podcast-preview-card";
import { useTopPodcastsQuery } from "../services/podcasts/podcast.hooks";
import { PodcastPreviewCard, Input } from "../components";
import { useTopPodcastsQuery } from "../services";
export function Home() {
const { data, isLoading } = useTopPodcastsQuery();
@@ -28,15 +28,14 @@ export function Home() {
>
{filteredData?.length}
</span>
<input
type="text"
<Input
className={"w-1/3"}
value={search}
onChange={(e) => setSearch(e.target.value)}
onValueChange={setSearch}
placeholder="Filter podcasts..."
className={"w-1/3 rounded-md border border-gray-300 p-2"}
/>
</div>
<div className={"grid grid-cols-4 gap-x-6 gap-y-24"}>
<section role={"list"} className={"grid grid-cols-4 gap-x-6 gap-y-24"}>
{filteredData?.map((podcast) => (
<PodcastPreviewCard
detailUrl={`/podcast/${podcast.id}`}
@@ -46,7 +45,7 @@ export function Home() {
imageUrl={podcast.images.large}
/>
))}
</div>
</section>
</div>
);
}

View File

@@ -1,6 +1,6 @@
import { Outlet, useParams } from "react-router-dom";
import { usePodcastQuery } from "../services/podcasts/podcast.hooks";
import { PodcastInfoCard } from "../components/podcast/podcast-info-card";
import { usePodcastQuery } from "../services";
import { PodcastInfoCard } from "../components";
export function Podcast() {
const { podcastId } = useParams<{ podcastId: string }>();

View File

@@ -1,9 +1,7 @@
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { Home } from "./pages/home";
import { Podcast } from "./pages/podcast";
import { Episode } from "./components/episode";
import { PodcastEpisodesList } from "./components/podcast/podcast-episodes-list";
import { Layout } from "./components/ui/layout/layout";
import { PodcastEpisodesList, Episode, Layout } from "./components";
const router = createBrowserRouter([
{

1
src/services/index.ts Normal file
View File

@@ -0,0 +1 @@
export * from "./podcasts";

View File

@@ -0,0 +1 @@
export * from "./persisters";

View File

@@ -0,0 +1 @@
export * from "./create-idb-persister";

View File

@@ -0,0 +1,3 @@
export * from "./episode.dto";
export * from "./podcast.dto";
export * from "./podcast-extra.dto";

View File

@@ -0,0 +1,3 @@
export * from "./podcast.hooks";
export * from "./dto";
export * from "./podcasts.types";

View File

@@ -1,7 +1,6 @@
import { GetEpisodesResponse, GetTopPodcastsResponse } from "./podcasts.types";
import { PodcastDTO } from "./dto/podcast.dto";
import { PodcastDTO, PodcastExtraDTO } from "./dto";
import { RssParser } from "../rss-parser";
import { PodcastExtraDTO } from "./dto/podcast-extra.dto";
import { bypassCorsService } from "../bypass-cors";
class PodcastsService {

View File

@@ -1 +1,2 @@
export * from "./rss-parser";
export * from "./rss-parser.types";

View File

@@ -1,26 +1,14 @@
export interface Episode {
id: string;
releaseDate: string;
audioUrl: string;
title: string;
durationSeconds: number;
description: string;
}
export interface Podcast {
description: string;
episodes: Episode[];
}
import { RssEpisode, RssPodcast } from "./rss-parser.types";
export class RssParser {
public static parse(rss: string): Podcast {
public static parse(rss: string): RssPodcast {
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(rss, "text/xml");
const channel = xmlDoc.querySelector("channel");
const description = this.getElementInnerHtml(channel, "description");
const episodes: Episode[] = [];
const episodes: RssEpisode[] = [];
const items = Array.from(xmlDoc.querySelectorAll("item"));
@@ -55,6 +43,7 @@ export class RssParser {
episodes,
};
}
public static parseDuration(durationStr: string): number {
if (durationStr.includes(":")) {
const parts = durationStr.split(":").map((part) => parseInt(part, 10));
@@ -71,6 +60,7 @@ export class RssParser {
return parseInt(durationStr, 10);
}
}
public static getElementByTagName(element: Element | null, tagName: string) {
return element?.getElementsByTagName(tagName)[0];
}

View File

@@ -0,0 +1,13 @@
export interface RssEpisode {
id: string;
releaseDate: string;
audioUrl: string;
title: string;
durationSeconds: number;
description: string;
}
export interface RssPodcast {
description: string;
episodes: RssEpisode[];
}

6
src/utils/classnames.ts Normal file
View File

@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}

View File

@@ -1 +1,2 @@
export * from "./datetime";
export * from "./classnames";