Skip to content

Commit

Permalink
attempt to fix prompt issues by replacing withPrompts hoc with functi…
Browse files Browse the repository at this point in the history
…onal component and hook
  • Loading branch information
jthrilly committed Dec 11, 2023
1 parent 14fb145 commit 84235c1
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 141 deletions.
180 changes: 93 additions & 87 deletions lib/interviewer/behaviours/withPrompt.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import React, { Component, useEffect } from 'react';
import { connect, useDispatch, useSelector } from 'react-redux';
import { bindActionCreators } from 'redux';
import PropTypes from 'prop-types';
import { actionCreators as sessionActions } from '../ducks/modules/session';
import { getAllVariableUUIDsByEntity, getProtocolStages } from '../selectors/protocol';
import { get } from '../utils/lodash-replacements';
import { processProtocolSortRule } from '../utils/createSorter';
import { getPromptIndex } from '../selectors/session';
import { getIsFirstPrompt, getIsLastPrompt, getPromptIndex, getPrompts } from '../selectors/session';

/**
* Convert sort rules to new format. See `processProtocolSortRule` for details.
Expand All @@ -15,7 +15,7 @@ import { getPromptIndex } from '../selectors/session';
* @returns {Array}
* @private
*/
const processSortRules = (prompts, codebookVariables) => {
const processSortRules = (prompts = [], codebookVariables) => {
const sortProperties = ['bucketSortOrder', 'binSortOrder'];

return prompts.map((prompt) => {
Expand All @@ -31,97 +31,103 @@ const processSortRules = (prompts, codebookVariables) => {
});
};

export default function withPrompt(WrappedComponent) {
class WithPrompt extends Component {
get prompts() {
const {
codebookVariables,
} = this.props;

const prompts = get(this.props, ['stage', 'prompts'], []);
const processedPrompts = processSortRules(prompts, codebookVariables);
return processedPrompts;
}

get promptsCount() {
return this.prompts.length;
}

isFirstPrompt = () => {
const { promptIndex } = this.props;
return promptIndex === 0;
}

isLastPrompt = () => {
const { promptIndex } = this.props;
const lastPromptIndex = this.promptsCount - 1;
return promptIndex === lastPromptIndex;
}

promptForward = () => {
const { updatePrompt, promptIndex } = this.props;

updatePrompt(
(this.promptsCount + promptIndex + 1) % this.promptsCount,
);
}

promptBackward = () => {
const { updatePrompt, promptIndex } = this.props;

updatePrompt(
(this.promptsCount + promptIndex - 1) % this.promptsCount,
);
}

prompt() {
const { promptIndex } = this.props;

return get(this.prompts, promptIndex);
}

render() {
const { promptIndex, codebookVariables, ...rest } = this.props;

return (
<WrappedComponent
prompt={this.prompt()}
promptForward={this.promptForward}
promptBackward={this.promptBackward}
isLastPrompt={this.isLastPrompt}
isFirstPrompt={this.isFirstPrompt}
{...rest}
/>
);
}
}
const withPrompt = (WrappedComponent) => {
const WithPrompt = (props) => {
const dispatch = useDispatch();
const updatePrompt = (promptIndex) => dispatch(sessionActions.updatePrompt(promptIndex));

const codebookVariables = useSelector(getAllVariableUUIDsByEntity);
const prompts = useSelector(getPrompts);
const { ...rest } = props;

const processedPrompts = processSortRules(prompts, codebookVariables);

const isFirstPrompt = useSelector(getIsFirstPrompt);
const isLastPrompt = useSelector(getIsLastPrompt);
const promptIndex = useSelector(getPromptIndex);

const promptForward = () => {
updatePrompt((promptIndex + 1) % processedPrompts.length);
};

const promptBackward = () => {
updatePrompt((promptIndex - 1 + processedPrompts.length) % processedPrompts.length);
};

const prompt = () => {
console.log('calling prompt()', props, promptIndex);
return get(processedPrompts, promptIndex);
};

// useEffect(() => {
// let promptIndex = props.promptId;

// if (promptIndex === undefined) {
// promptIndex = getPromptIndex(state);
// }

// // Dispatch an action to update the promptIndex
// // This assumes you have an action to update the promptIndex in your sessionActions
// dispatch(sessionActions.updatePromptIndex(promptIndex));
// }, [props.promptId]);

return (
<WrappedComponent
prompt={prompt()}
promptForward={promptForward}
promptBackward={promptBackward}
isLastPrompt={isLastPrompt}
isFirstPrompt={isFirstPrompt}
{...rest}
/>
);
};

WithPrompt.propTypes = {
stage: PropTypes.object.isRequired,
promptIndex: PropTypes.number,
updatePrompt: PropTypes.func.isRequired,
promptId: PropTypes.number,
};

WithPrompt.defaultProps = {
promptIndex: 0,
return WithPrompt;
};

export const usePrompts = () => {
const dispatch = useDispatch();
const updatePrompt = (promptIndex) => dispatch(sessionActions.updatePrompt(promptIndex));

const codebookVariables = useSelector(getAllVariableUUIDsByEntity);
const prompts = useSelector(getPrompts);

const processedPrompts = processSortRules(prompts, codebookVariables);

const isFirstPrompt = useSelector(getIsFirstPrompt);
const isLastPrompt = useSelector(getIsLastPrompt);
const promptIndex = useSelector(getPromptIndex);

const promptForward = () => {
updatePrompt((promptIndex + 1) % processedPrompts.length);
};

function mapStateToProps(state, ownProps) {
let promptIndex = ownProps.promptId;
if (promptIndex === undefined) {
promptIndex = getPromptIndex(state);
}
return {
promptIndex,
stage: ownProps.stage || getProtocolStages(state)[ownProps.currentStep],
codebookVariables: getAllVariableUUIDsByEntity(state),
};
}
const promptBackward = () => {
updatePrompt((promptIndex - 1 + processedPrompts.length) % processedPrompts.length);
};

function mapDispatchToProps(dispatch) {
return {
updatePrompt: bindActionCreators(sessionActions.updatePrompt, dispatch),
};
}
const currentPrompt = () => {
return processedPrompts[promptIndex] ?? null;
};

return connect(mapStateToProps, mapDispatchToProps)(WithPrompt);
return {
promptIndex,
currentPrompt: currentPrompt(),
prompts,
promptForward,
promptBackward,
isLastPrompt,
isFirstPrompt,
};
}


export default withPrompt;
6 changes: 4 additions & 2 deletions lib/interviewer/components/Prompts.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React from 'react';
import UIPrompts from '~/lib/ui/components/Prompts/Prompts';
import { useSelector } from 'react-redux';
import { usePrompts } from '../behaviours/withPrompt';

const Prompts = (props) => {
const Prompts = () => {
const { currentPrompt, prompts } = usePrompts();
const speakable = useSelector((state) => state.deviceSettings.enableExperimentalTTS);

return <UIPrompts speakable={speakable} {...props} />;
return <UIPrompts speakable={speakable} currentPromptId={currentPrompt.id} prompts={prompts} />;
};

export default Prompts;
49 changes: 20 additions & 29 deletions lib/interviewer/containers/Interfaces/NameGenerator.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import { compose } from 'redux';
import { useDispatch, useSelector } from 'react-redux';
import PropTypes from 'prop-types';
import {
Expand All @@ -8,7 +7,7 @@ import {
import { createPortal } from 'react-dom';
import { entityAttributesProperty, entityPrimaryKeyProperty } from '@codaco/shared-consts';
import Prompts from '../../components/Prompts';
import withPrompt from '../../behaviours/withPrompt';
import { usePrompts } from '../../behaviours/withPrompt';
import { actionCreators as sessionActions } from '../../ducks/modules/session';
import { getStageNodeCount, getNetworkNodesForPrompt } from '../../selectors/interface';
import { getPromptModelData as getPromptNodeModelData, getNodeIconName } from '../../selectors/name-generator';
Expand All @@ -28,12 +27,11 @@ import { getAdditionalAttributesSelector } from '../../selectors/prop';
const NameGenerator = (props) => {
const {
registerBeforeNext,
prompt,
onComplete,
stage,
} = props;

const {
prompts,
form,
quickAdd,
behaviours,
Expand All @@ -42,6 +40,13 @@ const NameGenerator = (props) => {

const interfaceRef = useRef(null);

const {
currentPrompt,
isFirstPrompt,
isLastPrompt,
prompts
} = usePrompts();

const [selectedNode, setSelectedNode] = useState(null);
const [showMinWarning, setShowMinWarning] = useState(false);

Expand Down Expand Up @@ -72,18 +77,12 @@ const NameGenerator = (props) => {
if (stageNodeCount >= minNodes) {
setShowMinWarning(false);
}
}, [stageNodeCount]);
}, [stageNodeCount, minNodes]);

// Prevent leaving the stage if the minimum number of nodes has not been met
const handleBeforeLeaving = (direction, destination) => {
const {
isFirstPrompt,
isLastPrompt,
onComplete,
} = props;

const isLeavingStage = (isFirstPrompt() && direction === -1)
|| (isLastPrompt() && direction === 1);
const isLeavingStage = (isFirstPrompt && direction === -1)
|| (isLastPrompt && direction === 1);

// Implementation quirk that destination is only provided when navigation
// is triggered by Stages Menu. Use this to skip message if user has
Expand Down Expand Up @@ -111,7 +110,7 @@ const NameGenerator = (props) => {
if (has(node, 'promptIDs')) {
addNodeToPrompt(
node[entityPrimaryKeyProperty],
prompt.id,
currentPrompt.id,
{ ...newNodeAttributes },
);
} else {
Expand All @@ -133,23 +132,22 @@ const NameGenerator = (props) => {
setSelectedNode(node);
};

console.log('rendering NameGenerator', currentPrompt, prompts)

return (
<div className="name-generator-interface" ref={interfaceRef}>
<div className="name-generator-interface__prompt">
<Prompts
prompts={prompts}
currentPrompt={prompt.id}
/>
<Prompts />
</div>
<div className="name-generator-interface__main">
<div className="name-generator-interface__panels">
<NodePanels stage={stage} prompt={prompt} disableAddNew={maxNodesReached} />
<NodePanels stage={stage} prompt={currentPrompt} disableAddNew={maxNodesReached} />
</div>
<div className="name-generator-interface__nodes">
<NodeList
items={nodesForPrompt}
stage={stage}
listId={`${stage.id}_${prompt.id}_MAIN_NODE_LIST`}
listId={`${stage.id}_${currentPrompt.id}_MAIN_NODE_LIST`}
id="MAIN_NODE_LIST"
accepts={({ meta }) => get(meta, 'itemType', null) === 'NEW_NODE'}
itemType="EXISTING_NODE"
Expand Down Expand Up @@ -204,15 +202,8 @@ const NameGenerator = (props) => {
);
};

export default NameGenerator;

NameGenerator.propTypes = {
prompt: PropTypes.object.isRequired,
stage: PropTypes.object.isRequired,
};

export default compose(
withPrompt,
)(NameGenerator);

export {
NameGenerator as UnconnectedNameGenerator,
};
2 changes: 0 additions & 2 deletions lib/interviewer/containers/NodePanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ class NodePanel extends PureComponent {
...nodeListProps
} = this.props;

console.log('NodePanel', this.props);

return (
<Panel
title={title}
Expand Down
10 changes: 5 additions & 5 deletions lib/interviewer/containers/NodePanels.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ class NodePanels extends PureComponent {
};

this.colorPresets = [
getCSSVariableAsString('--primary-color-seq-1'),
getCSSVariableAsString('--primary-color-seq-2'),
getCSSVariableAsString('--primary-color-seq-3'),
getCSSVariableAsString('--primary-color-seq-4'),
getCSSVariableAsString('--primary-color-seq-5'),
getCSSVariableAsString('--nc-primary-color-seq-1'),
getCSSVariableAsString('--nc-primary-color-seq-2'),
getCSSVariableAsString('--nc-primary-color-seq-3'),
getCSSVariableAsString('--nc-primary-color-seq-4'),
getCSSVariableAsString('--nc-primary-color-seq-5'),
];
}

Expand Down
6 changes: 0 additions & 6 deletions lib/interviewer/containers/withExternalData.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,6 @@ const mapStateToProps = (state) => {
protocolCodebook,
} = getSessionMeta(state);

console.log({
protocolUID,
assetManifest,
protocolCodebook,
});

return {
protocolUID,
assetManifest,
Expand Down
1 change: 1 addition & 0 deletions lib/interviewer/ducks/modules/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ const getReducer = (network) => (state = initialState, action = {}) => {
[action.sessionId]: withTimestamp({
...state[action.sessionId],
currentStep: action.currentStep,
promptIndex: 0,
}),
};
}
Expand Down
Loading

0 comments on commit 84235c1

Please sign in to comment.