Untitled
unknown
typescript
5 years ago
3.6 kB
9
Indexable
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
Editor is loading...