diff --git a/README.md b/README.md index a90cdff..856fbfe 100644 --- a/README.md +++ b/README.md @@ -64,13 +64,21 @@ Awesome Radio is a personal internet radio station aggregator. See the [demo](ht cp .env.example .env ``` -2. Migrate & Seed the SQLite DB +2. Install dependencies + +```shell +npm install +``` + +3. Migrate & Seed the SQLite DB ```shell npx prisma migrate deploy npx prisma db seed ``` +Note: The db schema, migrations, and seed steps are located in the [prisma](prisma) folder. + ### Running ```shell diff --git a/app/components/page-layout.tsx b/app/components/page-layout.tsx index d58fa1b..4d2da88 100644 --- a/app/components/page-layout.tsx +++ b/app/components/page-layout.tsx @@ -1,13 +1,46 @@ -import { RadioIcon, UserCircleIcon } from "@heroicons/react/24/solid"; import { Link, NavLink } from "@remix-run/react"; import type { RemixLinkProps, RemixNavLinkProps } from "@remix-run/react/dist/components"; -import type { ReactNode } from "react"; -import * as React from "react"; +import type { ReactNode, RefAttributes } from "react"; import { createContext, useContext } from "react"; +import { SideNav } from "~/components/side-nav"; +import { TopNavbar } from "~/components/top-navbar"; import type { StationWithTagsClientSide } from "~/models/station.server"; import type { TagWithStationsClientSide } from "~/models/tag.server"; import type { UserWithFavoriteStationsClientSide } from "~/models/user.server"; +export type StationContextType = { + station: StationWithTagsClientSide | null +} + +export const StationContext = createContext({ station: null }); + +/** + * Hook that provides the active radio station. Station is null if one is not selected. + */ +export function useStationContext() { + return useContext(StationContext); +} + +/** + * Helper component that wraps and appends `?station={station.id}` to the link `to` prop if + * there's an active station playing + */ +export function ListenLink(props: RemixLinkProps & RefAttributes) { + const { station } = useStationContext(); + const url = props.to + (station ? `?station=${station.id}` : ""); + return {props.children}; +} + +/** + * Helper component that wraps and appends `?station={station.id}` to the link `to` prop if + * there's an active station playing + */ +export function ListenNavLink(props: RemixNavLinkProps & RefAttributes) { + const { station } = useStationContext(); + const url = props.to + (station ? `?station=${station.id}` : ""); + return {props.children}; +} + export type PageLayoutProps = { children: ReactNode; tags: TagWithStationsClientSide[]; @@ -15,123 +48,20 @@ export type PageLayoutProps = { station: StationWithTagsClientSide | null; } -export type StationContextType = { - station: StationWithTagsClientSide | null -} - -const StationContext = createContext({ station: null }); - -export function useStationContext() { - return useContext(StationContext); -} - - -export function ListenLink(props: RemixLinkProps & React.RefAttributes) { - const { station } = useStationContext(); - const url = props.to + (station ? `?station=${station.id}` : ""); - return {props.children}; -} - -export function ListenNavLink(props: RemixNavLinkProps & React.RefAttributes) { - const { station } = useStationContext(); - const url = props.to + (station ? `?station=${station.id}` : ""); - return {props.children}; -} - -export type ManageContentNavProps = { - user?: UserWithFavoriteStationsClientSide; -}; - -export function ManageContentNav({ user }: ManageContentNavProps) { - if (!user) { - return <>; - } - return ( - <> -
  • - Manage Content -
  • -
  • - Sources -
  • - - ); -} - export function PageLayout({ children, tags, user, station }: PageLayoutProps) { - return (
    -
    -
    - -
    -
    - -

    Awesome Radio

    -
    -
    - {user ? -
    - -
      -
    • - Logout -
    • -
    -
    : - Join - } - -
    -
    +
    {children}
    -
      -
    • - -

      Awesome Radio

      -
    • -
    • - Listen -
    • -
    • - Home -
    • -
    • - Tags -
    • - {tags - .filter(tag => tag.stations.length > 0) - .map((tag) => { - return ( -
    • - - {tag.name} - {tag.stations?.length ?? 0} - -
    • - ); - })} - -
    - +
    diff --git a/app/components/side-nav.tsx b/app/components/side-nav.tsx new file mode 100644 index 0000000..3dd9186 --- /dev/null +++ b/app/components/side-nav.tsx @@ -0,0 +1,63 @@ +import { RadioIcon } from "@heroicons/react/24/solid"; +import { NavLink } from "@remix-run/react"; +import { ListenNavLink } from "~/components/page-layout"; +import type { TagWithStationsClientSide } from "~/models/tag.server"; +import type { UserWithFavoriteStationsClientSide } from "~/models/user.server"; + +export type ManageContentNavProps = { + user?: UserWithFavoriteStationsClientSide; +}; + +export function ManageContentNav({ user }: ManageContentNavProps) { + if (!user) { + return <>; + } + return ( + <> +
  • + Manage Content +
  • +
  • + Sources +
  • + + ); +} + +export type SideNavProps = { + tags: TagWithStationsClientSide[]; + user?: UserWithFavoriteStationsClientSide; +}; + +export function SideNav({ tags, user }: SideNavProps) { + return ( +
      +
    • + +

      Awesome Radio

      +
    • +
    • + Listen +
    • +
    • + Home +
    • +
    • + Tags +
    • + {tags + .filter(tag => tag.stations.length > 0) + .map((tag) => { + return ( +
    • + + {tag.name} + {tag.stations?.length ?? 0} + +
    • + ); + })} + +
    + ); +} diff --git a/app/components/station-player.tsx b/app/components/station-player.tsx index 8f8b534..b11731d 100644 --- a/app/components/station-player.tsx +++ b/app/components/station-player.tsx @@ -13,7 +13,7 @@ export function StationPlayer({ station }: StationPlayerProps) { className="fixed bottom-0 left-0 w-full h-[70px] px-4 py-2 z-50 flex justify-end content-center items-center gap-2 bg-accent text-accent-content">

    Now Playing: {station.name}

    -