sparkletown/sparkle

View on GitHub
src/utils/event.ts

Summary

Maintainability
A
0 mins
Test Coverage
import {
  addMinutes,
  areIntervalsOverlapping,
  differenceInMinutes,
  fromUnixTime,
  isAfter,
  isFuture,
  isWithinInterval,
} from "date-fns";

import { EVENT_STARTING_SOON_TIMEFRAME } from "settings";

import { WorldEvent } from "types/venues";

import { formatUtcSecondsRelativeToNow, getDayInterval } from "./time";

type EventStartTimeOptions = {
  event: WorldEvent;
  defaultStartTime?: Date;
};

type EventEndTimeOptions = {
  event: WorldEvent;
  defaultDuration?: number;
};

export const isEventLive = (event: WorldEvent) => {
  if (!event?.startUtcSeconds) {
    return false;
  }

  return isWithinInterval(Date.now(), getEventInterval(event));
};

export const isEventFuture = (event: WorldEvent) =>
  isFuture(fromUnixTime(event.startUtcSeconds));

export const isEventLater = (event: WorldEvent) =>
  isEventFuture(event) &&
  !isEventStartingSoon(event, EVENT_STARTING_SOON_TIMEFRAME);

export const isEventSoon = (event: WorldEvent) =>
  isEventFuture(event) &&
  isEventStartingSoon(event, EVENT_STARTING_SOON_TIMEFRAME);

export const isEventLiveOrFuture = (event: WorldEvent) =>
  isEventLive(event) || isEventFuture(event);

export const hasEventFinished = (event: WorldEvent) =>
  isAfter(Date.now(), eventEndTime({ event }));

export const eventStartTime = ({
  event,
  defaultStartTime,
}: EventStartTimeOptions) =>
  fromUnixTime(event?.startUtcSeconds ?? defaultStartTime);

export const eventEndTime = ({ event, defaultDuration }: EventEndTimeOptions) =>
  addMinutes(
    eventStartTime({ event }),
    event?.durationMinutes ?? defaultDuration
  );

export const isEventStartingSoon = (
  event: WorldEvent,
  rangeInMinutes: number | undefined = 60
) =>
  differenceInMinutes(eventStartTime({ event }), Date.now()) <= rangeInMinutes;

export const getEventInterval = (event: WorldEvent) => ({
  start: eventStartTime({ event, defaultStartTime: new Date() }),
  end: eventEndTime({ event, defaultDuration: 1 }),
});

export const isEventWithinDate = (checkDate: Date | number) => (
  event: WorldEvent
) =>
  areIntervalsOverlapping(getDayInterval(checkDate), getEventInterval(event));

export const isEventWithinDateAndNotFinished = (checkDate: Date | number) => (
  event: WorldEvent
) => isEventWithinDate(checkDate)(event) && !hasEventFinished(event);

export const getEventStatus = (event: WorldEvent) => {
  if (isEventLive(event)) return `Happening now`;

  if (hasEventFinished(event)) {
    return `Ended`;
  } else {
    return `Starts ${formatUtcSecondsRelativeToNow(event.startUtcSeconds)}`;
  }
};

export const eventsByStartUtcSecondsSorter = (a: WorldEvent, b: WorldEvent) =>
  a.startUtcSeconds - b.startUtcSeconds;

export const eventTimeComparator = (a: WorldEvent, b: WorldEvent) => {
  if (a.startUtcSeconds !== b.startUtcSeconds) {
    return eventsByStartUtcSecondsSorter(a, b);
  }

  return a.durationMinutes - b.durationMinutes;
};

export const eventTimeAndOrderComparator = (a: WorldEvent, b: WorldEvent) => {
  const aOrderPriority = a.orderPriority ?? 0;
  const bOrderPriority = b.orderPriority ?? 0;

  if (aOrderPriority === bOrderPriority) {
    return eventTimeComparator(a, b);
  } else {
    return bOrderPriority - aOrderPriority;
  }
};