import classNames from 'classnames'; import { Cycler, } from '../Cycler'; import { ICyclingLinkInternalDispatchProps, } from './ICyclingLinkInternalDispatchProps'; import { ICyclingLinkInternalOwnProps, } from './ICyclingLinkInternalOwnProps'; import { ICyclingLinkInternalState, } from './ICyclingLinkInternalState'; import { ICyclingLinkInternalStateProps, } from './ICyclingLinkInternalStateProps'; import { IPassage, } from '../../passages/IPassage'; import { IState, } from '../../state/IState'; import { IStoryStateSetter, } from '../../interfaces/IStoryStateSetter'; import { mutateCurrentStoryStateInstanceWithPluginExecution, } from '../../state/mutateCurrentStoryStateInstanceWithPluginExecution'; import { connect, MapDispatchToProps, MapStateToProps, } from 'react-redux'; import { assertValid, } from 'ts-assertions'; import * as React from 'react'; export const strings = { FIRST_STATE_EMPTY: 'The first state was not provided to the CyclingLinkInternal component, ' + 'or it was an empty string.', PASSAGE_NOT_FOUND: 'No passage could be found in the passages map with the name %NAME%.', }; export class CyclingLinkInternal extends React.PureComponent< ICyclingLinkInternalOwnProps & ICyclingLinkInternalStateProps & ICyclingLinkInternalDispatchProps, ICyclingLinkInternalState > { public readonly state = { startIndex: 0 }; constructor( props: ICyclingLinkInternalOwnProps & ICyclingLinkInternalStateProps & ICyclingLinkInternalDispatchProps ) { super(props); const { callback, children, history: { present: { storyState }, }, dontCallbackOnMount, dontSetVariableOnMount, variableToSet, } = props; if (!dontSetVariableOnMount && variableToSet && typeof variableToSet === 'string') { if (typeof storyState[variableToSet] !== 'undefined') { const index = children.indexOf(storyState[variableToSet]); this.state = { startIndex: index === -1 ? 0 : index }; } const firstState = assertValid( children[this.state.startIndex], strings.FIRST_STATE_EMPTY, ); /* If this is not guarded by whether the story state already contains * this value, it will re-render to the React limit and then break. */ if (storyState[variableToSet] !== firstState) { this.setStoryState({ [variableToSet]: firstState }); } if (!dontCallbackOnMount && typeof callback === 'function') { callback(firstState); } } }; public render = () => ( {this.props.children} ); private doCallback = (current: string) => { const { callback, variableToSet, } = this.props; if (variableToSet && typeof variableToSet === 'string') { this.setStoryState({ [variableToSet]: current }); } if (typeof callback === 'function') { callback(current); } }; private setStoryState: IStoryStateSetter = (updatedStateProps) => { const { dispatch, history, history: { present: { passageName }, }, passagesMap, plugins, } = this.props; const passageObject = assertValid( passagesMap[passageName], strings.PASSAGE_NOT_FOUND.replace('%NAME%', passageName), ); mutateCurrentStoryStateInstanceWithPluginExecution({ dispatch, history, passageObject, plugins, updatedStateProps, }); } } export const mapStateToProps: MapStateToProps< ICyclingLinkInternalStateProps, ICyclingLinkInternalOwnProps, IState > = ({ history }) => ({ history }); export const mapDispatchToProps: MapDispatchToProps< ICyclingLinkInternalDispatchProps, ICyclingLinkInternalOwnProps > = (dispatch) => ({ dispatch }); export const CyclingLinkInternalConnected = connect( mapStateToProps, mapDispatchToProps, )(CyclingLinkInternal);