Skip to content

Commit

Permalink
add autoplay
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Feb 24, 2024
1 parent 2288d04 commit 863bd8d
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .vitest-preview/index.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en" class="dark h-full">
<head>
<meta charset="UTF-8" />
Expand Down
15 changes: 15 additions & 0 deletions src/components/AutoPlay/AutoPlay.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@import "src/assets/styles/styles";

.autoplay {
position: relative;
}

.progress {
position: absolute;
top: 0;
left: 0;
background-color: $primary;
height: 5px;
z-index: 0;
transition: width 0.5s linear;
}
71 changes: 71 additions & 0 deletions src/components/AutoPlay/AutoPlay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React, { useMemo, useState } from "react";
import { Technique } from "src/model/Technique";
import { Alert, Col, Form, Row } from "react-bootstrap";
import css from "./AutoPlay.module.scss";
import { useCountDown } from "./useCounter";

export interface AutoPlayProps {
className?: string;
playing: boolean;
lastTechnique?: Technique;
onWaitDone: () => void;
}

const LENGTH_OF_VIDEO_INTRO_SECONDS = 6;

const speeds: Record<string, number> = {
slow: 1000,
medium: 750,
fast: 500,
};

export const AutoPlay: React.FC<AutoPlayProps> = ({ className, playing, lastTechnique, onWaitDone }) => {
const classes = [className, css.autoplay];

const [mode, setMode] = useState("no");

const duration = useMemo(() => {
if (lastTechnique == null) return null;
return videoDurationSeconds(lastTechnique);
}, [lastTechnique]);

const { progressPercent } = useCountDown(mode !== "no" && !playing, duration, speeds[mode], onWaitDone);

return (
<Alert variant={"secondary"} className={classes.join(" ")}>
<div className={css.progress} style={{ width: `${progressPercent}%`, transitionDuration: speeds[mode] + "ms" }}>
{" "}
</div>
<Form.Group as={Row}>
<Form.Label column xs={4}>
Autoplay
</Form.Label>
<Col xs={8}>
<Form.Select
aria-label="Default select example"
value={mode}
onChange={(event) => setMode(event.target.value)}
>
<option value="no">Aus</option>
<option value="slow">Langsam</option>
<option value="medium">Mittel</option>
<option value="fast">Schnell</option>
</Form.Select>
</Col>
</Form.Group>
</Alert>
);
};

const FALLBACK_DURATION_SECONDS = 20;

function videoDurationSeconds(technique: Technique): number {
const youtube = technique.metadata.youtube;
if (youtube == null) {
return FALLBACK_DURATION_SECONDS;
}
if (Array.isArray(youtube)) {
return youtube[0].durationSeconds - LENGTH_OF_VIDEO_INTRO_SECONDS;
}
return youtube.durationSeconds - LENGTH_OF_VIDEO_INTRO_SECONDS;
}
31 changes: 31 additions & 0 deletions src/components/AutoPlay/useCounter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useState } from "react";

export function useCountDown(run: boolean, startFrom: null | number, waitMillis: number, onDone: () => void) {
const [counter, setCounter] = useState<number>(0);
const [progressPercent, setProgressPercent] = useState<number>(0);

useEffect(() => {
if (run && startFrom != null && startFrom > 0) {
let myCounter = startFrom;
setCounter(startFrom);
setProgressPercent(0);
const interval = setInterval(() => {
myCounter--;
setProgressPercent(Math.ceil(((startFrom - myCounter) / startFrom) * 100));
setCounter(myCounter);
if (myCounter <= 0) {
clearInterval(interval);
onDone();
}
}, waitMillis);
return () => {
clearInterval(interval);
};
}
}, [run, startFrom, onDone, waitMillis]);

return {
counter,
progressPercent,
};
}
2 changes: 1 addition & 1 deletion src/components/HandTracker/HandTracker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const HandTracker: React.FC<{
</div>
<div className={css.label}>
{active
? `Hand gestures are enabled, show your pointing finger to the camera to play the next defence.`
? `Hand gestures are enabled, point to your partner to get next technique`
: `Hand gestures are disabled`}
</div>
</div>
Expand Down
9 changes: 7 additions & 2 deletions src/components/Reader/Reader.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
grid-template-areas:
"back play repeat forward"
"technique technique technique technique"
"hand hand hand hand";
"autoplay autoplay hand hand";

@include media-breakpoint-up(md) {
grid-template-columns: 1fr 1fr 4fr;
Expand All @@ -20,7 +20,7 @@
"back forward technique"
"play play technique"
"repeat repeat technique"
"hand hand hand";
"autoplay autoplay hand";
}
}

Expand Down Expand Up @@ -60,6 +60,11 @@
grid-area: hand;
}

.autoplay {
grid-area: autoplay;
}


.techniqueDisplay {
grid-area: technique;
height: 100%;
Expand Down
7 changes: 7 additions & 0 deletions src/components/Reader/Reader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { NoTechniquesChosen } from "./NoTechniquesChosen";
import { useMediaSessionIfPresent } from "../../utils/hooks/useMediaSession";
import { HandTracker } from "../HandTracker/HandTracker";
import { Technique } from "../../model/Technique";
import { AutoPlay } from "../AutoPlay/AutoPlay";

export const Reader: React.FC<{
techniques: Technique[];
Expand Down Expand Up @@ -81,6 +82,12 @@ export const Reader: React.FC<{
<ChevronDoubleRight />
</Button>
<HandTracker className={css.handGestures} playing={playing} onPointGesture={playCurrentTechnique} />
<AutoPlay
className={css.autoplay}
playing={playing}
lastTechnique={lastTechnique}
onWaitDone={playCurrentTechnique}
/>
{nextTechnique != null ? (
<CurrentTechnique technique={nextTechnique} className={css.techniqueDisplay} />
) : (
Expand Down
4 changes: 2 additions & 2 deletions src/layout/DefaultLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Alert, Container, Form, Navbar } from "react-bootstrap";
import { useTranslation } from "react-i18next";
import React, { ReactNode } from "react";
import logo from "src/assets/logo.svg";
import { Square } from "react-bootstrap-icons";
import { CheckSquare } from "react-bootstrap-icons";
import { Impress } from "src/components/Impress/Imress";

export const DefaultLayout: React.FC<{ hideNavbar?: boolean; navbuttons?: ReactNode; children: ReactNode }> = ({
Expand Down Expand Up @@ -54,7 +54,7 @@ export const DefaultLayout: React.FC<{ hideNavbar?: boolean; navbuttons?: ReactN
<p>Was ich noch einbauen will:</p>
<ul>
<li>
Zeitbasiertes Auto-Play <Square />
Zeitbasiertes Auto-Play <CheckSquare />
</li>
</ul>
<hr />
Expand Down
11 changes: 2 additions & 9 deletions src/store/techniqueList.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
import { persistentAtom } from "@nanostores/persistent";
import { TechniqueList } from "../model/TechniqueList";
import { useStore } from "@nanostores/react";
import { atom } from "nanostores";

const techniqueList = persistentAtom<TechniqueList>("aikido-exam-techniques", new TechniqueList(), {
encode: (techniqueList) => {
return JSON.stringify(techniqueList.toJson());
},
decode: (string) => {
return TechniqueList.fromJson(JSON.parse(string));
},
});
const techniqueList = atom<TechniqueList>(new TechniqueList());

export function useTechniqueList(): [techniques: TechniqueList, setTechniques: (techniques: TechniqueList) => void] {
return [useStore(techniqueList), techniqueList.set.bind(techniqueList)];
Expand Down

0 comments on commit 863bd8d

Please sign in to comment.