mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
276 lines
6.3 KiB
JavaScript
Executable File
276 lines
6.3 KiB
JavaScript
Executable File
import React, { useState, useEffect } from "react"
|
|
import { Skeleton, Button, Popover } from "antd"
|
|
import ReactMarkdown from "react-markdown"
|
|
import remarkGfm from "remark-gfm"
|
|
import { DateTime } from "luxon"
|
|
|
|
import { Icons } from "@components/Icons"
|
|
|
|
import EventsModel from "@models/events"
|
|
import useCenteredContainer from "@hooks/useCenteredContainer"
|
|
|
|
import createGoogleCalendarEvent from "@utils/createGoogleCalendarEvent"
|
|
import ContrastYIQ from "@utils/contrastYIQ"
|
|
import ProcessString from "@utils/processString"
|
|
|
|
import "./index.less"
|
|
|
|
const LocationProcessRegexs = [
|
|
{
|
|
regex: /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
|
|
fn: (key, result) => (
|
|
<a
|
|
key={key}
|
|
href={result[1]}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
>
|
|
{result[1]}
|
|
</a>
|
|
),
|
|
},
|
|
]
|
|
|
|
const EventCountdown = ({ date, prefix }) => {
|
|
const [label, setLabel] = useState(null)
|
|
|
|
useEffect(() => {
|
|
function updateCountdown() {
|
|
const nowDate = DateTime.local()
|
|
const fromDate = DateTime.fromISO(date)
|
|
const diff = fromDate.diff(nowDate, "minutes").values
|
|
setLabel(nowDate.plus(diff).toRelative())
|
|
}
|
|
|
|
updateCountdown()
|
|
const interval = setInterval(updateCountdown, 1000)
|
|
return () => clearInterval(interval)
|
|
}, [date])
|
|
|
|
return (
|
|
<div className="field">
|
|
<div className="field-label">
|
|
<Icons.FiClock />
|
|
<p>
|
|
{prefix} {label}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const EventStartDate = ({ startDate, started }) => (
|
|
<Popover
|
|
content={
|
|
<EventCountdown
|
|
date={startDate}
|
|
prefix={started ? "Started" : "Starts"}
|
|
/>
|
|
}
|
|
>
|
|
<div className="field">
|
|
<div className="field-label">
|
|
<Icons.FiCalendar />
|
|
{startDate.toLocaleString(DateTime.DATE_FULL)}
|
|
</div>
|
|
<div className="field-label">
|
|
<Icons.FiClock />
|
|
{startDate.toLocaleString(DateTime.TIME_SIMPLE)}
|
|
</div>
|
|
</div>
|
|
</Popover>
|
|
)
|
|
|
|
const EventHeader = ({ pageConfig, event, contrastColor }) => {
|
|
if (pageConfig.header) {
|
|
return (
|
|
<div
|
|
id="eventHeader"
|
|
className="header custom"
|
|
style={{
|
|
...(pageConfig.header.style ?? {}),
|
|
color: `var(--text-color-${contrastColor})`,
|
|
}}
|
|
>
|
|
{pageConfig.header.displayLogo && (
|
|
<div className="logo">
|
|
<img src={pageConfig.header.logoImg} alt="Event Logo" />
|
|
</div>
|
|
)}
|
|
{pageConfig.header.displayTitle && (
|
|
<div className="title">
|
|
<h1>{pageConfig.header.title}</h1>
|
|
<h2>{pageConfig.header.description}</h2>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div
|
|
className="header"
|
|
style={event.announcement.backgroundStyle}
|
|
id="eventHeader"
|
|
>
|
|
{event.announcement.logoImg && (
|
|
<div className="logo">
|
|
<img src={event.announcement.logoImg} alt="Event Logo" />
|
|
</div>
|
|
)}
|
|
<div className="title">
|
|
<h1>{event.name}</h1>
|
|
<h2>{event.announcement.description}</h2>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const EventPage = (props) => {
|
|
useCenteredContainer(false)
|
|
|
|
const [L_Event, R_Event, E_Event, M_Event] = app.cores.api.useRequest(
|
|
EventsModel.data,
|
|
props.params["id"],
|
|
)
|
|
const [contrastColor, setContrastColor] = useState(null)
|
|
const [started, setStarted] = useState(false)
|
|
const [ended, setEnded] = useState(false)
|
|
|
|
useEffect(() => {
|
|
if (!R_Event) return
|
|
|
|
// Calculate event status
|
|
const now = DateTime.local()
|
|
const eventStart = DateTime.fromISO(R_Event.startDate)
|
|
const eventEnd = DateTime.fromISO(R_Event.endDate)
|
|
|
|
const startDiff = eventStart.diff(now, "minutes").values
|
|
const endDiff = eventEnd.diff(now, "minutes").values
|
|
|
|
setStarted(startDiff.minutes < 0)
|
|
setEnded(endDiff.minutes < 0)
|
|
|
|
// Calculate contrast color for header
|
|
if (R_Event.pageConfig?.header?.style?.backgroundImage) {
|
|
const url = R_Event.pageConfig.header.style.backgroundImage
|
|
.replace("url(", "")
|
|
.replace(")", "")
|
|
.replace(/['"]/gi, "")
|
|
|
|
ContrastYIQ.fromUrl(url).then(setContrastColor)
|
|
}
|
|
}, [R_Event])
|
|
|
|
const handleClickWatchLiveStream = () => {
|
|
if (!R_Event?.pageConfig?.livestreamId) return
|
|
app.location.push(`/tv/live/${R_Event.pageConfig.livestreamId}`)
|
|
}
|
|
|
|
const handleClickAddToCalendar = () => {
|
|
createGoogleCalendarEvent({
|
|
title: R_Event.name,
|
|
startDate: new Date(R_Event.startDate),
|
|
endDate: new Date(R_Event.endDate),
|
|
description: `${R_Event.shortDescription} - See details at ${location.href}`,
|
|
location: R_Event.location,
|
|
})
|
|
}
|
|
|
|
if (E_Event) {
|
|
return null
|
|
}
|
|
|
|
if (L_Event) {
|
|
return <Skeleton active />
|
|
}
|
|
|
|
if (!R_Event) {
|
|
return null
|
|
}
|
|
|
|
const eventStartedOrEnded = started || ended
|
|
const startDate = DateTime.fromISO(R_Event.startDate)
|
|
const endDate = DateTime.fromISO(R_Event.endDate)
|
|
const { pageConfig } = R_Event
|
|
|
|
return (
|
|
<div className="event">
|
|
<EventHeader
|
|
pageConfig={pageConfig}
|
|
event={R_Event}
|
|
contrastColor={contrastColor}
|
|
/>
|
|
|
|
<div className="content">
|
|
<div className="panel">
|
|
<div className="card">
|
|
{started && !ended && (
|
|
<div className="field">
|
|
<div className="field-label">
|
|
<div className="pulse_circle" />
|
|
<p>Started</p>
|
|
</div>
|
|
<div className="field-value">
|
|
<EventCountdown
|
|
date={endDate}
|
|
prefix="Ends"
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{!started && (
|
|
<EventStartDate
|
|
startDate={startDate}
|
|
started={started}
|
|
/>
|
|
)}
|
|
|
|
<div className="field">
|
|
<div className="field-label">
|
|
<Icons.FiMapPin />
|
|
{ProcessString(LocationProcessRegexs)(
|
|
R_Event.location,
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{!eventStartedOrEnded && (
|
|
<div className="card">
|
|
<Button onClick={handleClickAddToCalendar}>
|
|
<Icons.FiCalendar /> Add to Calendar
|
|
</Button>
|
|
</div>
|
|
)}
|
|
|
|
{started && pageConfig.livestreamId && (
|
|
<div className="card">
|
|
<Button
|
|
type="primary"
|
|
onClick={handleClickWatchLiveStream}
|
|
>
|
|
<Icons.FiPlay /> Watch Live
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<div className="panel">
|
|
<div className="card">
|
|
<div className="page-render">
|
|
<ReactMarkdown
|
|
remarkPlugins={[remarkGfm]}
|
|
children={R_Event.page}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default EventPage
|