72 lines
1.8 KiB
TypeScript
72 lines
1.8 KiB
TypeScript
import { slugify } from '@/utils/slugify'
|
|
|
|
export interface IcalEvent {
|
|
eventToken: string
|
|
title: string
|
|
dateTime: string
|
|
location?: string
|
|
description?: string
|
|
}
|
|
|
|
function escapeText(value: string): string {
|
|
return value
|
|
.replace(/\\/g, '\\\\')
|
|
.replace(/;/g, '\\;')
|
|
.replace(/,/g, '\\,')
|
|
.replace(/\n/g, '\\n')
|
|
}
|
|
|
|
function toUtcString(isoDateTime: string): string {
|
|
const d = new Date(isoDateTime)
|
|
const pad = (n: number) => String(n).padStart(2, '0')
|
|
return (
|
|
`${d.getUTCFullYear()}${pad(d.getUTCMonth() + 1)}${pad(d.getUTCDate())}` +
|
|
`T${pad(d.getUTCHours())}${pad(d.getUTCMinutes())}${pad(d.getUTCSeconds())}Z`
|
|
)
|
|
}
|
|
|
|
export function generateIcs(event: IcalEvent): string {
|
|
const lines: string[] = [
|
|
'BEGIN:VCALENDAR',
|
|
'VERSION:2.0',
|
|
'PRODID:-//fete//EN',
|
|
'BEGIN:VEVENT',
|
|
`UID:${event.eventToken}@fete`,
|
|
`DTSTAMP:${toUtcString(new Date().toISOString())}`,
|
|
`DTSTART:${toUtcString(event.dateTime)}`,
|
|
`SUMMARY:${escapeText(event.title)}`,
|
|
'SEQUENCE:0',
|
|
]
|
|
|
|
if (event.location) {
|
|
lines.push(`LOCATION:${escapeText(event.location)}`)
|
|
}
|
|
|
|
if (event.description) {
|
|
lines.push(`DESCRIPTION:${escapeText(event.description)}`)
|
|
}
|
|
|
|
lines.push('END:VEVENT', 'END:VCALENDAR', '')
|
|
|
|
return lines.join('\r\n')
|
|
}
|
|
|
|
export function useIcalDownload() {
|
|
function download(event: IcalEvent) {
|
|
const ics = generateIcs(event)
|
|
const blob = new Blob([ics], { type: 'text/calendar;charset=utf-8' })
|
|
const url = URL.createObjectURL(blob)
|
|
|
|
const filename = `${slugify(event.title) || 'event'}.ics`
|
|
const link = document.createElement('a')
|
|
link.href = url
|
|
link.download = filename
|
|
document.body.appendChild(link)
|
|
link.click()
|
|
document.body.removeChild(link)
|
|
URL.revokeObjectURL(url)
|
|
}
|
|
|
|
return { download }
|
|
}
|