Skip to content
Open
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
8 changes: 8 additions & 0 deletions task-launcher/src/styles/layout/_containers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,11 @@
width: 100%;
gap: 10vw;
}

.sds-progress-container {
max-width: 50vw;
display: flex;
flex-direction: row;
align-items: center;
gap: 30px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
setupReplayAudio,
camelize,
addPracticeButtonListeners,
disableOkButton,
} from '../../shared/helpers';
import { isTouchScreen, jsPsych } from '../../taskSetup';
import { taskStore } from '../../../taskStore';
Expand Down Expand Up @@ -210,11 +211,7 @@ export const downexInstructions1 = {
target.style.zIndex = '';
}

// disable ok button
const okButton: HTMLButtonElement | null = document.querySelector('.primary');
if (okButton) {
okButton.disabled = true;
}
disableOkButton();

// set up animations
let itemsToAnimate = [target, buttons, stimImage];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import jsPsychHtmlMultiResponse from '@jspsych-contrib/plugin-html-multi-response';
import { mediaAssets } from '../../..';
import { taskStore } from '../../../taskStore';
import { camelize, PageAudioHandler, replayButtonSvg } from '../../shared/helpers';
import { camelize, disableOkButton, PageAudioHandler, replayButtonSvg } from '../../shared/helpers';
import { animate } from '../helpers/animate';
import { jsPsych } from '../../taskSetup';
import { pulseOkButton } from '../../shared/helpers/pulseOkButton';
Expand Down Expand Up @@ -123,10 +123,8 @@ export const downexInstructions = downexData.map((data: any) => {
if (replayButton) {
(replayButton as HTMLButtonElement).disabled = true;
}
const okButton: HTMLButtonElement | null = document.querySelector('.primary');
if (okButton) {
okButton.disabled = true;
}

disableOkButton();

// Preserve stim-container height before animation
const stimContainer = document.getElementById('stim-container');
Expand Down
26 changes: 21 additions & 5 deletions task-launcher/src/tasks/same-different-selection/catTimeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,6 @@ export default function buildSameDifferentTimelineCat(config: Record<string, any
timeline.push(afcMatch());
timeline.push(buttonNoise);
}

if (i < trialNum - 1) {
timeline.push({ ...fixationOnly, stimulus: '' });
}
}

return {
Expand All @@ -125,7 +121,23 @@ export default function buildSameDifferentTimelineCat(config: Record<string, any
const timeline = [preloadTrials, initialTimeline];

// all instructions + practice trials
const instructionPractice: StimulusType[] = heavy ? preparedCorpus.ipHeavy : preparedCorpus.ipLight;
let instructionPractice: StimulusType[] = preparedCorpus.ipLight;

let fiveBlockIntroTrial: StimulusType;
let fiveBlockIntro: any;
if (taskStore().taskVersion === 2) {
// separate this out so that it is inserted at the right place in the timeline
fiveBlockIntroTrial = instructionPractice.find((trial) => trial.itemId === "sds-instruct5") as StimulusType;
instructionPractice = instructionPractice.filter((trial) => trial.itemId !== "sds-instruct5");

fiveBlockIntro = {
timeline: [ipBlock(fiveBlockIntroTrial)],
conditional_function: () => {
return taskStore().nextStimulus.trialType === "4-match";
}
};
}


// returns practice + instruction trials for a given block
function getPracticeInstructions(blockNum: number): StimulusType[] {
Expand Down Expand Up @@ -156,6 +168,7 @@ export default function buildSameDifferentTimelineCat(config: Record<string, any

const numOfTrials = index === 0 ? count : count / 2; // change this based on simulation results?
for (let i = 0; i < numOfTrials; i++) {

timeline.push({ ...setupStimulusFromBlock(index), stimulus: '' });

if (index === 0) {
Expand All @@ -165,6 +178,9 @@ export default function buildSameDifferentTimelineCat(config: Record<string, any
timeline.push(runCatTrials(2, 'stimulus'));
}
if (index === 2) {
if (taskStore().taskVersion === 2) {
timeline.push(fiveBlockIntro);
}
timeline.push(runCatTrials(2, 'afc'));
timeline.push(runCatTrials(3, 'afc'));
timeline.push(runCatTrials(4, 'afc'));
Expand Down
6 changes: 4 additions & 2 deletions task-launcher/src/tasks/same-different-selection/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,17 @@ export default function buildSameDifferentTimeline(config: Record<string, any>,
timeline.push(stimulusBlock);
};

const setupTrialDuration = taskStore().taskVersion === 2 ? 0 : 350;

const updateSomethingSame = () => {
timeline.push({ ...setupStimulus, stimulus: '' });
timeline.push({ ...setupStimulus, stimulus: '', trial_duration: setupTrialDuration});
timeline.push(stimulusBlock);
timeline.push(buttonNoise);
timeline.push(dataQualityBlock);
};

const updateMatching = () => {
timeline.push({ ...setupStimulus, stimulus: '' });
timeline.push({ ...setupStimulus, stimulus: '', trial_duration: setupTrialDuration});
timeline.push(afcBlock);
timeline.push(buttonNoise);
timeline.push(dataQualityBlock);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { finishExperiment } from '../../shared/trials';
import { taskStore } from '../../../taskStore';
import { updateTheta } from '../../shared/helpers';
import { sdsProgressComponentFilled, sdsProgressComponentEmpty } from '../../shared/helpers/components';

let selectedCards: string[] = [];
let selectedCardIdxs: number[] = [];
Expand Down Expand Up @@ -193,29 +194,54 @@ export const afcMatch = (trial?: StimulusType) => {

let numberOfErrors = 0;

// Add primary OK button under the other buttons
if (stim.trialType !== 'instructions') {
if (taskStore().taskVersion === 2) {
// insert progress indicator
const numbers = {
'first_response': 1,
'second_response': 2,
'third_response': 3,
'fourth_response': 4,
}
const currentResponse = numbers[stim.assessmentStage as keyof typeof numbers];
const maxResponses = Number(stim.trialType[0]);

if (currentResponse !== undefined) {
const progressContainer = document.createElement('div');
progressContainer.className = 'sds-progress-container';
progressContainer.innerHTML = `
${sdsProgressComponentFilled.repeat(currentResponse)} ${sdsProgressComponentEmpty.repeat(maxResponses - currentResponse)}
`;
progressContainer.style.marginTop = '32px';

buttonContainer.parentNode?.insertBefore(progressContainer, buttonContainer.nextSibling);
}

// Add primary OK button under the other buttons
const okButton = document.createElement('button');
okButton.className = 'primary';
okButton.textContent = 'OK';
okButton.style.marginTop = '16px';
okButton.disabled = true;
okButton.addEventListener('click', () => {
if (!isPractice || compareSelections(selectedCards, previousSelections, getIgnoreDims(stim))) {
numberOfErrors = 0;
jsPsych.finishTrial();
} else {
const prompt = document.getElementById('afc-match-prompt') as HTMLParagraphElement;
prompt.textContent = `${taskStore().translations.feedbackNotQuiteRight} ${
taskStore().translations[camelize(audioFile)]
}`;

numberOfErrors++;
const numberOfErrorsThisCall = numberOfErrors;

const audioConfig: AudioConfigType = {
restrictRepetition: {
enabled: false,
maxRepetitions: 2,
},
onEnded: (numberOfErrorsThisCall: number) => {
onEnded: () => {
if (numberOfErrorsThisCall === numberOfErrors) {
// don't overlap audio
PageAudioHandler.playAudio(mediaAssets.audio[camelize(audioFile)]);
Expand All @@ -229,7 +255,6 @@ export const afcMatch = (trial?: StimulusType) => {
responseBtns.forEach((btn) => btn.classList.remove(SELECT_CLASS_NAME));
selectedCards = [];
disableOkButton();
numberOfErrors++;

if (numberOfErrors >= 2) {
let animationStarted = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ export const stimulus = (trial?: StimulusType) => {
response_ends_trial: () => {
const stim = trial || taskStore().nextStimulus;

return stim.trialType !== 'test-dimensions';
return !(stim.trialType === 'test-dimensions' || (stim.trialType === 'something-same-2' && stim.assessmentStage === 'practice_response'));
},
on_load: () => {
startTime = performance.now();
Expand Down Expand Up @@ -296,8 +296,7 @@ export const stimulus = (trial?: StimulusType) => {
leftPrompt.style.height = `${contentBoxHeight}px`;
}

const okButton = document.querySelector('.primary') as HTMLButtonElement;
okButton.disabled = true;
disableOkButton();

const responseBtns = Array.from(
document.getElementById('img-button-container')?.children as any,
Expand Down Expand Up @@ -335,6 +334,43 @@ export const stimulus = (trial?: StimulusType) => {
setTimeout(() => enableBtns(responseBtns), 500);
});
});

let numberOfErrors = 0;

if (stimulus.assessmentStage === 'practice_response') {
const okButton = document.querySelector('.primary') as HTMLButtonElement;
okButton.addEventListener('click', (e) => {
if (selectionIdx !== taskStore().correctResponseIdx) {
const rightPrompt = document.getElementById('right-prompt') as HTMLParagraphElement;
const leftPrompt = document.getElementById('left-prompt') as HTMLParagraphElement;
rightPrompt.innerHTML = `<p>${taskStore().translations.feedbackNotQuiteRight}</p>`;
leftPrompt.style.visibility = 'hidden';

numberOfErrors++;

const audioConfig: AudioConfigType = {
restrictRepetition: {
enabled: false,
maxRepetitions: 2,
}
};

PageAudioHandler.stopAndDisconnectNode();
PageAudioHandler.playAudio(mediaAssets.audio.feedbackNotQuiteRight, audioConfig);

responseBtns.forEach((btn) => btn.classList.remove(SELECT_CLASS_NAME));
selection = null;
selectionIdx = null;

if (numberOfErrors >= 2) {
responseBtns[taskStore().correctResponseIdx].style.animation = 'pulse 2s infinite';
}
} else {
e.stopPropagation(); // prevents jspsych from disabling the buttons in the next trial
jsPsych.finishTrial();
}
});
}
}

// if the task is running in a cypress test, the correct answer should be indicated with 'correct' class
Expand Down Expand Up @@ -392,7 +428,7 @@ export const stimulus = (trial?: StimulusType) => {
if (stim.trialType !== 'something-same-1') {
// update task store
taskStore('isCorrect', isCorrect);
if (isCorrect === false) {
if (isCorrect === false && stim.assessmentStage !== 'practice_response') {
taskStore.transact('numIncorrect', (oldVal: number) => oldVal + 1);
} else {
taskStore('numIncorrect', 0);
Expand Down
12 changes: 12 additions & 0 deletions task-launcher/src/tasks/shared/helpers/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,15 @@ export const arrowKeyEmojis = [
</svg>`,
],
];

export const sdsProgressComponentFilled = `
<svg xmlns="http://www.w3.org/2000/svg" width="88" height="32" viewBox="0 0 88 32" fill="none" aria-hidden="true">
<rect x="1" y="1" width="80" height="24" rx="12" ry="12" fill="#275BDD" />
</svg>
`;

export const sdsProgressComponentEmpty = `
<svg xmlns="http://www.w3.org/2000/svg" width="88" height="32" viewBox="0 0 88 32" fill="none" aria-hidden="true">
<rect x="1" y="1" width="80" height="24" rx="12" ry="12" fill="#828282" />
</svg>
`;
1 change: 1 addition & 0 deletions task-launcher/src/tasks/shared/trials/feedback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import jsPsychHtmlMultiResponse from '@jspsych-contrib/plugin-html-multi-respons
import { mediaAssets } from '../../..';
import { PageAudioHandler } from '../helpers';
import { taskStore } from '../../../taskStore';
import { enableOkButton } from '../helpers/enableButtons';

// isPractice parameter is for tasks that don't have a corpus (e.g. memory game)
export const feedback = (
Expand Down
Loading