Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 34 additions & 8 deletions src/components/menu-bar/menu-bar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -539,16 +539,16 @@ class MenuBar extends React.Component {
this.props.onArtieChangeFlowState(ARTIE_FLOW_EXERCISE_STATEMENT_STATE);
}
handleClickRequestEmotionalHelp (){
// Evita múltiples solicitudes mientras está cargando
// Avoid multiple requests while help loading is in progress
if (this.props.artieExercises.loadingHelp) return;

this.props.onArtieShowHelpPopup(null, true);

if (this.artieEmotionalPopupFeature === 'on') {
// We show the emotional popup just in the case the flag is active
// Show the emotional popup only when the feature flag is active
this.props.onArtieChangeFlowState(ARTIE_FLOW_EMOTIONAL_STATE);
} else {
// Activamos pantalla de carga mientras esperamos la ayuda
// Turn on loading overlay while waiting for the help response
this.props.onArtieLoadingHelp(true);

// Compute Split guard: if Help_Popup is OFF and the student interacts with the robot,
Expand All @@ -560,8 +560,10 @@ class MenuBar extends React.Component {
);
const hideByFeature = this.artieHelpPopupFeature === 'off' && interactsWithRobot;

// In case the the flag is off we just show the help popup
sendBlockArtie(
// Wrap the help request so we can enforce a client-side timeout
const HELP_TIMEOUT_MS = 15000; // 15 seconds to avoid an infinite loading overlay

const helpRequestPromise = sendBlockArtie(
this.props.artieLogin.currentStudent,
this.props.sprites,
this.props.artieExercises.currentExercise,
Expand All @@ -573,8 +575,25 @@ class MenuBar extends React.Component {
this.props.artieExercises.lastExerciseChange,
null,
null
)
);

const timeoutPromise = new Promise((resolve, reject) => {
this.artieHelpTimeoutId = setTimeout(() => {
// Mark this as a timeout-specific error so we can handle it if needed
const timeoutError = new Error('ARTIE_HELP_TIMEOUT');
timeoutError.code = 'ARTIE_HELP_TIMEOUT';
reject(timeoutError);
}, HELP_TIMEOUT_MS);
});

Promise.race([helpRequestPromise, timeoutPromise])
.then(responseBodyObject => {
// If we got a real response, clear the timeout timer
if (this.artieHelpTimeoutId) {
clearTimeout(this.artieHelpTimeoutId);
this.artieHelpTimeoutId = null;
}

// If the response has a solution distance object
if (responseBodyObject !== null && responseBodyObject.solutionDistance !== null){
this.props.onArtieHelpReceived(responseBodyObject.solutionDistance);
Expand All @@ -586,12 +605,19 @@ class MenuBar extends React.Component {
}
})
.catch(() => {
// En caso de error, aseguramos ocultar el overlay
// If the error is a timeout or a network/server error, just ensure the overlay closes.
if (this.artieHelpTimeoutId) {
clearTimeout(this.artieHelpTimeoutId);
this.artieHelpTimeoutId = null;
}

// TODO: Optionally dispatch a user-visible error for ARTIE help timeout
})
.finally(() => {
// Stops the loading help
// Stop the loading help overlay in all paths (success, error or timeout)
this.props.onArtieLoadingHelp(false);
});

if (this.props.artieExercises.secondsHelpOpen > 0) {
this.props.onArtieResetSecondsHelpOpen();
}
Expand Down
68 changes: 42 additions & 26 deletions src/containers/artie-help.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,16 @@ class ArtieHelp extends React.Component {

this.workspace.options.pathToMedia = 'static/blocks-media/';

// Gets the workspace metrics
// Get workspace metrics
const metrics = this.workspace.getMetrics();

// If the help is not null and we have some blocks to add
if (this.props.help !== null && this.props.help.nextSteps !== null && this.props.help.nextSteps.addBlocks !== null) {
// If we have help data and a non-empty list of blocks to add
if (this.props.help !== null &&
this.props.help.nextSteps !== null &&
this.props.help.nextSteps.addBlocks !== null
) {

// We build the block array for the elements we have to add
// Build the block array for the blocks we need to add
const addBlockArray = [];
let dy = 10;
let dx = -1;
Expand All @@ -69,7 +72,7 @@ class ArtieHelp extends React.Component {
block.setDeletable(false);
block.contextMenu = false;

// If we haven't set the center of the workspace
// If we have not yet computed the workspace center offset
if (dx === -1) {

const {x} = block.getRelativeToSurfaceXY();
Expand All @@ -79,7 +82,7 @@ class ArtieHelp extends React.Component {
const midPoint = metrics.viewWidth / 2;

if (x === 0) {
// If it's the first time positioning, it should always move right
// On first positioning, we always move the block to the right
if (block.width < midPoint) {
dx = ltrX;
} else if (block.width < metrics.viewWidth) {
Expand Down Expand Up @@ -126,13 +129,16 @@ class ArtieHelp extends React.Component {

this.workspace.options.pathToMedia = 'static/blocks-media/';

// Gets the workspace metrics
// Get workspace metrics
const metrics = this.workspace.getMetrics();

// If the help is not null and we have some blocks to delete
if (this.props.help !== null && this.props.help.nextSteps !== null && this.props.help.nextSteps.deleteBlocks !== null) {
// If we have help data and a non-empty list of blocks to delete
if (this.props.help !== null &&
this.props.help.nextSteps !== null &&
this.props.help.nextSteps.deleteBlocks !== null
) {

// We build the block array for the blocks we have to delete
// Build the block array for the blocks we need to delete
const delBlockArray = [];
let dy = 10;
let dx = -1;
Expand All @@ -146,7 +152,7 @@ class ArtieHelp extends React.Component {
block.setDeletable(false);
block.contextMenu = false;

// If we haven't set the center of the workspace
// If we have not yet computed the workspace center offset
if (dx === -1) {

const {x} = block.getRelativeToSurfaceXY();
Expand All @@ -156,7 +162,7 @@ class ArtieHelp extends React.Component {
const midPoint = metrics.viewWidth / 2;

if (x === 0) {
// If it's the first time positioning, it should always move right
// On first positioning, we always move the block to the right
if (block.width < midPoint) {
dx = ltrX;
} else if (block.width < metrics.viewWidth) {
Expand Down Expand Up @@ -203,13 +209,16 @@ class ArtieHelp extends React.Component {

this.workspace.options.pathToMedia = 'static/blocks-media/';

// Gets the workspace metrics
// Get workspace metrics
const metrics = this.workspace.getMetrics();

// If the help is not null and we have some input values to replace
if (this.props.help !== null && this.props.help.nextSteps !== null && this.props.help.nextSteps.replaceInputs !== null) {
// If we have help data and a non-empty list of inputs to replace
if (this.props.help !== null &&
this.props.help.nextSteps !== null &&
this.props.help.nextSteps.replaceInputs !== null
) {

// We build the block array for the elements we have to replace
// Build the block array for the blocks whose inputs we need to replace
const replaceBlockArray = [];
let dy = 10;
let dx = -1;
Expand All @@ -223,7 +232,7 @@ class ArtieHelp extends React.Component {
block.setDeletable(false);
block.contextMenu = false;

// If we haven't set the center of the workspace
// If we have not yet computed the workspace center offset
if (dx === -1) {

const {x} = block.getRelativeToSurfaceXY();
Expand All @@ -233,7 +242,7 @@ class ArtieHelp extends React.Component {
const midPoint = metrics.viewWidth / 2;

if (x === 0) {
// If it's the first time positioning, it should always move right
// On first positioning, we always move the block to the right
if (block.width < midPoint) {
dx = ltrX;
} else if (block.width < metrics.viewWidth) {
Expand Down Expand Up @@ -280,13 +289,16 @@ class ArtieHelp extends React.Component {

this.workspace.options.pathToMedia = 'static/blocks-media/';

// Gets the workspace metrics
// Get workspace metrics
const metrics = this.workspace.getMetrics();

// If the help is not null and we have some misplaced blocks
if (this.props.help !== null && this.props.help.nextSteps !== null && this.props.help.nextSteps.replacePositions !== null) {
// If we have help data and a non-empty list of misplaced blocks
if (this.props.help !== null &&
this.props.help.nextSteps !== null &&
this.props.help.nextSteps.replacePositions !== null
) {

// We build the block array for the elements we have to add
// Build the block array for the misplaced blocks we need to show
const misplacedBlockArray = [];
let dy = 10;
let dx = -1;
Expand All @@ -300,7 +312,7 @@ class ArtieHelp extends React.Component {
block.setDeletable(false);
block.contextMenu = false;

// If we haven't set the center of the workspace
// If we have not yet computed the workspace center offset
if (dx === -1) {

const {x} = block.getRelativeToSurfaceXY();
Expand All @@ -310,7 +322,7 @@ class ArtieHelp extends React.Component {
const midPoint = metrics.viewWidth / 2;

if (x === 0) {
// If it's the first time positioning, it should always move right
// On first positioning, we always move the block to the right
if (block.width < midPoint) {
dx = ltrX;
} else if (block.width < metrics.viewWidth) {
Expand Down Expand Up @@ -376,8 +388,12 @@ class ArtieHelp extends React.Component {
// do not render the help popup at all. Data fetching and timers can still run elsewhere,
// but the visual popup must remain hidden so the robot tutor handles the help.
const interactsWithRobot = Boolean(
(this.props.artieLogin && this.props.artieLogin.currentStudent && this.props.artieLogin.currentStudent.interactsWithRobot) ||
(this.props.artieLogin && this.props.artieLogin.user && this.props.artieLogin.user.interactsWithRobot)
(this.props.artieLogin &&
this.props.artieLogin.currentStudent &&
this.props.artieLogin.currentStudent.interactsWithRobot) ||
(this.props.artieLogin &&
this.props.artieLogin.user &&
this.props.artieLogin.user.interactsWithRobot)
);
const hideByFeature = this.props.artieHelpPopupFeature === 'off' && interactsWithRobot;

Expand Down
Loading