From d3323e242d4a725ffd341111428989e878855f1b Mon Sep 17 00:00:00 2001 From: Eli Gundry Date: Thu, 11 Jul 2024 23:46:12 -0400 Subject: [PATCH] fix: move the layout to an actual remix layout This allows me to have a data loader for the layout itself, which can have a different caching strategy than the root loader. --- app/components/Base/HomeSection.tsx | 8 +- app/components/Base/Layout.tsx | 150 ---------- app/components/Base/LayoutV2.tsx | 145 ---------- app/components/Base/index.tsx | 1 - app/components/ErrorBoundary.tsx | 38 ++- app/context/User.tsx | 14 +- app/routes/_index.tsx | 239 ---------------- app/routes/_layout._index.tsx | 25 ++ app/routes/_layout.about.tsx | 93 +++++++ ...re.$genre.tsx => _layout.genre.$genre.tsx} | 7 +- app/routes/{genres.tsx => _layout.genres.tsx} | 26 +- app/routes/_layout.help.tsx | 37 +++ ...abel.$slug.tsx => _layout.label.$slug.tsx} | 7 +- app/routes/{label.tsx => _layout.label.tsx} | 7 +- app/routes/{labs.tsx => _layout.labs.tsx} | 60 ++-- app/routes/_layout.library.tsx | 56 ++++ ...slug.tsx => _layout.publication.$slug.tsx} | 6 +- ...> _layout.spotify.artist-id.$artistID.tsx} | 7 +- ...tsx => _layout.spotify.artist.$artist.tsx} | 7 +- ...ies.tsx => _layout.spotify.categories.tsx} | 66 ++--- ...d.tsx => _layout.spotify.category.$id.tsx} | 21 +- ... => _layout.spotify.featured-playlist.tsx} | 7 +- ...or-you.tsx => _layout.spotify.for-you.tsx} | 7 +- ...ibrary.tsx => _layout.spotify.library.tsx} | 7 +- ...s.tsx => _layout.spotify.new-releases.tsx} | 7 +- ..._layout.spotify.top-artists-relations.tsx} | 8 +- ...ts.tsx => _layout.spotify.top-artists.tsx} | 8 +- app/routes/_layout.tsx | 256 ++++++++++++++++++ ...name.tsx => _layout.twitter.$username.tsx} | 23 +- app/routes/about.tsx | 105 ------- app/routes/help.tsx | 39 --- app/routes/library.tsx | 58 ---- 32 files changed, 591 insertions(+), 954 deletions(-) delete mode 100644 app/components/Base/Layout.tsx delete mode 100644 app/components/Base/LayoutV2.tsx delete mode 100644 app/routes/_index.tsx create mode 100644 app/routes/_layout._index.tsx create mode 100644 app/routes/_layout.about.tsx rename app/routes/{genre.$genre.tsx => _layout.genre.$genre.tsx} (93%) rename app/routes/{genres.tsx => _layout.genres.tsx} (62%) create mode 100644 app/routes/_layout.help.tsx rename app/routes/{label.$slug.tsx => _layout.label.$slug.tsx} (86%) rename app/routes/{label.tsx => _layout.label.tsx} (86%) rename app/routes/{labs.tsx => _layout.labs.tsx} (50%) create mode 100644 app/routes/_layout.library.tsx rename app/routes/{publication.$slug.tsx => _layout.publication.$slug.tsx} (98%) rename app/routes/{spotify.artist-id.$artistID.tsx => _layout.spotify.artist-id.$artistID.tsx} (94%) rename app/routes/{spotify.artist.$artist.tsx => _layout.spotify.artist.$artist.tsx} (93%) rename app/routes/{spotify.categories.tsx => _layout.spotify.categories.tsx} (54%) rename app/routes/{spotify.category.$id.tsx => _layout.spotify.category.$id.tsx} (82%) rename app/routes/{spotify.featured-playlist.tsx => _layout.spotify.featured-playlist.tsx} (89%) rename app/routes/{spotify.for-you.tsx => _layout.spotify.for-you.tsx} (91%) rename app/routes/{spotify.library.tsx => _layout.spotify.library.tsx} (91%) rename app/routes/{spotify.new-releases.tsx => _layout.spotify.new-releases.tsx} (91%) rename app/routes/{spotify.top-artists-relations.tsx => _layout.spotify.top-artists-relations.tsx} (93%) rename app/routes/{spotify.top-artists.tsx => _layout.spotify.top-artists.tsx} (93%) create mode 100644 app/routes/_layout.tsx rename app/routes/{twitter.$username.tsx => _layout.twitter.$username.tsx} (89%) delete mode 100644 app/routes/about.tsx delete mode 100644 app/routes/help.tsx delete mode 100644 app/routes/library.tsx diff --git a/app/components/Base/HomeSection.tsx b/app/components/Base/HomeSection.tsx index 89384c9..d90be71 100644 --- a/app/components/Base/HomeSection.tsx +++ b/app/components/Base/HomeSection.tsx @@ -1,4 +1,4 @@ -import clsx from 'clsx' +import { cn } from '~/lib/util' import { Heading, Typography } from '~/components/Base' @@ -15,12 +15,12 @@ const HomeSection: React.FC> = ({ className, }) => { return ( -
- +
+ {title} {typeof subtitle === 'string' ? ( - + {subtitle} ) : ( diff --git a/app/components/Base/Layout.tsx b/app/components/Base/Layout.tsx deleted file mode 100644 index 03d7a80..0000000 --- a/app/components/Base/Layout.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React from 'react' -import { ClientOnly } from 'remix-utils/client-only' - -import { cn } from '~/lib/util' - -import AutoAlert from '~/components/AutoAlert' -import { DesktopLoader, MobileLoader } from '~/components/Loading' -import SearchBreadcrumbs, { - SearchBreadcrumbsProps, -} from '~/components/SearchBreadcrumbs' -import useLoading from '~/hooks/useLoading' -import { useIsMobile } from '~/hooks/useMediaQuery' - -import { A, ButtonLink, Container, EmojiText, Link } from './index' - -interface LayoutProps { - className?: string - headerBreadcrumbs?: SearchBreadcrumbsProps['crumbs'] - hideFooter?: boolean -} - -const Layout: React.FC> = ({ - children, - className, - headerBreadcrumbs, - hideFooter = false, -}) => { - const { loading } = useLoading() - const isMobile = useIsMobile() - - return ( - <> -
- - -

- - - Album Mode.party{' '} - - -

- {headerBreadcrumbs && ( - .breadcrumbs]:md:py-0', - )} - crumbs={headerBreadcrumbs} - /> - )} -
- - - Library - - -
-
-
-
- {children} -
- {!hideFooter && ( - - - - )} - - {() => isMobile && } - - ) -} - -export default Layout diff --git a/app/components/Base/LayoutV2.tsx b/app/components/Base/LayoutV2.tsx deleted file mode 100644 index f2d7451..0000000 --- a/app/components/Base/LayoutV2.tsx +++ /dev/null @@ -1,145 +0,0 @@ -import React from 'react' -import { ClientOnly } from 'remix-utils/client-only' - -import { cn } from '~/lib/util' - -import AutoAlert from '~/components/AutoAlert' -import SuperHeaderSearch from '~/components/Forms/SuperHeaderSearch' -import { DesktopLoader, MobileLoader } from '~/components/Loading' -import SearchBreadcrumbs, { - SearchBreadcrumbsProps, -} from '~/components/SearchBreadcrumbs' -import useLoading from '~/hooks/useLoading' -import { useIsMobile } from '~/hooks/useMediaQuery' - -import { A, Container, EmojiText, Link } from './index' - -interface LayoutProps { - className?: string - headerBreadcrumbs?: SearchBreadcrumbsProps['crumbs'] - hideFooter?: boolean -} - -const Layout: React.FC> = ({ - children, - className, - headerBreadcrumbs, - hideFooter = false, -}) => { - const { loading } = useLoading() - const isMobile = useIsMobile() - - return ( - <> -
- - :not(.super-search)]:hidden', - )} - > -

- - - Album Mode.party{' '} - - -

- -
-
-
- {headerBreadcrumbs && ( - .breadcrumbs]:md:py-0', - )} - crumbs={headerBreadcrumbs} - /> - )} - {children} -
- {!hideFooter && ( - - - - )} - - {() => isMobile && } - - ) -} - -export default Layout diff --git a/app/components/Base/index.tsx b/app/components/Base/index.tsx index 082e717..614c727 100644 --- a/app/components/Base/index.tsx +++ b/app/components/Base/index.tsx @@ -5,7 +5,6 @@ import React from 'react' import { cn } from '~/lib/util' -export { default as Layout } from './LayoutV2' export { default as EmojiText } from './EmojiText' const headingVariants = cva('', { diff --git a/app/components/ErrorBoundary.tsx b/app/components/ErrorBoundary.tsx index 0106c28..86f636e 100644 --- a/app/components/ErrorBoundary.tsx +++ b/app/components/ErrorBoundary.tsx @@ -6,7 +6,6 @@ import { Container, EmojiText, Heading, - Layout, Typography, } from '~/components/Base' import Document from '~/components/Base/Document' @@ -33,26 +32,23 @@ export const PageErrorBoundary: React.FC = () => { return ( - - -
- ⛔️ Whoops! - - We seem to have run into an error. We are working on fixing it - now. - -
- Detailed error message -
{body}
-
- - - Head Home - - -
-
-
+ +
+ ⛔️ Whoops! + + We seem to have run into an error. We are working on fixing it now. + +
+ Detailed error message +
{body}
+
+ + + Head Home + + +
+
) } diff --git a/app/context/User.tsx b/app/context/User.tsx index f40b1e6..d5bcab6 100644 --- a/app/context/User.tsx +++ b/app/context/User.tsx @@ -11,13 +11,17 @@ const UserProvider: React.FC< React.PropsWithChildren<{ user: User | null }> > = ({ user, children }) => { const sendEvent = useGTM() + const [, setIdentified] = React.useState(false) useEffect(() => { - if (user?.id) { - sendEvent({ - user_id: user.id, - }) - } + setIdentified((identified) => { + if (!identified && user?.id) { + sendEvent({ user_id: user.id }) + return true + } + + return false + }) }, [user?.id, sendEvent]) return {children} diff --git a/app/routes/_index.tsx b/app/routes/_index.tsx deleted file mode 100644 index a2e384f..0000000 --- a/app/routes/_index.tsx +++ /dev/null @@ -1,239 +0,0 @@ -import { LoaderFunctionArgs, json } from '@remix-run/node' -import { useLoaderData } from '@remix-run/react' -import clsx from 'clsx' - -import { getRequestContextValues } from '~/lib/context.server' - -import { - ButtonLink, - Container, - EmojiText, - Heading, - Layout, - Link, -} from '~/components/Base' -import ButtonLinkGroup, { - ButtonLinkGroupWrapper, -} from '~/components/Base/ButtonLinkGroup' -import HomeSection from '~/components/Base/HomeSection' -import { PageErrorBoundary } from '~/components/ErrorBoundary' -import GenreSearchForm from '~/components/Forms/GenreSearch' -import RelatedArtistSearchForm from '~/components/Forms/RelatedArtistSearch' -import SpotifyLoginButton from '~/components/Spotify/LoginButton' -import config from '~/config' -import useLoading from '~/hooks/useLoading' -import useSavedSearches from '~/hooks/useSavedSearches' -import useUser from '~/hooks/useUser' - -export async function loader({ request, context }: LoaderFunctionArgs) { - const { database } = getRequestContextValues(request, context) - - return json( - { publications: await database.getPublications() }, - { - headers: { - 'Cache-Control': config.cacheControl.public, - }, - }, - ) -} - -export const ErrorBoundary = PageErrorBoundary - -export default function Index() { - const data = useLoaderData() - const { hasSavedSearches, searches } = useSavedSearches() - const { loading } = useLoading() - const user = useUser() - - return ( - - -
-
- man dancing to music -
- - It's time for new music - -

- Tired of the same old songs?
- Spotify's algorithim know you too well?
- Take a chance on a random album! -

- - - Play me someting - - -
-
-
- - - - Genre} - subtitle="Have a genre in mind? Search for it and we'll find you something." - className="genre" - > -
- - - Random Genre - -
-
- - - {!user ? ( - - ) : ( - <> - - Spotify Library - - - Currently Playing - - - Top Artists - - - Top Artists Relations - - - For You - - - )} - - New Release - - - Featured Playlist - - - Playlist Categories - - - - - `/publication/${publication.slug}`} - keyFunction={(publication) => publication.slug} - childFunction={(publication) => publication.name} - disabled={loading} - /> - - {hasSavedSearches && ( - - path} - keyFunction={({ path }) => path} - childFunction={({ crumbs }) => ( -
    - {crumbs.map((crumb) => ( -
  • {crumb}
  • - ))} -
- )} - disabled={loading} - /> -
- )} -
-
- ) -} diff --git a/app/routes/_layout._index.tsx b/app/routes/_layout._index.tsx new file mode 100644 index 0000000..1074010 --- /dev/null +++ b/app/routes/_layout._index.tsx @@ -0,0 +1,25 @@ +import { LoaderFunctionArgs, json } from '@remix-run/node' +import { useLoaderData } from '@remix-run/react' + +import { getRequestContextValues } from '~/lib/context.server' + +import { Container } from '~/components/Base' +import { PageErrorBoundary } from '~/components/ErrorBoundary' + +export async function loader({ request, context }: LoaderFunctionArgs) { + const { database } = getRequestContextValues(request, context) + + return json({}) +} + +export const ErrorBoundary = PageErrorBoundary + +export default function Index() { + const _data = useLoaderData() + + return ( + +

TBD

+
+ ) +} diff --git a/app/routes/_layout.about.tsx b/app/routes/_layout.about.tsx new file mode 100644 index 0000000..b9c317d --- /dev/null +++ b/app/routes/_layout.about.tsx @@ -0,0 +1,93 @@ +import clsx from 'clsx' + +import { AppMetaFunction, mergeMeta } from '~/lib/remix' + +import { A, Container, Heading, Link, Typography } from '~/components/Base' +import config from '~/config' + +export const meta: AppMetaFunction = ({ matches }) => { + return mergeMeta(matches, [ + { title: `About | ${config.siteTitle}` }, + { + name: 'description', + content: `Everything you need to know about ${config.siteTitle}`, + }, + ]) +} + +const emailHref = 'mailto:eligundry+album-mode.party@gmail.com' + +export default function About() { + return ( + +
+ About + + Have you ever headed to your Spotify Discover page and found that all + it's recommendations are albums that you've already heard or ones that + you aren't interested in? Are you just so tired of hunting for new + music that you fallback on a reliable favorite? Wouldn't it be nice if + you were presented a single album and just listened to it, just like + back in the days of record stores? + + + This has been my reality for the past few years. I listen to too much + music that I've burned through anything good that Spotify could + recommend to me. Spotify thinks I want more of what I listen to, but, + in reality, I want music recommendations that I don't know that I'd + want or even like. + + + This is why I built album-mode.party. It allows me + to get a random album based upon a search query (like genre or artist) + or a random highly rated album from a variety of publications. Since + I've started using it, the amount of new music I have discovered has + shot through the roof. + + + Please enjoy and{' '} + + let me know if there is anything you would like added + + ! + + Privacy + + I take your privacy extremely seriously. I use Google Analytics + + Google Tag Manager only to collect metrics on if you like my + recommendations or not. If you login with Spotify, I save your access + token to your cookies locally and will sync the albums that you like + to the Cloud™️. I do this so that you can sync saved albums across + devices and doing this peer to peer is way harder than it should be. + + + + You are not, nor will you ever be, my funding source for this + application. + + + + If you have any questions about your privacy on this site,{' '} + please contact me! + + Thanks + + Thanks to{' '} + Paul DeCotiis{' '} + for help checking my design! + + Legal + + I do not own and am not associated with any publications listed on + this site. Their names and lists are owned by them and used under fair + use. I absolutely love all the publications listed and I hope that is + reciprocated or at least tolerated. + + + If any publication would like to be scrubbed from this site,{' '} + reach out and we can talk about it. + +
+
+ ) +} diff --git a/app/routes/genre.$genre.tsx b/app/routes/_layout.genre.$genre.tsx similarity index 93% rename from app/routes/genre.$genre.tsx rename to app/routes/_layout.genre.$genre.tsx index fc9102e..5d6a468 100644 --- a/app/routes/genre.$genre.tsx +++ b/app/routes/_layout.genre.$genre.tsx @@ -12,7 +12,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, params, context }: LoaderFunctionArgs) { @@ -90,9 +89,5 @@ export default function GenreSearch() { return null } - return ( - - - - ) + return } diff --git a/app/routes/genres.tsx b/app/routes/_layout.genres.tsx similarity index 62% rename from app/routes/genres.tsx rename to app/routes/_layout.genres.tsx index 333c662..dad7abc 100644 --- a/app/routes/genres.tsx +++ b/app/routes/_layout.genres.tsx @@ -4,7 +4,7 @@ import clsx from 'clsx' import { getRequestContextValues } from '~/lib/context.server' -import { Container, Heading, Layout } from '~/components/Base' +import { Container, Heading } from '~/components/Base' import ButtonLinkGroup from '~/components/Base/ButtonLinkGroup' import { PageErrorBoundary } from '~/components/ErrorBoundary' import GenreSearchForm from '~/components/Forms/GenreSearch' @@ -22,18 +22,16 @@ export default function Genres() { const data = useLoaderData() return ( - - - Search by Genre - - `/genre/${genre}`} - keyFunction={(genre) => genre} - childFunction={(genre) => genre} - wrapperClassName={clsx('mt-4')} - /> - - + + Search by Genre + + `/genre/${genre}`} + keyFunction={(genre) => genre} + childFunction={(genre) => genre} + wrapperClassName={clsx('mt-4')} + /> + ) } diff --git a/app/routes/_layout.help.tsx b/app/routes/_layout.help.tsx new file mode 100644 index 0000000..18bf0e1 --- /dev/null +++ b/app/routes/_layout.help.tsx @@ -0,0 +1,37 @@ +import { AppMetaFunction, mergeMeta } from '~/lib/remix' + +import { Container, Heading } from '~/components/Base' +import FAQ from '~/components/FAQ' +import config from '~/config' + +export const meta: AppMetaFunction = ({ matches }) => + mergeMeta(matches, [ + { title: `Help | ${config.siteTitle}` }, + { + name: 'description', + content: `Answers to commonly asked questions for ${config.siteTitle}`, + }, + ]) + +export default function Help() { + return ( + + Help + +

+ This is happening because you are not logged into Spotify in the + browser. You have a few options to fix this: +

+
    +
  1. Press the Play button to open in the native player.
  2. +
  3. Login into Spotify in the browser.
  4. +
+ + } + /> +
+ ) +} diff --git a/app/routes/label.$slug.tsx b/app/routes/_layout.label.$slug.tsx similarity index 86% rename from app/routes/label.$slug.tsx rename to app/routes/_layout.label.$slug.tsx index a66b3c5..13605d1 100644 --- a/app/routes/label.$slug.tsx +++ b/app/routes/_layout.label.$slug.tsx @@ -8,7 +8,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' export async function loader({ params, request, context }: LoaderFunctionArgs) { const { logger } = getRequestContextValues(request, context) @@ -37,9 +36,5 @@ export const ErrorBoundary = AlbumErrorBoundary export default function LabelBySlug() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/label.tsx b/app/routes/_layout.label.tsx similarity index 86% rename from app/routes/label.tsx rename to app/routes/_layout.label.tsx index 96d46a0..80282db 100644 --- a/app/routes/label.tsx +++ b/app/routes/_layout.label.tsx @@ -8,7 +8,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' export async function loader({ request, context }: LoaderFunctionArgs) { const { logger } = getRequestContextValues(request, context) @@ -41,9 +40,5 @@ export const ErrorBoundary = AlbumErrorBoundary export default function LabelSearch() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/labs.tsx b/app/routes/_layout.labs.tsx similarity index 50% rename from app/routes/labs.tsx rename to app/routes/_layout.labs.tsx index aa23564..2103ccf 100644 --- a/app/routes/labs.tsx +++ b/app/routes/_layout.labs.tsx @@ -5,7 +5,7 @@ import clsx from 'clsx' import { getRequestContextValues } from '~/lib/context.server' import { AppMetaFunction, mergeMeta } from '~/lib/remix' -import { Container, Heading, Layout, Typography } from '~/components/Base' +import { Container, Heading, Typography } from '~/components/Base' import ButtonLinkGroup from '~/components/Base/ButtonLinkGroup' import HomeSection from '~/components/Base/HomeSection' import { PageErrorBoundary } from '~/components/ErrorBoundary' @@ -35,35 +35,33 @@ export default function LibraryPage() { const data = useLoaderData() return ( - - - - Labs 🧪 - - - These features are not ready for prime time (and may never be), but I - think they are cool to see. - - - username} - childFunction={(username) => `@${username}`} - toFunction={(username) => `/twitter/${username}`} - /> - - - - - - + + + Labs 🧪 + + + These features are not ready for prime time (and may never be), but I + think they are cool to see. + + + username} + childFunction={(username) => `@${username}`} + toFunction={(username) => `/twitter/${username}`} + /> + + + + + ) } diff --git a/app/routes/_layout.library.tsx b/app/routes/_layout.library.tsx new file mode 100644 index 0000000..9ae1efb --- /dev/null +++ b/app/routes/_layout.library.tsx @@ -0,0 +1,56 @@ +import clsx from 'clsx' + +import { AppMetaFunction, mergeMeta } from '~/lib/remix' + +import { Container, Heading, Typography } from '~/components/Base' +import { PageErrorBoundary } from '~/components/ErrorBoundary' +import SettingsForm from '~/components/Forms/Settings' +import Library from '~/components/Library' +import config from '~/config' +import useUser from '~/hooks/useUser' + +export const ErrorBoundary = PageErrorBoundary +export const meta: AppMetaFunction = ({ matches }) => + mergeMeta(matches, [ + { title: `Library | ${config.siteTitle}` }, + { + name: 'description', + content: 'Albums that you like are saved here.', + }, + ]) + +export default function LibraryPage() { + const user = useUser() + + return ( + + + Library + + {!user ? ( + + These items are saved to your browser's local storage. + + ) : ( + + These items are synced to the cloud because you are logged in with + Spotify. Logging in on another device to this site will sync your + library to it. + + )} + {user && ( + <> + + Settings + + + These settings allow Album Mode.party to improve the quality of your + Spotify recommendations as you use the application. + + + + )} + + + ) +} diff --git a/app/routes/publication.$slug.tsx b/app/routes/_layout.publication.$slug.tsx similarity index 98% rename from app/routes/publication.$slug.tsx rename to app/routes/_layout.publication.$slug.tsx index 060347f..496c293 100644 --- a/app/routes/publication.$slug.tsx +++ b/app/routes/_layout.publication.$slug.tsx @@ -12,7 +12,7 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import BandcampAlbum from '~/components/Album/Bandcamp' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { A, Heading, Layout } from '~/components/Base' +import { A, Heading } from '~/components/Base' import { SearchBreadcrumbsProps } from '~/components/SearchBreadcrumbs' import config from '~/config' import useUTM from '~/hooks/useUTM' @@ -212,7 +212,7 @@ export default function PublicationBySlug() { } return ( - + <> {data.type === 'spotify' && ( )} @@ -229,6 +229,6 @@ export default function PublicationBySlug() { footer={footer} /> )} - + ) } diff --git a/app/routes/spotify.artist-id.$artistID.tsx b/app/routes/_layout.spotify.artist-id.$artistID.tsx similarity index 94% rename from app/routes/spotify.artist-id.$artistID.tsx rename to app/routes/_layout.spotify.artist-id.$artistID.tsx index c4692ad..5ea1021 100644 --- a/app/routes/spotify.artist-id.$artistID.tsx +++ b/app/routes/_layout.spotify.artist-id.$artistID.tsx @@ -16,7 +16,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, params, context }: LoaderFunctionArgs) { @@ -110,9 +109,5 @@ export const meta: AppMetaFunction = ({ data, matches }) => { export default function RelatedArtistSearch() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.artist.$artist.tsx b/app/routes/_layout.spotify.artist.$artist.tsx similarity index 93% rename from app/routes/spotify.artist.$artist.tsx rename to app/routes/_layout.spotify.artist.$artist.tsx index 58b5bfd..6f4832b 100644 --- a/app/routes/spotify.artist.$artist.tsx +++ b/app/routes/_layout.spotify.artist.$artist.tsx @@ -14,7 +14,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, params, context }: LoaderFunctionArgs) { @@ -103,9 +102,5 @@ export const meta: AppMetaFunction = ({ data, matches }) => { export default function RelatedArtistSearch() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.categories.tsx b/app/routes/_layout.spotify.categories.tsx similarity index 54% rename from app/routes/spotify.categories.tsx rename to app/routes/_layout.spotify.categories.tsx index 5216b02..30b97db 100644 --- a/app/routes/spotify.categories.tsx +++ b/app/routes/_layout.spotify.categories.tsx @@ -7,23 +7,27 @@ import { AppMetaFunction, mergeMeta } from '~/lib/remix' import { forwardServerTimingHeaders } from '~/lib/responses.server' import spotifyLib from '~/lib/spotify.server' -import { Container, Heading, Layout } from '~/components/Base' +import { Container, Heading } from '~/components/Base' import { CardLink } from '~/components/Base/Card' import { PageErrorBoundary } from '~/components/ErrorBoundary' import config from '~/config' export async function loader({ request, context }: LoaderFunctionArgs) { const { serverTiming } = getRequestContextValues(request, context) - const headers = new Headers({ - 'Cache-Control': config.cacheControl.public, - }) const spotify = await serverTiming.track('spotify.init', () => spotifyLib.initializeFromRequest(request, context), ) const categories = await spotify.getCategories() - headers.set(serverTiming.headerKey, serverTiming.toString()) - return json({ categories }, { headers }) + return json( + { categories }, + { + headers: { + [serverTiming.headerKey]: serverTiming.toString(), + 'Cache-Control': config.cacheControl.private, + }, + }, + ) } export const ErrorBoundary = PageErrorBoundary @@ -41,31 +45,29 @@ export default function SpotifyCategories() { const { categories } = useLoaderData() return ( - - - Playlist Categories -
- {categories.map((category) => ( - - } - title={category.name} - /> - ))} -
-
-
+ + Playlist Categories +
+ {categories.map((category) => ( + + } + title={category.name} + /> + ))} +
+
) } diff --git a/app/routes/spotify.category.$id.tsx b/app/routes/_layout.spotify.category.$id.tsx similarity index 82% rename from app/routes/spotify.category.$id.tsx rename to app/routes/_layout.spotify.category.$id.tsx index 3969865..1c77137 100644 --- a/app/routes/spotify.category.$id.tsx +++ b/app/routes/_layout.spotify.category.$id.tsx @@ -14,7 +14,6 @@ import userSettings from '~/lib/userSettings.server' import PlaylistErrorBoundary from '~/components/Album/ErrorBoundary' import Playlist from '~/components/Album/Playlist' -import { Layout, Link } from '~/components/Base' import config from '~/config' export async function loader({ params, request, context }: LoaderFunctionArgs) { @@ -71,23 +70,7 @@ export const meta: AppMetaFunction = ({ data, matches }) => { } export default function RandomSpotifyCategoryPlaylist() { - const { playlist, category } = useLoaderData() + const { playlist } = useLoaderData() - return ( - - Playlist Category - , - ], - category.name, - ]} - > - - - ) + return } diff --git a/app/routes/spotify.featured-playlist.tsx b/app/routes/_layout.spotify.featured-playlist.tsx similarity index 89% rename from app/routes/spotify.featured-playlist.tsx rename to app/routes/_layout.spotify.featured-playlist.tsx index 39375a3..367e65a 100644 --- a/app/routes/spotify.featured-playlist.tsx +++ b/app/routes/_layout.spotify.featured-playlist.tsx @@ -9,7 +9,6 @@ import userSettings from '~/lib/userSettings.server' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' import Playlist from '~/components/Album/Playlist' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, context }: LoaderFunctionArgs) { @@ -48,9 +47,5 @@ export const meta: AppMetaFunction = ({ matches }) => export default function RandomSpotifyFeaturedPlaylist() { const { playlist } = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.for-you.tsx b/app/routes/_layout.spotify.for-you.tsx similarity index 91% rename from app/routes/spotify.for-you.tsx rename to app/routes/_layout.spotify.for-you.tsx index 1d907e9..a30e3ec 100644 --- a/app/routes/spotify.for-you.tsx +++ b/app/routes/_layout.spotify.for-you.tsx @@ -11,7 +11,6 @@ import userSettings from '~/lib/userSettings.server' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' import Playlist from '~/components/Album/Playlist' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, context }: LoaderFunctionArgs) { @@ -63,9 +62,5 @@ export const meta: AppMetaFunction = ({ matches }) => export default function RandomAlbumFromSpotifyLibrary() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.library.tsx b/app/routes/_layout.spotify.library.tsx similarity index 91% rename from app/routes/spotify.library.tsx rename to app/routes/_layout.spotify.library.tsx index 6113e47..9c537b8 100644 --- a/app/routes/spotify.library.tsx +++ b/app/routes/_layout.spotify.library.tsx @@ -12,7 +12,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, context }: LoaderFunctionArgs) { @@ -67,9 +66,5 @@ export const meta: AppMetaFunction = ({ matches }) => export default function RandomAlbumFromSpotifyLibrary() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.new-releases.tsx b/app/routes/_layout.spotify.new-releases.tsx similarity index 91% rename from app/routes/spotify.new-releases.tsx rename to app/routes/_layout.spotify.new-releases.tsx index 1387a78..2c962af 100644 --- a/app/routes/spotify.new-releases.tsx +++ b/app/routes/_layout.spotify.new-releases.tsx @@ -11,7 +11,6 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout } from '~/components/Base' import config from '~/config' export async function loader({ request, context }: LoaderFunctionArgs) { @@ -66,9 +65,5 @@ export const meta: AppMetaFunction = ({ matches }) => export default function SpotifyNewReleases() { const data = useLoaderData() - return ( - - - - ) + return } diff --git a/app/routes/spotify.top-artists-relations.tsx b/app/routes/_layout.spotify.top-artists-relations.tsx similarity index 93% rename from app/routes/spotify.top-artists-relations.tsx rename to app/routes/_layout.spotify.top-artists-relations.tsx index 11a64f1..b10d12e 100644 --- a/app/routes/spotify.top-artists-relations.tsx +++ b/app/routes/_layout.spotify.top-artists-relations.tsx @@ -12,7 +12,7 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout, Link } from '~/components/Base' +import { Link } from '~/components/Base' import type { SearchBreadcrumbsProps } from '~/components/SearchBreadcrumbs' import config from '~/config' @@ -86,9 +86,5 @@ export default function RandomAlbumFromRelatedTopArtistOnSpotify() { ], ] - return ( - - - - ) + return } diff --git a/app/routes/spotify.top-artists.tsx b/app/routes/_layout.spotify.top-artists.tsx similarity index 93% rename from app/routes/spotify.top-artists.tsx rename to app/routes/_layout.spotify.top-artists.tsx index 7cabd46..613095a 100644 --- a/app/routes/spotify.top-artists.tsx +++ b/app/routes/_layout.spotify.top-artists.tsx @@ -12,7 +12,7 @@ import wikipedia from '~/lib/wikipedia.server' import Album from '~/components/Album' import AlbumErrorBoundary from '~/components/Album/ErrorBoundary' -import { Layout, Link } from '~/components/Base' +import { Link } from '~/components/Base' import type { SearchBreadcrumbsProps } from '~/components/SearchBreadcrumbs' import config from '~/config' @@ -80,9 +80,5 @@ export default function RandomAlbumFromTopArtistOnSpotify() { ], ] - return ( - - - - ) + return } diff --git a/app/routes/_layout.tsx b/app/routes/_layout.tsx new file mode 100644 index 0000000..99c8c72 --- /dev/null +++ b/app/routes/_layout.tsx @@ -0,0 +1,256 @@ +import { LoaderFunctionArgs, json } from '@remix-run/node' +import { Outlet, useLoaderData } from '@remix-run/react' +import React, { useMemo } from 'react' +import { ClientOnly } from 'remix-utils/client-only' + +import { getRequestContextValues } from '~/lib/context.server' +import { cn } from '~/lib/util' + +import AutoAlert from '~/components/AutoAlert' +import { A, ButtonLink, Container, EmojiText, Link } from '~/components/Base' +import { ButtonLinkGroupWrapper } from '~/components/Base/ButtonLinkGroup' +import HomeSection from '~/components/Base/HomeSection' +import SuperHeaderSearch from '~/components/Forms/SuperHeaderSearch' +import { DesktopLoader, MobileLoader } from '~/components/Loading' +import config from '~/config' +import useLoading from '~/hooks/useLoading' +import { useIsMobile } from '~/hooks/useMediaQuery' +import useSavedSearches from '~/hooks/useSavedSearches' +import useUser from '~/hooks/useUser' + +export async function loader({ request, context }: LoaderFunctionArgs) { + const { database } = getRequestContextValues(request, context) + + return json( + { publications: await database.getPublications() }, + { + headers: { + 'Cache-Control': config.cacheControl.public, + }, + }, + ) +} + +const Layout: React.FC = () => { + const { loading } = useLoading() + const isMobile = useIsMobile() + + return ( + <> + +
+ +
+ + {() => isMobile && } + + ) +} + +function useNavSections() { + const user = useUser() + const { publications } = useLoaderData() + const { searches: savedSearches } = useSavedSearches() + + return useMemo(() => { + const sections = { + spotify: [ + { + label: 'New Releases', + to: '/spotify/new-releases', + }, + { + label: 'Featured Playlist', + to: '/spotify/featured-playlist', + }, + { + label: 'Playlist Categories', + to: '/spotify/categories', + }, + ], + publications: publications.map((publication) => ({ + label: publication.name, + to: `/publication/${publication.slug}`, + })), + savedSearches, + } + + if (user) { + sections.spotify.unshift( + { + label: 'Currently Playing', + to: '/spotify/currently-playing', + }, + { + label: 'Top Artists', + to: '/spotify/top-artists', + }, + { + label: 'Top Artists Relations', + to: '/spotify/top-artists-relations', + }, + { + label: 'For You', + to: '/spotify/for-you', + }, + ) + } else { + sections.spotify.unshift({ + label: 'Spotify Login', + to: '', + }) + } + + return sections + }, [user, publications, savedSearches]) +} + +const DesktopHeader: React.FC = () => { + const navSections = useNavSections() + + return ( +
+ + +
+
+ + + +
+ +
+

+ + + Album Mode.party{' '} + + +

+ +
+
+ ) +} + +export default Layout diff --git a/app/routes/twitter.$username.tsx b/app/routes/_layout.twitter.$username.tsx similarity index 89% rename from app/routes/twitter.$username.tsx rename to app/routes/_layout.twitter.$username.tsx index cf10fa0..21cc28f 100644 --- a/app/routes/twitter.$username.tsx +++ b/app/routes/_layout.twitter.$username.tsx @@ -158,26 +158,5 @@ export default function AlbumFromTwitter() { } } - return ( - - @{data.tweet.publicationName} - , - ], - ]} - > - {album ? album : } - - ) + return album ? album : } diff --git a/app/routes/about.tsx b/app/routes/about.tsx deleted file mode 100644 index 769df0b..0000000 --- a/app/routes/about.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import clsx from 'clsx' - -import { AppMetaFunction, mergeMeta } from '~/lib/remix' - -import { - A, - Container, - Heading, - Layout, - Link, - Typography, -} from '~/components/Base' -import config from '~/config' - -export const meta: AppMetaFunction = ({ matches }) => { - return mergeMeta(matches, [ - { title: `About | ${config.siteTitle}` }, - { - name: 'description', - content: `Everything you need to know about ${config.siteTitle}`, - }, - ]) -} - -const emailHref = 'mailto:eligundry+album-mode.party@gmail.com' - -export default function About() { - return ( - - -
- About - - Have you ever headed to your Spotify Discover page and found that - all it's recommendations are albums that you've already heard or - ones that you aren't interested in? Are you just so tired of hunting - for new music that you fallback on a reliable favorite? Wouldn't it - be nice if you were presented a single album and just listened to - it, just like back in the days of record stores? - - - This has been my reality for the past few years. I listen to too - much music that I've burned through anything good that Spotify could - recommend to me. Spotify thinks I want more of what I listen to, - but, in reality, I want music recommendations that I don't know that - I'd want or even like. - - - This is why I built album-mode.party. It allows - me to get a random album based upon a search query (like genre or - artist) or a random highly rated album from a variety of - publications. Since I've started using it, the amount of new music I - have discovered has shot through the roof. - - - Please enjoy and{' '} - - let me know if there is anything you would like added - - ! - - Privacy - - I take your privacy extremely seriously. I use Google Analytics + - Google Tag Manager only to collect metrics on if you like my - recommendations or not. If you login with Spotify, I save your - access token to your cookies locally and will sync the albums that - you like to the Cloud™️. I do this so that you can sync saved albums - across devices and doing this peer to peer is way harder than it - should be. - - - - You are not, nor will you ever be, my funding source for this - application. - - - - If you have any questions about your privacy on this site,{' '} - please contact me! - - Thanks - - Thanks to{' '} - - Paul DeCotiis - {' '} - for help checking my design! - - Legal - - I do not own and am not associated with any publications listed on - this site. Their names and lists are owned by them and used under - fair use. I absolutely love all the publications listed and I hope - that is reciprocated or at least tolerated. - - - If any publication would like to be scrubbed from this site,{' '} - reach out and we can talk about it. - -
-
-
- ) -} diff --git a/app/routes/help.tsx b/app/routes/help.tsx deleted file mode 100644 index 121b1ce..0000000 --- a/app/routes/help.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { AppMetaFunction, mergeMeta } from '~/lib/remix' - -import { Container, Heading, Layout } from '~/components/Base' -import FAQ from '~/components/FAQ' -import config from '~/config' - -export const meta: AppMetaFunction = ({ matches }) => - mergeMeta(matches, [ - { title: `Help | ${config.siteTitle}` }, - { - name: 'description', - content: `Answers to commonly asked questions for ${config.siteTitle}`, - }, - ]) - -export default function Help() { - return ( - - - Help - -

- This is happening because you are not logged into Spotify in the - browser. You have a few options to fix this: -

-
    -
  1. Press the Play button to open in the native player.
  2. -
  3. Login into Spotify in the browser.
  4. -
- - } - /> -
-
- ) -} diff --git a/app/routes/library.tsx b/app/routes/library.tsx deleted file mode 100644 index c7e4ede..0000000 --- a/app/routes/library.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import clsx from 'clsx' - -import { AppMetaFunction, mergeMeta } from '~/lib/remix' - -import { Container, Heading, Layout, Typography } from '~/components/Base' -import { PageErrorBoundary } from '~/components/ErrorBoundary' -import SettingsForm from '~/components/Forms/Settings' -import Library from '~/components/Library' -import config from '~/config' -import useUser from '~/hooks/useUser' - -export const ErrorBoundary = PageErrorBoundary -export const meta: AppMetaFunction = ({ matches }) => - mergeMeta(matches, [ - { title: `Library | ${config.siteTitle}` }, - { - name: 'description', - content: 'Albums that you like are saved here.', - }, - ]) - -export default function LibraryPage() { - const user = useUser() - - return ( - - - - Library - - {!user ? ( - - These items are saved to your browser's local storage. - - ) : ( - - These items are synced to the cloud because you are logged in with - Spotify. Logging in on another device to this site will sync your - library to it. - - )} - {user && ( - <> - - Settings - - - These settings allow Album Mode.party to improve the quality of - your Spotify recommendations as you use the application. - - - - )} - - - - ) -}