diff --git a/src/JsPsych/timeline.js b/src/JsPsych/timeline.js
index 13afe99c3..670e759d3 100644
--- a/src/JsPsych/timeline.js
+++ b/src/JsPsych/timeline.js
@@ -1,9 +1,10 @@
// TODO 204: @ import for language
-import { language } from "./language";
+// import { language } from "./language";
// TODO 204: @ import for trials
-import { Preamble, createCountdownTrial, EndExperiment } from "./trials/examples";
-import { createHoneycombBlock } from "./trials/honeycombBlock";
+import { Preamble, EndExperiment } from "./trials/examples";
+// import { Preamble, createCountdownTrial, EndExperiment } from "./trials/examples";
+// import { createHoneycombBlock } from "./trials/honeycombBlock";
/**
* Create your custom JsPsych options here. These settings will applied experiment wide.
@@ -25,44 +26,38 @@ export const JSPSYCH_OPTIONS = {
* @returns array of trials
*/
// TODO 207: Eslint warning is causing build to fail
-// eslint-disable-next-line
-export function buildTimeline(jsPsych) {
- // Get slider text from the language file and create the trials
- // const sliderLanguage = language.quiz.direction.slider;
- // const sliderLeft = createSliderTrial(sliderLanguage.left);
- // const sliderRight = createSliderTrial(sliderLanguage.right);
-
+export function buildTimeline() {
// Get countdown txt from the language fil and create the trials+
- const countdownLanguage = language.countdown;
- const firstBlockCountdown = createCountdownTrial({ message: countdownLanguage.first });
- const secondBlockCountdown = createCountdownTrial({ message: countdownLanguage.second });
+ // const countdownLanguage = language.countdown;
+ // const firstBlockCountdown = createCountdownTrial({ message: countdownLanguage.first });
+ // const secondBlockCountdown = createCountdownTrial({ message: countdownLanguage.second });
- // Create a tutorial block of Honeycomb's custom task
- const honeycombTutorialBlock = createHoneycombBlock({
- isTutorial: true,
- photodiodeActive: false,
- });
+ // // Create a tutorial block of Honeycomb's custom task
+ // const honeycombTutorialBlock = createHoneycombBlock({
+ // isTutorial: true,
+ // photodiodeActive: false,
+ // });
- // Create a practice block of Honeycomb's custom task
- const honeycombPracticeBlock = createHoneycombBlock({
- conditions: ["m", "n"],
- repeatsPerCondition: 1,
- isPractice: true,
- });
+ // // Create a practice block of Honeycomb's custom task
+ // const honeycombPracticeBlock = createHoneycombBlock({
+ // conditions: ["m", "n"],
+ // repeatsPerCondition: 1,
+ // isPractice: true,
+ // });
- // Create an experiment block
- const honeycombBlock1 = createHoneycombBlock({
- repeatsPerCondition: 2,
- });
+ // // Create an experiment block
+ // const honeycombBlock1 = createHoneycombBlock({
+ // repeatsPerCondition: 2,
+ // });
// Build the timeline
const timeline = [
Preamble,
- honeycombTutorialBlock,
- firstBlockCountdown,
- honeycombPracticeBlock,
- secondBlockCountdown,
- honeycombBlock1,
+ // honeycombTutorialBlock,
+ // firstBlockCountdown,
+ // honeycombPracticeBlock,
+ // secondBlockCountdown,
+ // honeycombBlock1,
EndExperiment, // Task complete message
];
return timeline;
diff --git a/src/components/App.jsx b/src/components/App.jsx
index fc490d97c..7d05bc154 100644
--- a/src/components/App.jsx
+++ b/src/components/App.jsx
@@ -4,7 +4,7 @@ import "bootstrap/dist/css/bootstrap.css";
import { addToFirebase, validateParticipant } from "../firebase";
-import JsPsychExperiment from "./JsPsychExperiment";
+import Experiment from "./Experiment";
import Login from "./Login";
import Error from "./Error";
@@ -13,6 +13,9 @@ import { getQueryVariable } from "../utils";
import { TASK_VERSION } from "../JsPsych/constants";
+// Additional options passed by the user
+import { JSPSYCH_OPTIONS } from "../JsPsych/timeline";
+
/** Top-level React component for Honeycomb.
*
* This component stores the state of the app.
@@ -189,11 +192,10 @@ function App() {
setLoggedIn(true);
}, []);
- if (isError) {
- return ;
- } else {
+ if (isError) return ;
+ else {
return loggedIn ? (
-
) : (
// Not logged in - display login screen
diff --git a/src/components/Experiment.jsx b/src/components/Experiment.jsx
new file mode 100644
index 000000000..12a767082
--- /dev/null
+++ b/src/components/Experiment.jsx
@@ -0,0 +1,55 @@
+import { initJsPsych } from "jspsych";
+import React, { useEffect } from "react";
+
+import { initParticipant } from "../firebase";
+
+// These will be passed in as props to the package once it's split
+import { buildTimeline } from "../JsPsych/timeline";
+
+const EXPERIMENT_ID = "jspsych-experiment-window";
+
+// Based on https://react.dev/reference/react/useEffect#controlling-a-non-react-widget
+export default function Experiment({
+ config,
+ studyID,
+ participantID,
+ taskVersion,
+ dataUpdateFunction,
+ dataFinishFunction,
+ jsPsychOptions,
+}) {
+ // Initialize and run JsPsych on first render
+ useEffect(() => {
+ // Start date of the experiment - used as the UID
+ // TODO 169: JsPsych has a built in timestamp function
+ const startDate = new Date().toISOString();
+
+ // Write the initial record to Firestore
+ if (config.USE_FIREBASE) initParticipant(studyID, participantID, startDate);
+
+ // Initialize experiment, user passed options are merged with defaults
+ const jsPsych = initJsPsych({
+ display_element: EXPERIMENT_ID,
+ on_data_update: (data) => dataUpdateFunction(data),
+ on_finish: (data) => dataFinishFunction(data),
+ ...jsPsychOptions,
+ });
+ // Add experiment data to the trials
+ jsPsych.data.addProperties({
+ study_id: studyID,
+ participant_id: participantID,
+ start_date: startDate,
+ task_version: taskVersion,
+ });
+
+ // Run the experiment
+ jsPsych.run(buildTimeline()).then(() => {
+ // TODO: Return to home page after experiment completes
+ // TODO: Need to update the electron code to save data here, not
+ console.log("FINISHED EXPERIMENT");
+ window.location.reload();
+ });
+ }, []);
+
+ return
;
+}
diff --git a/src/components/JsPsychExperiment.jsx b/src/components/JsPsychExperiment.jsx
deleted file mode 100644
index eec1a425b..000000000
--- a/src/components/JsPsychExperiment.jsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import { initJsPsych } from "jspsych";
-import React, { useEffect, useMemo, useRef } from "react";
-
-import { initParticipant } from "../firebase";
-
-// These will be passed in as props to the package once it's split
-import { buildTimeline, JSPSYCH_OPTIONS } from "../JsPsych/timeline";
-
-/** JsPsych Experiment
- *
- * This component initializes the JsPsych instance for the experiment.
- * It provides a window for JsPsych to run inside of (experimentDiv)
- * It also handles the passing of keyboard/mouse events into JsPsych
- */
-function JsPsychExperiment({
- config,
- studyID,
- participantID,
- taskVersion,
- dataUpdateFunction,
- dataFinishFunction,
-}) {
- // This will be the div in the dom that holds the experiment.
- // We reference it explicitly here so we can do some plumbing with react, jspsych, and events.
- const experimentDivID = "experimentWindow";
- const experimentDivRef = useRef(null);
-
- // Create the instance of jsPsych that we'll reuse within the scope of this JsPsychExperiment component.
- // As of jspsych 7, we create our own jspsych instance(s) where needed instead of importing one global instance.
- const jsPsych = useMemo(() => {
- // Start date of the experiment - used as the UID
- // TODO 169: JsPsych has a built in timestamp function
- const startDate = new Date().toISOString();
-
- // Write the initial record to Firestore
- if (config.USE_FIREBASE) initParticipant(studyID, participantID, startDate);
-
- // Initialize experiment with needed data. Combines custom options with necessary Honeycomb options.
- const jsPsych = initJsPsych({
- ...JSPSYCH_OPTIONS, // This will be passed to the package
- display_element: experimentDivID,
- on_data_update: (data) => dataUpdateFunction(data),
- on_finish: (data) => dataFinishFunction(data),
- });
- jsPsych.data.addProperties({
- study_id: studyID,
- participant_id: participantID,
- start_date: startDate,
- task_version: taskVersion,
- });
- return jsPsych;
- }, [studyID, participantID, taskVersion]);
-
- // Build our jspsych experiment timeline (in this case a Honeycomb demo, you could substitute your own here).
- // ? I wonder if it makes the most sense to just have the user create and pass the jsPsych object?
- const timeline = buildTimeline(jsPsych);
- // TODO 238: I think the user needs to confirm if they're going to enable audio?
-
- // Set up event and lifecycle callbacks to start and stop jspsych.
- // Inspiration from jspsych-react: https://github.com/makebrainwaves/jspsych-react/blob/master/src/index.js
- // These useEffect callbacks are similar to componentDidMount / componentWillUnmount.
- function handleKeyEvent(e) {
- if (e.redispatched) return;
-
- const newEvent = new e.constructor(e.type, e);
- newEvent.redispatched = true;
- experimentDivRef.current.dispatchEvent(newEvent);
- }
-
- useEffect(() => {
- window.addEventListener("keyup", handleKeyEvent, true);
- window.addEventListener("keydown", handleKeyEvent, true);
- jsPsych.run(timeline);
-
- return () => {
- window.removeEventListener("keyup", handleKeyEvent, true);
- window.removeEventListener("keydown", handleKeyEvent, true);
- try {
- jsPsych.endExperiment("Ended Experiment");
- } catch (e) {
- console.error("Experiment closed before unmount");
- }
- };
- });
-
- // TODO 184: Root is not taking up 100vh here? The isn't? Are the trials causing that?
- return (
-
- );
-}
-
-export default JsPsychExperiment;
diff --git a/src/firebase/firebase.js b/src/firebase/firebase.js
index f5284a20d..65a3d11f4 100644
--- a/src/firebase/firebase.js
+++ b/src/firebase/firebase.js
@@ -56,9 +56,9 @@ export async function initParticipant(studyID, participantID, startDate) {
await experiment.set({
start_time: startDate,
// TODO 173: app_version and app_platform are deprecated
+ // TODO: Pass taskVersion into this function from
app_version: window.navigator.appVersion,
app_platform: window.navigator.platform,
- // TODO 175: Store participantID and studyID here, not on each trial
});
console.log("Initialized experiment:", studyID, participantID, startDate);
return true;