feature/version-1 #1
2
.github/workflows/deploy.yml
vendored
2
.github/workflows/deploy.yml
vendored
@ -6,7 +6,7 @@ on:
|
|||||||
- main
|
- main
|
||||||
- dev
|
- dev
|
||||||
pull_request:
|
pull_request:
|
||||||
|
- main
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
@ -3,7 +3,7 @@ import type { RemixLinkProps, RemixNavLinkProps } from "@remix-run/react/dist/co
|
|||||||
import type { ReactNode, RefAttributes } from "react";
|
import type { ReactNode, RefAttributes } from "react";
|
||||||
import { createContext, useContext } from "react";
|
import { createContext, useContext } from "react";
|
||||||
import { SideNav } from "~/components/side-nav";
|
import { SideNav } from "~/components/side-nav";
|
||||||
import { TopNavbar } from "~/components/top-navbar";
|
// import { TopNavbar } from "~/components/top-navbar";
|
||||||
import type { StationWithTagsClientSide } from "~/models/station.server";
|
import type { StationWithTagsClientSide } from "~/models/station.server";
|
||||||
import type { TagWithStationsClientSide } from "~/models/tag.server";
|
import type { TagWithStationsClientSide } from "~/models/tag.server";
|
||||||
import type { UserWithFavoriteStationsClientSide } from "~/models/user.server";
|
import type { UserWithFavoriteStationsClientSide } from "~/models/user.server";
|
||||||
@ -28,7 +28,7 @@ export function useStationContext() {
|
|||||||
export function ListenLink(props: RemixLinkProps & RefAttributes<HTMLAnchorElement>) {
|
export function ListenLink(props: RemixLinkProps & RefAttributes<HTMLAnchorElement>) {
|
||||||
const { station } = useStationContext();
|
const { station } = useStationContext();
|
||||||
const url = props.to + (station ? `?station=${station.id}` : "");
|
const url = props.to + (station ? `?station=${station.id}` : "");
|
||||||
return <Link {...props} to={url}>{props.children}</Link>;
|
return <Link preventScrollReset={true} {...props} to={url}>{props.children}</Link>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -54,7 +54,7 @@ export function PageLayout({ children, tags, user, station }: PageLayoutProps) {
|
|||||||
<div className="drawer drawer-mobile">
|
<div className="drawer drawer-mobile">
|
||||||
<input id="primary-drawer" type="checkbox" className="drawer-toggle" />
|
<input id="primary-drawer" type="checkbox" className="drawer-toggle" />
|
||||||
<div className="drawer-content flex flex-col">
|
<div className="drawer-content flex flex-col">
|
||||||
<TopNavbar user={user} />
|
{/* <TopNavbar user={user} /> */}
|
||||||
<div className="py-2 px-6">
|
<div className="py-2 px-6">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,13 +1,20 @@
|
|||||||
|
import { useNavigate } from "@remix-run/react";
|
||||||
import type { ReactNode} from "react";
|
import type { ReactNode} from "react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import type { StationWithTagsClientSide } from "~/models/station.server";
|
import type { StationWithTagsClientSide } from "~/models/station.server";
|
||||||
|
import { getStationUrl } from "~/utils";
|
||||||
|
|
||||||
export type StationPlayerProps = {
|
export type StationPlayerProps = {
|
||||||
station: StationWithTagsClientSide | null
|
station: StationWithTagsClientSide | null,
|
||||||
|
nextPrevStationIds: {
|
||||||
|
prev?: string;
|
||||||
|
next?: string;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export function StationPlayer({ station }: StationPlayerProps) {
|
export function StationPlayer({ station, nextPrevStationIds }: StationPlayerProps) {
|
||||||
const [player, setPlayer] = useState<ReactNode | null>(null);
|
const [player, setPlayer] = useState<ReactNode | null>(null);
|
||||||
|
const route = useNavigate();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof window === 'undefined') return;
|
if (typeof window === 'undefined') return;
|
||||||
@ -22,21 +29,31 @@ export function StationPlayer({ station }: StationPlayerProps) {
|
|||||||
title={station.name}
|
title={station.name}
|
||||||
description={station.description || undefined}
|
description={station.description || undefined}
|
||||||
image={station.imgUrl}
|
image={station.imgUrl}
|
||||||
secondDescription={`* ${station}`}
|
isNextButtonDisabled={!nextPrevStationIds.next}
|
||||||
|
isPrevButtonDisabled={!nextPrevStationIds.prev}
|
||||||
|
onNextButtonClicked={() => {
|
||||||
|
if (!nextPrevStationIds.next) return;
|
||||||
|
route(getStationUrl(nextPrevStationIds.next))
|
||||||
|
}}
|
||||||
|
onPrevButtonClicked={() => {
|
||||||
|
if (!nextPrevStationIds.prev) return;
|
||||||
|
route(getStationUrl(nextPrevStationIds.prev))
|
||||||
|
}}
|
||||||
|
secondDescription={station.popularity ? <>★ {station.popularity}</> : undefined}
|
||||||
/>);
|
/>);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('rerrerer', error)
|
console.log('rerrerer', error)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
importComponent()
|
importComponent()
|
||||||
}, [station])
|
}, [station, nextPrevStationIds, route])
|
||||||
|
|
||||||
if (!station || !player) {
|
if (!station || !player) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="fixed bottom-0 right-[-1rem] w-[76%] h-[70px] px-4 py-2 z-50 flex justify-end content-center items-center gap-2 text-accent-content">
|
className="fixed bottom-0 right-0 w-full lg:w-[calc(100vw-20rem)] h-[90px] z-50">
|
||||||
{player}
|
{player}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { PlayIcon } from "@heroicons/react/24/solid";
|
import { PlayIcon, SpeakerWaveIcon } from "@heroicons/react/24/solid";
|
||||||
import type { Tag } from "@prisma/client";
|
import type { Tag } from "@prisma/client";
|
||||||
import { Link } from "@remix-run/react";
|
import { Link, useLocation } from "@remix-run/react";
|
||||||
import { ListenLink } from "~/components/page-layout";
|
import { ListenLink } from "~/components/page-layout";
|
||||||
import type { StationWithTagsClientSide } from "~/models/station.server";
|
import type { StationWithTagsClientSide } from "~/models/station.server";
|
||||||
import type { ConvertDatesToStrings } from "~/utils";
|
import { getStationUrl, type ConvertDatesToStrings } from "~/utils";
|
||||||
|
|
||||||
export type StationsGalleryProps = {
|
export type StationsGalleryProps = {
|
||||||
stations: StationWithTagsClientSide[];
|
stations: StationWithTagsClientSide[];
|
||||||
@ -11,14 +11,8 @@ export type StationsGalleryProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export function StationsGallery({ stations, tag }: StationsGalleryProps) {
|
export function StationsGallery({ stations, tag }: StationsGalleryProps) {
|
||||||
|
const location = useLocation();
|
||||||
function getStationUrl(id: string): string {
|
const currentStationId = new URLSearchParams(location.search).get('station')?.trim()
|
||||||
if (tag) {
|
|
||||||
return `/listen/tag/${tag?.slug}?station=${id}`;
|
|
||||||
}
|
|
||||||
return `/listen?station=${id}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||||
{stations.map((station) => {
|
{stations.map((station) => {
|
||||||
@ -26,9 +20,14 @@ export function StationsGallery({ stations, tag }: StationsGalleryProps) {
|
|||||||
<div key={station.id} className="card card-compact bg-base-100 shadow-xl mb-[70px]">
|
<div key={station.id} className="card card-compact bg-base-100 shadow-xl mb-[70px]">
|
||||||
<figure><img src={station.imgUrl} alt="Radio Station" /></figure>
|
<figure><img src={station.imgUrl} alt="Radio Station" /></figure>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<h2 className="card-title">
|
<div className="flex items-center justify-start gap-2">
|
||||||
{station.name}
|
<h2 className="card-title">
|
||||||
</h2>
|
{station.name}
|
||||||
|
</h2>
|
||||||
|
{station.id === currentStationId ? <div className="w-4 h-4">
|
||||||
|
<SpeakerWaveIcon />
|
||||||
|
</div> : null}
|
||||||
|
</div>
|
||||||
<h2 className="flex gap-1">
|
<h2 className="flex gap-1">
|
||||||
{station.tags.map((t, id) => {
|
{station.tags.map((t, id) => {
|
||||||
return <ListenLink key={id} to={`/listen/tag/${t.tag.slug}`}
|
return <ListenLink key={id} to={`/listen/tag/${t.tag.slug}`}
|
||||||
@ -37,7 +36,7 @@ export function StationsGallery({ stations, tag }: StationsGalleryProps) {
|
|||||||
</h2>
|
</h2>
|
||||||
<p>{station.description}</p>
|
<p>{station.description}</p>
|
||||||
<div className="card-actions justify-end">
|
<div className="card-actions justify-end">
|
||||||
<Link to={getStationUrl(station.id)}
|
<Link preventScrollReset to={getStationUrl(station.id, tag)}
|
||||||
className={`btn btn-primary gap-2 plausible-event-name=play-station plausible-event-station=${station.slug}`}>
|
className={`btn btn-primary gap-2 plausible-event-name=play-station plausible-event-station=${station.slug}`}>
|
||||||
<PlayIcon className="h-6 w-6" />
|
<PlayIcon className="h-6 w-6" />
|
||||||
Listen Now
|
Listen Now
|
||||||
|
@ -37,6 +37,34 @@ export function getStations(reliability: number = 80) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param stationId current station id that we want to get next and prev
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getPrevNextStations = async (stationId?: string) => {
|
||||||
|
const defaultData = {
|
||||||
|
prev: undefined,
|
||||||
|
next: undefined
|
||||||
|
}
|
||||||
|
if (!stationId) return defaultData;
|
||||||
|
|
||||||
|
const stations = await getStations();
|
||||||
|
|
||||||
|
return stations.reduce((prev, currStation, index, array) => {
|
||||||
|
if (currStation.id === stationId) {
|
||||||
|
return {
|
||||||
|
prev: array[index - 1]?.id,
|
||||||
|
next: array[index + 1]?.id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
}, defaultData as {
|
||||||
|
prev?: string,
|
||||||
|
next?: string,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch stations tagged with `tags` and a reliability score GTE to the `reliability` parameter.
|
* Fetch stations tagged with `tags` and a reliability score GTE to the `reliability` parameter.
|
||||||
*/
|
*/
|
||||||
|
@ -1,166 +0,0 @@
|
|||||||
import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node";
|
|
||||||
import { json, redirect } from "@remix-run/node";
|
|
||||||
import { Form, Link, useActionData, useSearchParams } from "@remix-run/react";
|
|
||||||
import { useEffect, useRef } from "react";
|
|
||||||
|
|
||||||
import { createUser, getUserByEmail } from "~/models/user.server";
|
|
||||||
import { createUserSession, getUserId } from "~/session.server";
|
|
||||||
import { safeRedirect, validateEmail } from "~/utils";
|
|
||||||
|
|
||||||
export const loader = async ({ request }: LoaderArgs) => {
|
|
||||||
const userId = await getUserId(request);
|
|
||||||
if (userId) return redirect("/");
|
|
||||||
return json({});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const action = async ({ request }: ActionArgs) => {
|
|
||||||
const formData = await request.formData();
|
|
||||||
const email = formData.get("email");
|
|
||||||
const password = formData.get("password");
|
|
||||||
const redirectTo = safeRedirect(formData.get("redirectTo"), "/");
|
|
||||||
|
|
||||||
if (!validateEmail(email)) {
|
|
||||||
return json(
|
|
||||||
{ errors: { email: "Email is invalid", password: null } },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof password !== "string" || password.length === 0) {
|
|
||||||
return json(
|
|
||||||
{ errors: { email: null, password: "Password is required" } },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password.length < 8) {
|
|
||||||
return json(
|
|
||||||
{ errors: { email: null, password: "Password is too short" } },
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingUser = await getUserByEmail(email);
|
|
||||||
if (existingUser) {
|
|
||||||
return json(
|
|
||||||
{
|
|
||||||
errors: {
|
|
||||||
email: "A user already exists with this email",
|
|
||||||
password: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await createUser(email, password);
|
|
||||||
|
|
||||||
return createUserSession({
|
|
||||||
redirectTo,
|
|
||||||
remember: false,
|
|
||||||
request,
|
|
||||||
userId: user.id,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
export const meta: V2_MetaFunction = () => [{ title: "Sign Up" }];
|
|
||||||
|
|
||||||
export default function Join() {
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const redirectTo = searchParams.get("redirectTo") ?? undefined;
|
|
||||||
const actionData = useActionData<typeof action>();
|
|
||||||
const emailRef = useRef<HTMLInputElement>(null);
|
|
||||||
const passwordRef = useRef<HTMLInputElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (actionData?.errors?.email) {
|
|
||||||
emailRef.current?.focus();
|
|
||||||
} else if (actionData?.errors?.password) {
|
|
||||||
passwordRef.current?.focus();
|
|
||||||
}
|
|
||||||
}, [actionData]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex min-h-full flex-col justify-center">
|
|
||||||
<div className="mx-auto w-full max-w-md px-8">
|
|
||||||
<Form method="post" className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor="email"
|
|
||||||
className="block text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Email address
|
|
||||||
</label>
|
|
||||||
<div className="mt-1">
|
|
||||||
<input
|
|
||||||
ref={emailRef}
|
|
||||||
id="email"
|
|
||||||
required
|
|
||||||
autoFocus={true}
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
autoComplete="email"
|
|
||||||
aria-invalid={actionData?.errors?.email ? true : undefined}
|
|
||||||
aria-describedby="email-error"
|
|
||||||
className="w-full rounded border border-gray-500 px-2 py-1 text-lg"
|
|
||||||
/>
|
|
||||||
{actionData?.errors?.email ? (
|
|
||||||
<div className="pt-1 text-red-700" id="email-error">
|
|
||||||
{actionData.errors.email}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label
|
|
||||||
htmlFor="password"
|
|
||||||
className="block text-sm font-medium text-gray-700"
|
|
||||||
>
|
|
||||||
Password
|
|
||||||
</label>
|
|
||||||
<div className="mt-1">
|
|
||||||
<input
|
|
||||||
id="password"
|
|
||||||
ref={passwordRef}
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
autoComplete="new-password"
|
|
||||||
aria-invalid={actionData?.errors?.password ? true : undefined}
|
|
||||||
aria-describedby="password-error"
|
|
||||||
className="w-full rounded border border-gray-500 px-2 py-1 text-lg"
|
|
||||||
/>
|
|
||||||
{actionData?.errors?.password ? (
|
|
||||||
<div className="pt-1 text-red-700" id="password-error">
|
|
||||||
{actionData.errors.password}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<input type="hidden" name="redirectTo" value={redirectTo} />
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="w-full rounded bg-blue-500 px-4 py-2 text-white hover:bg-blue-600 focus:bg-blue-400"
|
|
||||||
>
|
|
||||||
Create Account
|
|
||||||
</button>
|
|
||||||
<div className="flex items-center justify-center">
|
|
||||||
<div className="text-center text-sm text-gray-500">
|
|
||||||
Already have an account?{" "}
|
|
||||||
<Link
|
|
||||||
className="text-blue-500 underline"
|
|
||||||
to={{
|
|
||||||
pathname: "/login",
|
|
||||||
search: searchParams.toString(),
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Log in
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ export default function ListenHome() {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Breadcrumbs>
|
<Breadcrumbs>
|
||||||
<Link to="/listen">Home</Link>
|
<Link preventScrollReset to="/listen">Home</Link>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
<StationsGallery stations={stations} />
|
<StationsGallery stations={stations} />
|
||||||
<Outlet />
|
<Outlet />
|
||||||
|
@ -4,7 +4,7 @@ import { Outlet, useLoaderData } from "@remix-run/react";
|
|||||||
import { PageLayout } from "~/components/page-layout";
|
import { PageLayout } from "~/components/page-layout";
|
||||||
import { StationPlayer } from "~/components/station-player";
|
import { StationPlayer } from "~/components/station-player";
|
||||||
import type { StationWithTags } from "~/models/station.server";
|
import type { StationWithTags } from "~/models/station.server";
|
||||||
import { getStationById } from "~/models/station.server";
|
import { getPrevNextStations, getStationById } from "~/models/station.server";
|
||||||
import type { TagWithStations } from "~/models/tag.server";
|
import type { TagWithStations } from "~/models/tag.server";
|
||||||
import { getTags } from "~/models/tag.server";
|
import { getTags } from "~/models/tag.server";
|
||||||
import { getUser } from "~/session.server";
|
import { getUser } from "~/session.server";
|
||||||
@ -15,15 +15,17 @@ export async function loader({ request }: LoaderArgs) {
|
|||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
const stationId = url.searchParams.get("station");
|
const stationId = url.searchParams.get("station");
|
||||||
const station: StationWithTags | null = stationId ? await getStationById(stationId) : null;
|
const station: StationWithTags | null = stationId ? await getStationById(stationId) : null;
|
||||||
return json({ user, tags, station });
|
const prevNextStationIds = await getPrevNextStations(station?.id)
|
||||||
|
return json({ user, tags, station, prevNextStationIds });
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ListenLayout() {
|
export default function ListenLayout() {
|
||||||
const { tags, user, station } = useLoaderData<typeof loader>();
|
const { tags, user, station, prevNextStationIds } = useLoaderData<typeof loader>();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageLayout tags={tags} user={user} station={station}>
|
<PageLayout tags={tags} user={user} station={station}>
|
||||||
<Outlet />
|
<Outlet />
|
||||||
<StationPlayer station={station} />
|
<StationPlayer station={station} nextPrevStationIds={prevNextStationIds} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node";
|
import type { ActionArgs, LoaderArgs, V2_MetaFunction } from "@remix-run/node";
|
||||||
import { json, redirect } from "@remix-run/node";
|
import { json, redirect } from "@remix-run/node";
|
||||||
import { Form, Link, useActionData, useSearchParams } from "@remix-run/react";
|
import { Form, useActionData, useSearchParams } from "@remix-run/react";
|
||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef } from "react";
|
||||||
|
|
||||||
import { verifyLogin } from "~/models/user.server";
|
import { verifyLogin } from "~/models/user.server";
|
||||||
@ -150,23 +150,12 @@ export default function LoginPage() {
|
|||||||
/>
|
/>
|
||||||
<label
|
<label
|
||||||
htmlFor="remember"
|
htmlFor="remember"
|
||||||
className="ml-2 block text-sm text-gray-900"
|
className="ml-2 block text-sm text-secondary"
|
||||||
>
|
>
|
||||||
Remember me
|
Remember me
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-center text-sm text-gray-500">
|
|
||||||
Don't have an account?{" "}
|
|
||||||
<Link
|
|
||||||
className="text-blue-500 underline"
|
|
||||||
to={{
|
|
||||||
pathname: "/join",
|
|
||||||
search: searchParams.toString()
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Sign up
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,6 +27,13 @@ export function safeRedirect(
|
|||||||
return to;
|
return to;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getStationUrl(id: string, tag?: {slug: string}): string {
|
||||||
|
if (tag) {
|
||||||
|
return `/listen/tag/${tag?.slug}?station=${id}`;
|
||||||
|
}
|
||||||
|
return `/listen?station=${id}`;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This base hook is used in other hooks to quickly search for specific data
|
* This base hook is used in other hooks to quickly search for specific data
|
||||||
* across all loader data using useMatches.
|
* across all loader data using useMatches.
|
||||||
|
17359
package-lock.json
generated
17359
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -39,7 +39,7 @@
|
|||||||
"isbot": "^3.6.8",
|
"isbot": "^3.6.8",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-radio-player": "^0.0.7",
|
"react-radio-player": "^0.1.8",
|
||||||
"tiny-invariant": "^1.3.1"
|
"tiny-invariant": "^1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -7,7 +7,7 @@ export default {
|
|||||||
},
|
},
|
||||||
plugins: [require("@tailwindcss/typography"), require("daisyui")],
|
plugins: [require("@tailwindcss/typography"), require("daisyui")],
|
||||||
daisyui: {
|
daisyui: {
|
||||||
darkTheme: "night",
|
darkTheme: "black",
|
||||||
themes: ["light", "night"]
|
themes: ["light", "black"]
|
||||||
}
|
}
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
Arguments:
|
Arguments:
|
||||||
C:\Program Files\nodejs\node.exe C:\Users\98921\AppData\Roaming\npm\node_modules\yarn\bin\yarn.js add react-radio-player
|
C:\Program Files\nodejs\node.exe C:\Users\98921\AppData\Roaming\npm\node_modules\yarn\bin\yarn.js
|
||||||
|
|
||||||
PATH:
|
PATH:
|
||||||
C:\Program Files\Eclipse Adoptium\jre-21.0.1.12-hotspot\bin;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Git\cmd;C:\Program Files\nodejs\;C:\ProgramData\chocolatey\bin;C:\Users\98921\AppData\Local\Microsoft\WindowsApps;;C:\Users\98921\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\98921\AppData\Roaming\npm
|
C:\Program Files\Eclipse Adoptium\jre-21.0.1.12-hotspot\bin;C:\Program Files (x86)\Common Files\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR;C:\Program Files\Git\cmd;C:\Program Files\nodejs\;C:\ProgramData\chocolatey\bin;C:\Users\98921\AppData\Local\Microsoft\WindowsApps;;C:\Users\98921\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\98921\AppData\Roaming\npm
|
||||||
@ -58,7 +58,7 @@ npm manifest:
|
|||||||
"isbot": "^3.6.8",
|
"isbot": "^3.6.8",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-radio-player": "^0.0.7",
|
"react-radio-player": "^0.1.4",
|
||||||
"tiny-invariant": "^1.3.1"
|
"tiny-invariant": "^1.3.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user