Untitled

mail@pastecode.io avatar
unknown
typescript
3 years ago
3.6 kB
1
Indexable
Never
import React, { FC, ReactNode } from 'react';
import { observer } from 'mobx-react-lite';
import { getParent } from 'mobx-state-tree';

import { POIInstance } from 'models/POI.model';
import { useStore } from 'hooks/useStore';
import { StoreName } from 'stores/storeName';
import { ActiveSection } from 'types/Sidebar';
import type { StageType } from 'models/Stage.model';
import { useScrollToRef } from 'hooks/useScrolltoRef';
import { POIType } from 'types/POI';
import { ReactComponent as PodcastIcon } from 'svg/podcast.svg';
import { ReactComponent as CityLargeIcon } from 'svg/city-large.svg';
import { ReactComponent as CitySmallIcon } from 'svg/city-small.svg';
import { ReactComponent as StarIcon } from 'svg/star.svg';
import { ReactComponent as EuroregionIcon } from 'svg/euroregion.svg';

import { Wrapper } from './styles';
import { ActiveStage } from 'components/ActiveStage';

const POITypeToIconMapper: Record<POIType, ReactNode> = {
  [POIType.PODCAST]: <PodcastIcon />,
  [POIType.CITY_LARGE]: <CityLargeIcon />,
  [POIType.CITY_SMALL]: <CitySmallIcon />,
  [POIType.STAR]: <StarIcon />,
  [POIType.EUROREGION]: <EuroregionIcon />,
};

interface Props {
  stageId: string;
  POI: POIInstance;
}

export const Poi: FC<Props> = observer(({ POI }) => {
  const { setActivePOI, activePOI, setActiveSection } = useStore(
    StoreName.SIDEBAR,
  );
  const { activeStage, setActiveStage } = useStore(StoreName.MAP);
  const { isMobileUp } = useStore(StoreName.UI);
  const parentStage = getParent<StageType>(POI, 2);
  const coordinates = isMobileUp ? POI.coordinates : POI.coordinatesMobile;

  const scrollToRef = useScrollToRef();

  const now = new Date();

  const isPOIVisible =
    POI.scheduledAt === undefined ||
    (POI.scheduledAt instanceof Date && now > POI.scheduledAt) ||
    !!localStorage.getItem('showAllPOIs');

  if (!isPOIVisible || !coordinates) {
    return null;
  }

  return (
    <Wrapper
      isActive={POI === activePOI}
      type={POI.type}
      onClick={() => {
        setActivePOI(POI);
        setActiveSection(ActiveSection.POI);
        if (activeStage !== parentStage) {
          scrollToRef(parentStage.ref);
          setActiveStage(parentStage);
        }
      }}
      style={{
        top: `${coordinates.y}%`,
        left: `${coordinates.x}%`,
      }}
    >
      {POITypeToIconMapper[POI.type]}
    </Wrapper>
  );
});


// nested entities that are meaningful logic-wise
|-MapStore
  |-activeStage
  |-stages: Stage[]
  | |-POIs : POI[]
  | |...
  |...

// It is idiomatic for Redux to accept only raw/serializable data
// In the code: how do you express the relationship between nested Domain Objects 
// (as in logically meaningful objects as oposed to DDD strict meaning)
<Poi POI={POIData} />
  POIData {
    id: 'poiId'
  }
  // in the "within <POI />" you would have to reverse lookup the POI by it's ID and
  // then check whether it is nested within activeStageId and 

  // Another approach to take would be to pass the logic down from the point where you initially connect to Redux 
  <Poi onClick={onClick} POI={POIData}/>

  the on Click is defined when we are still in scope of Stage
  onPOIClick = (clickedPoi) => {

    const currentlyActiveStage = useSelector(getCurrentlyActiveStage);
    const didThePOIClickOccurInCurrentlyActiveStage = !!currentlyActiveStage.POIs.find(poi => poi.id === clickedPoi.id)

    if(didThePOIClickOccurInCurrentlyActiveStage) {
      ...
    } else {
      ...
    }
  }
  <Stage onPOIClick={onPOIClick} />

  // alternatively you could normalise the data thus adding an another layer of complexity
  // I.E normalizr or redux-orm