Skip to content

Commit

Permalink
refactor: Improve typing of several components & utilities
Browse files Browse the repository at this point in the history
  • Loading branch information
drikusroor committed Aug 9, 2024
1 parent d2f5bb4 commit b3b27a0
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 18 deletions.
3 changes: 2 additions & 1 deletion frontend/src/components/FeedbackForm/FeedbackForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ import { useState, useRef } from "react";
import Question from "../Question/Question";
import Button from "../Button/Button";
import IQuestion from "@/types/Question";
import { OnResultType } from "@/hooks/useResultHandler";

interface FeedbackFormProps {
formActive: boolean;
form: IQuestion[];
buttonLabel: string;
skipLabel: string;
isSkippable: boolean;
onResult: (result: any) => void;
onResult: OnResultType
emphasizeTitle?: boolean;
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/MatchingPairs/MatchingPairs.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useRef, useState } from "react";
import { useRef, useState } from "react";
import classNames from "classnames";

import { scoreIntermediateResult } from "../../API";
Expand Down
31 changes: 27 additions & 4 deletions frontend/src/components/Trial/Trial.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,39 @@
import React, { useState, useRef, useCallback } from "react";
import { useState, useRef, useCallback } from "react";
import classNames from "classnames";

import { getCurrentTime, getTimeSince } from "../../util/time";
import FeedbackForm from "../FeedbackForm/FeedbackForm";
import HTML from "../HTML/HTML";
import Playback from "../Playback/Playback";
import Button from "../Button/Button";
import Question from "@/types/Question";
import { OnResultType } from "@/hooks/useResultHandler";
import { TrialConfig } from "@/types/Trial";

interface IFeedbackForm {
form: Question[];
submit_label: string;
skip_label: string;
is_skippable: boolean;
}

interface TrialProps {
playback: any;
html: { body: string | TrustedHTML };
feedback_form: IFeedbackForm;
config: TrialConfig;
result_id: string;
onNext: (breakRound?: boolean) => void;
onResult: OnResultType;
}

/**
* Trial is an block view to present information to the user and/or collect user feedback
* If "playback" is provided, it will play audio through the Playback component
* If "html" is provided, it will show html content
* If "feedback_form" is provided, it will present a form of questions to the user
*/
const Trial = (props) => {
const Trial = (props: TrialProps) => {

const {
playback,
Expand Down Expand Up @@ -41,13 +61,15 @@ const Trial = (props) => {

// Create result data
const makeResult = useCallback(
async (result) => {
async (result: { type: 'time_passed' }) => {
// Prevent multiple submissions
if (submitted.current) {
return;
}
submitted.current = true;

// TODO: Check if we can find another solution for
// the default value of form than [{}]
const form = feedback_form ? feedback_form.form : [{}];

if (result.type === "time_passed") {
Expand Down Expand Up @@ -97,7 +119,8 @@ const Trial = (props) => {
const checkBreakRound = (values, breakConditions) => {
switch (Object.keys(breakConditions)[0]) {
case 'EQUALS':
return values.some(val => breakConditions['EQUALS'].includes(val));
return values.some(val => breakConditions['EQUALS']
.includes(val));
case 'NOT':
return !values.some(val => breakConditions['NOT'].includes(val));
default:
Expand Down
21 changes: 19 additions & 2 deletions frontend/src/hooks/useResultHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useRef, useCallback } from "react";
import { scoreResult } from "@/API";
import Session from "@/types/Session";
import Participant from "@/types/Participant";
import Question from "@/types/Question";
import { TrialConfig } from "@/types/Trial";

interface ResultData {
session: Session;
Expand All @@ -10,18 +12,31 @@ interface ResultData {
section?: unknown;
}

interface UseResultHandlerParams {
session: Session;
participant: Participant;
onNext: () => void;
state: any;
}

interface OnResultParams {
form: Question[];
decision_time?: number;
config?: TrialConfig
}

/**
* useResult provides a reusable function to handle block view data
* - collect results in a buffer
* - handles advancing to next round
* - finally submits the data to the API and loads the new state
*/
const useResultHandler = ({ session, participant, onNext, state }) => {
const useResultHandler = ({ session, participant, onNext, state }: UseResultHandlerParams) => {
const resultBuffer = useRef([]);

const onResult = useCallback(
async (
result: unknown,
result: OnResultParams,
forceSubmit = false,
goToNextAction = true
) => {
Expand Down Expand Up @@ -74,4 +89,6 @@ const useResultHandler = ({ session, participant, onNext, state }) => {
return onResult;
};

export type OnResultType = ReturnType<typeof useResultHandler>;

export default useResultHandler;
20 changes: 10 additions & 10 deletions frontend/src/util/audio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Timer from "./timer";
// <audio /> docs: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio
const audio = document.createElement("audio");
audio.id = "audio-player";
audio.controls = "controls";
audio.controls = true;
audio.src = SILENT_MP3;

// switch to cors anonymous for local development (needed for webaudio)
Expand All @@ -27,7 +27,7 @@ document.body.appendChild(audio);
// expose global
window.audio = audio;

let _stopFadeTimer = null;
let _stopFadeTimer: () => void | null = null;
export let audioInitialized = false;

// Play a silent mp3 to make the audio element play after a user action
Expand Down Expand Up @@ -95,7 +95,7 @@ export const fadeOut = (duration = 0.1) => {
};

// Set audio volume
export const setVolume = (volume) => {
export const setVolume = (volume: number) => {
audio.volume = volume;
};

Expand All @@ -114,7 +114,7 @@ export const play = () => {
};

// Play audio from given time
export const playFrom = (time) => {
export const playFrom = (time: number) => {
setVolume(1);
play();
setCurrentTime(time);
Expand All @@ -131,7 +131,7 @@ export const stop = () => {
};

// Load audio from given source
export const load = (src) => {
export const load = (src: string) => {
stop();
audio.src = src;

Expand All @@ -140,7 +140,7 @@ export const load = (src) => {
};

// Set current time t
export const setCurrentTime = (t) => {
export const setCurrentTime = (t: number) => {
audio.currentTime = t;
};

Expand All @@ -151,7 +151,7 @@ export const getCurrentTime = () => {

// Add a time update listener that sends current time to the callback
// Return a function to remove the event listener
export const onTimeUpdate = (onTimeUpdate) => {
export const onTimeUpdate = (onTimeUpdate: (time: number) => void) => {
const callback = () => {
onTimeUpdate(audio.currentTime);
};
Expand All @@ -164,7 +164,7 @@ export const onTimeUpdate = (onTimeUpdate) => {
};

// Load audio, and call canPlay when audio is ready
export const loadUntilAvailable = (src, canPlay) => {
export const loadUntilAvailable = (src: string, canPlay: () => void) => {
// The canplaythrough event is fired when the user agent can play the media,
// and estimates that enough data has been loaded to play the media up to its end
// without having to stop for further buffering of content.
Expand All @@ -178,15 +178,15 @@ export const loadUntilAvailable = (src, canPlay) => {
if (audio.readyState > 3) {
removeListener();
canPlay();
return () => {};
return () => { };
}

return removeListener;
};

// Listen once to the given event
// After that remove listener
export const listenOnce = (event, callback) => {
export const listenOnce = (event: string, callback: () => void) => {
const remove = () => {
audio.removeEventListener(event, _callback);
};
Expand Down

0 comments on commit b3b27a0

Please sign in to comment.