znamenica/dneslov

View on GitHub
app/components/EventSpans.jsx

Summary

Maintainability
B
6 hrs
Test Coverage
import { Component } from 'react'
import {julianEaster, orthodoxEaster} from 'date-easter'

import EventSpan from 'EventSpan'
import { yeardateToMs } from 'support'

export default class EventSpans extends Component {
   static defaultProps = {
      msDate: null,
      calendarStyle: 'julian',
      defaultCalendarySlug: null,
      specifiedCalendarySlug: null,
      describedMemoIds: [],
      events: [],
      slug: null,
   }

   static staticKindCodes = [
      '',
      'Prophecy',
      'Genum',
      'Conception',
      'Nativity',
      'Circumcision',
      'Meeting',
      'Baptism',
      'Marriage',
      'Conceiving',
      'Assurance',
      'Transfiguration',
      'Revival',
      'Entrance',
      'Curse',
      'Preach',
      'Betraial',
      'Supper',
      'Passion',
      'Repose',
      'Rest',
      'Veneration',
      'Placing',
      'Resurrection',
      'Learning',
      'Writing',
      'Appearance',
      'Exaltation',
      'Ascension',
      'Uncovering',
      'Translation',
      'Miracle',
      'Protection',
      'Renunciation',
      'Adoration',
      'Restoration',
      'Sanctification',
      'Council',
   ]

   static getDerivedStateFromProps(props, state) {
      console.debug("[getDerivedStateFromProps] <<<", props, state)

      if (props !== state.prevProps) {
         return({
            events: EventSpans.sortEvents(props),
            prevProps: props
         })
      } else {
         return null
      }
   }

   static calculateNearbyDate(eventsIn, msDate) {
      console.debug("[calculateNearbyDate] <<<", eventsIn, msDate, new Date(msDate || Date.now()))

      let distances =
         eventsIn.map((event, index) => {
            return [event.date - msDate, index]
         }),
         indexNearbyIn = distances.filter(([dist, _]) => dist >= 0).sort(([dist1, _i], [dist2, _j]) => dist1 > dist2)[0] || [],
         indexNearby = indexNearbyIn[1]

      return eventsIn.map((event, index) => {
         if (index == indexNearby) {
            event.active = true
         }

         return event
      })
   }

   static yearDateFor(event, calendarySlug) {
      let memo = event.memoes.find(m => m.calendary_slug == calendarySlug) || event.memoes[0]

      return memo?.year_date
   }

   static easterDate(yearIn, style) {
      let year = yearIn || Date.now().getFullYear(),
          easterIn = Date.at(Date.parse(style == 'julian' ? julianEaster(year) : orthodoxEaster(year)))

      return easterIn
   }

   static fixedYearForYearDate(yearDate, year) {
      return yearDate && yearDate.split('.').reverse().join("") >= "0901" ? year : year + 1
   }

   static dateFor(yearDate, msDate, style) {
      console.debug("[dateFor] <<<", yearDate, msDate, style)

      let dateIn = new Date(msDate || Date.now()),
          yearIn = dateIn.getFullYear(),
          m = yearDate && yearDate.match(/(?<sign>[+-])(?<indent>.*)|(?<yearM>.*)(?<divisor>[%<>~])(?<dateM>.*)/),
          date, year, datePre, gapIn, gap, mul

      if (!m) {
         year = this.fixedYearForYearDate(yearDate, yearIn)
         date = yearDate && new Date(Date.parse((yearDate.concat("." + year)).split('.').reverse().join('-')))
      } else if (m[1]) {
         mul = m[1] == '-' ? -1 : 1

         date = new Date(this.easterDate(yearIn, style).getTime() + mul * parseInt(m[2]) * 24 * 60 * 60 * 1000)
      } else if (m[3]) {
         year = this.fixedYearForYearDate(m[3], yearIn),
         datePre = new Date(Date.parse((m[3].concat("." + year)).split('.').reverse().join('-'))),
         gapIn = parseInt(m[5]) - datePre.getDay()

         switch (m[4]) {
            case '%':
               // '%' => (0..6), 01.01%0 = 01.01.1900=1/+6, %0=0/+0, %0=6/+1
               gap = gapIn < 0 && gapIn + 7 || gapIn
               break
            case '<':
               // '<' => (1..7), // 01.01%0 = 01.01.1900=1/+6, %0=0/+7, %0=6/+1
               gap = gapIn >= 0 && gapIn - 7 || gapIn
               break
            case '>':
               // '>' => (-7..-1), 01.01%0 = 01.01.1900=1/+6, %0=0/-7, %0=6/-6
               gap = gapIn <= 0 && gapIn + 7 || gapIn
               break
            case '~':
               // '~' => (-3..3), 01.01%0 = 01.01.1900=1/+6, %0=0/+0, %0=6/+1
               gap = gapIn < -3 && gapIn + 7 || gapIn > 3 && gapIn - 7 || gapIn
         }

         date = new Date(datePre - gap * 24 * 60 * 60 * 1000)
      }

      console.debug("[dateFor] >>>", date)

      return date
   }

   static sortEvents(props) {
      // TODO remove in favor of sort by flexdate
      let events = props.events.sort((x, y) => {
         let xi = this.staticKindCodes.indexOf(x.kind_code) || this.staticKindCodes.length + 1,
             yi = this.staticKindCodes.indexOf(y.kind_code) || this.staticKindCodes.
length + 1

         if (xi < yi) {
            return -1
         } else if (xi > yi) {
            return 1
         }
         return 0
      }).flat().map((event) => {
         event.date = this.dateFor(this.yearDateFor(event, props.defaultCalendarySlug), props.msDate, props.calendarStyle)

         console.debug("[sortEvents] ***** event", event)

         return event
      })

      return this.calculateNearbyDate(events, props.msDate)
   }

   // system
   state = {}

   componentDidMount() {
      this.collapsible = M.Collapsible.init(this.$collapsible, {})
   }

   componentWillUnmount() {
      this.collapsible.destroy()
   }

   // custom
   render() {
      console.log("[render] * props:", this.props, "state:", this.state)

      return (
         <div
            className='row'
            id='events-list'>
            <ul
               key='events-list'
               ref={e => this.$collapsible = e}
               className='collapsible collection'
               data-collapsible='expandable' >
               {this.state.events.filterMap((event) =>
                  <EventSpan
                     key={'event-' + event.id}
                     active={event.active}
                     specifiedCalendarySlug={this.props.specifiedCalendarySlug}
                     defaultCalendarySlug={this.props.defaultCalendarySlug}
                     describedMemoIds={this.props.describedMemoIds}
                     happenedAt={event.happened_at}
                     kindName={event.kind_name}
                     place={event.place}
                     id={event.id}
                     date={event.date?.toLocaleDateString('ru-RU')}
                     slug={this.props.slug}
                     titles={event.titles}
                     scripta={event.scripta} />)}</ul></div>)}}