All files / Bandstand/src/app layout.tsx

96.42% Statements 81/84
56.52% Branches 13/23
100% Functions 3/3
96.42% Lines 81/84

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 991x     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x     4x 4x 4x 4x   4x 4x   4x 1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x   1x 1x 1x   1x 1x 1x 1x 1x 1x   3x 3x 3x 3x 3x 2x 2x 2x 2x 1x 1x 2x 2x 1x 3x 3x 3x 3x   3x 3x 3x 3x   3x 3x     3x 3x 3x 3x 3x 3x 3x 3x   3x  
import '@/app/globals.css'
import {ReactNode} from 'react'
import type {Metadata, Viewport} from 'next'
import {Archivo, JetBrains_Mono} from 'next/font/google'
import {getServerSession} from 'next-auth'
import {authOptions} from '@/lib/auth'
import {prisma} from '@/lib/db'
import {brandForRequest, currentBand, iconUrl, PRODUCT} from '@/lib/band'
import {chatLabel} from '@/lib/band-fields'
import Providers from '@/components/Providers'
import AppShell, {type ShellBand} from '@/components/AppShell'
import {DEMO} from '@/lib/demo'
import {THEME_COLORS, themeScript} from '@/lib/theme'
 
const archivo = Archivo({subsets: ['latin'], variable: '--font-archivo'})
const jetbrains = JetBrains_Mono({
  subsets: ['latin'],
  weight: ['500', '700'],
  variable: '--font-jetbrains',
})
 
/** The band this page is shown as: the chosen band, else this address's. */
async function shellContext() {
  const session = await getServerSession(authOptions)
  const user = session?.user?.email
    ? await prisma.user.findUnique({where: {email: session.user.email}})
    : null
  const {band, bands} = user
    ? await currentBand(user.id)
    : {band: null, bands: []}
  const brand = band ? null : await brandForRequest()
  return {session, user, band, bands, brand}
}
 
export async function generateMetadata(): Promise<Metadata> {
  const {band, brand} = await shellContext()
  const appName = band?.appName ?? brand?.appName ?? PRODUCT
  const icon = band ? iconUrl(band) : (brand?.iconUrl ?? iconUrl(null))
  const forWhom = band?.name ?? brand?.band?.name
  return {
    title: {default: appName, template: `%s ยท ${appName}`},
    description: forWhom
      ? `Charts, setlists and rehearsals for ${forWhom}`
      : 'Charts, setlists and rehearsals for bands',
    applicationName: appName,
    appleWebApp: {capable: true, title: appName, statusBarStyle: 'black'},
    manifest: '/manifest.webmanifest',
    icons: {icon, apple: icon},
    // The public demo is a copy of one made-up band, not a place to land
    ...(DEMO ? {robots: {index: false, follow: false}} : {}),
  }
}
 
export const viewport: Viewport = {
  themeColor: THEME_COLORS.dark,
  width: 'device-width',
  initialScale: 1,
  viewportFit: 'cover',
}
 
export default async function RootLayout({children}: {children: ReactNode}) {
  const {session, user, band, bands, brand} = await shellContext()
  const shell: ShellBand = {
    appName: band?.appName ?? brand?.appName ?? PRODUCT,
    band: band
      ? {
          id: band.id,
          name: band.name,
          chat: band.chatUrl
            ? {url: band.chatUrl, label: chatLabel(band.chatUrl)}
            : null,
          isAdmin: band.isAdmin || Boolean(user?.isOwner),
        }
      : null,
    bands: bands.map((b) => ({id: b.id, name: b.name})),
    isOwner: Boolean(user?.isOwner),
  }
  return (
    // data-theme is set by themeScript before React hydrates
    <html
      lang="en"
      className={`${archivo.variable} ${jetbrains.variable}`}
      suppressHydrationWarning
    >
      <head>
        <script dangerouslySetInnerHTML={{__html: themeScript}} />
        {/* Before anything can save: see scripts/demo/demo.js */}
        {/* eslint-disable-next-line @next/next/no-sync-scripts */}
        {DEMO && <script src="/demo.js" />}
      </head>
      <body>
        <Providers session={session}>
          <AppShell ctx={shell}>{children}</AppShell>
        </Providers>
      </body>
    </html>
  )
}