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 | 1x 1x 1x 1x 4x 4x 4x 4x 4x 3x 3x 3x 3x 4x 4x 4x 4x 4x 1x 2x 2x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x 2x 1x 4x 4x 4x 4x 4x 4x 4x 4x 4x 4x | import type {CalEvent} from './ics'
import {keyOf} from './availability'
import {formatClock, parseClock, runningOrder, timeRange} from './gig'
import {SITE} from './rehearsal-events'
type GigSetlist = {
id: string
name: string
gigDate: Date | null
startTime: string | null
venue: string | null
notes: string | null
items: {
kind: 'SONG' | 'SET' | 'BREAK'
label: string | null
minutes: number | null
startTime: string | null
song: {seconds: number | null} | null
}[]
}
/**
* A gig (a setlist with a date) as a calendar event: from its start time to
* the end of its last set when the sets' lengths are known (otherwise the
* calendar's usual three hours), at its venue, with each set's times.
*/
export function gigEvent(
s: GigSetlist,
band: {name: string; timezone: string},
site: string = SITE,
): CalEvent | null {
if (!s.gigDate) return null
const {sets} = runningOrder(s.startTime, s.items)
const start = parseClock(s.startTime)
// Sets with a planned length are the gig; an "Extras (if needed)" set
// without one shouldn't stretch it
const planned = sets.some((x) => x.minutes != null)
? sets.filter((x) => x.minutes != null)
: sets
const end = [...planned].reverse().find((x) => x.end != null)?.end ?? null
const hhmm = (m: number) =>
`${String(Math.floor(m / 60) % 24).padStart(2, '0')}:${String(m % 60).padStart(2, '0')}`
// "14:00–17:20" reads as a start and an end; a start alone gets 3 hours
const time =
start == null
? null
: end != null
? `${hhmm(start)}–${hhmm(end)}`
: hhmm(start)
const setTimes = planned
.filter((x) => x.label && x.start != null)
.map((x) => `${x.label} ${timeRange(x.start, x.end)}`)
return {
uid: `gig-${s.id}@bandstand`,
date: keyOf(s.gigDate),
time,
title: `${band.name}: ${s.name}`,
location: s.venue,
description: [
s.startTime
? `Starts ${formatClock(Number(s.startTime.slice(0, 2)) * 60 + Number(s.startTime.slice(3, 5)), true)}`
: null,
setTimes.length ? setTimes.join('\n') : null,
s.notes,
`Setlist: ${site}/setlists/${s.id}`,
]
.filter(Boolean)
.join('\n'),
url: `${site}/setlists/${s.id}`,
tz: band.timezone,
}
}
|