feat(client): persist playback rate in lecture videos (#58087)

This commit is contained in:
Tom
2025-01-17 02:26:06 -06:00
committed by GitHub
parent 90c1bd2e32
commit 499ccb53a0
6 changed files with 108 additions and 65 deletions

View File

@@ -81,12 +81,12 @@
"gatsby-source-filesystem": "3.15.0",
"gatsby-transformer-remark": "5.25.1",
"i18next": "22.5.1",
"instantsearch.js": "4.75.3",
"lodash": "4.17.21",
"lodash-es": "4.17.21",
"micromark": "4.0.0",
"monaco-editor": "0.28.1",
"nanoid": "3.3.7",
"instantsearch.js": "4.75.3",
"normalize-url": "4.5.1",
"path-browserify": "1.0.1",
"postcss": "8.4.35",
@@ -102,8 +102,8 @@
"react-helmet": "6.1.0",
"react-hotkeys": "2.0.0",
"react-i18next": "12.3.1",
"react-instantsearch-core": "7.13.6",
"react-instantsearch": "7.13.6",
"react-instantsearch-core": "7.13.6",
"react-monaco-editor": "0.40.0",
"react-redux": "7.2.9",
"react-reflex": "4.1.0",
@@ -112,7 +112,7 @@
"react-spinkit": "3.0.0",
"react-tooltip": "4.5.1",
"react-transition-group": "4.4.5",
"react-youtube": "7.14.0",
"react-youtube": "10.1.0",
"redux": "4.2.1",
"redux-actions": "2.6.5",
"redux-observable": "1.2.0",

View File

@@ -1,5 +1,6 @@
import React from 'react';
import YouTube from 'react-youtube';
import YouTube, { YouTubeEvent } from 'react-youtube';
import store from 'store';
import Loader from '../../../components/helpers/loader';
import envData from '../../../../config/env.json';
@@ -19,12 +20,16 @@ const { clientLocale } = envData as {
interface VideoPlayerProps {
videoId: string;
videoLocaleIds?: VideoLocaleIds;
onVideoLoad: () => void;
onVideoLoad: (e: YouTubeEvent) => void;
videoIsLoaded: boolean;
bilibiliIds?: BilibiliIds;
title: string;
}
function setPlaybackRate(e: YouTubeEvent<number>) {
store.set('fcc-yt-playback-rate', e.data);
}
function VideoPlayer({
videoId,
videoLocaleIds,
@@ -69,6 +74,7 @@ function VideoPlayer({
videoIsLoaded ? 'display-youtube-video' : 'hide-youtube-video'
}
onReady={onVideoLoad}
onPlaybackRateChange={setPlaybackRate}
opts={{
playerVars: {
rel: 0

View File

@@ -5,6 +5,8 @@ import { useTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { Container, Col, Row, Button, Spacer } from '@freecodecamp/ui';
import { isEqual } from 'lodash';
import store from 'store';
import { YouTubeEvent } from 'react-youtube';
// Local Utilities
import LearnLayout from '../../../components/layouts/learn';
@@ -127,7 +129,13 @@ const ShowGeneric = ({
// video
const [videoIsLoaded, setVideoIsLoaded] = useState(false);
const handleVideoIsLoaded = () => {
const handleVideoIsLoaded = (e: YouTubeEvent) => {
const playbackRate = Number(store.get('fcc-yt-playback-rate')) || 1;
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const player = e.target;
// eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
player.setPlaybackRate(playbackRate);
setVideoIsLoaded(true);
};

View File

@@ -7,7 +7,8 @@
}
.video-wrapper iframe,
.video-wrapper object,
.video-wrapper embed {
.video-wrapper embed,
.video-wrapper .display-youtube-video {
position: absolute;
top: 0;
left: 0;

View File

@@ -8,13 +8,10 @@ test.beforeEach(async ({ page }) => {
test.describe('Challenge Video Player Component Tests', () => {
test('should render video player and play button', async ({ page }) => {
await expect(page.locator('.display-youtube-video')).toBeVisible();
await expect(
page.locator('iframe[title="YouTube video player"]')
).toBeVisible();
await expect(
page
.frameLocator('.display-youtube-video')
.getByRole('button', { name: 'Play' })
).toBeVisible();
});
});

137
pnpm-lock.yaml generated
View File

@@ -657,8 +657,8 @@ importers:
specifier: 4.4.5
version: 4.4.5(react-dom@16.14.0(react@16.14.0))(react@16.14.0)
react-youtube:
specifier: 7.14.0
version: 7.14.0(react@16.14.0)
specifier: 10.1.0
version: 10.1.0(react@16.14.0)
redux:
specifier: 4.2.1
version: 4.2.1
@@ -833,7 +833,7 @@ importers:
version: 10.9.2(@types/node@20.12.8)(typescript@5.2.2)
webpack:
specifier: 5.90.3
version: 5.90.3(webpack-cli@4.10.0)
version: 5.90.3
curriculum:
devDependencies:
@@ -1122,7 +1122,7 @@ importers:
version: 4.3.12
'@types/copy-webpack-plugin':
specifier: ^8.0.1
version: 8.0.1(webpack-cli@4.10.0)
version: 8.0.1(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))
'@types/enzyme':
specifier: 3.10.16
version: 3.10.16
@@ -1140,13 +1140,13 @@ importers:
version: 1.6.0(typescript@5.4.5)
babel-loader:
specifier: 8.3.0
version: 8.3.0(@babel/core@7.23.7)(webpack@5.90.3)
version: 8.3.0(@babel/core@7.23.7)(webpack@5.90.3(webpack-cli@4.10.0))
chai:
specifier: 4.4.1
version: 4.4.1
copy-webpack-plugin:
specifier: 9.1.0
version: 9.1.0(webpack@5.90.3)
version: 9.1.0(webpack@5.90.3(webpack-cli@4.10.0))
enzyme:
specifier: 3.11.0
version: 3.11.0
@@ -11126,9 +11126,6 @@ packages:
prop-types-exact@1.2.0:
resolution: {integrity: sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA==}
prop-types@15.7.2:
resolution: {integrity: sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==}
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -11481,9 +11478,9 @@ packages:
react: '>=16.6.0'
react-dom: '>=16.6.0'
react-youtube@7.14.0:
resolution: {integrity: sha512-SUHZ4F4pd1EHmQu0CV0KSQvAs5KHOT5cfYaq4WLCcDbU8fBo1ouTXaAOIASWbrz8fHwg+G1evfoSIYpV2AwSAg==}
engines: {node: '>= 10.x'}
react-youtube@10.1.0:
resolution: {integrity: sha512-ZfGtcVpk0SSZtWCSTYOQKhfx5/1cfyEW1JN/mugGNfAxT3rmVJeMbGpA9+e78yG21ls5nc/5uZJETE3cm3knBg==}
engines: {node: '>= 14.x'}
peerDependencies:
react: '>=0.14.1'
@@ -17692,7 +17689,7 @@ snapshots:
react-refresh: 0.9.0
schema-utils: 2.7.1
source-map: 0.7.4
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
'@polka/url@1.0.0-next.23': {}
@@ -18432,7 +18429,7 @@ snapshots:
'@types/cookiejar@2.1.2': {}
'@types/copy-webpack-plugin@8.0.1(webpack-cli@4.10.0)':
'@types/copy-webpack-plugin@8.0.1(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))':
dependencies:
'@types/node': 20.8.0
tapable: 2.2.1
@@ -19181,17 +19178,17 @@ snapshots:
'@webassemblyjs/ast': 1.11.6
'@xtuc/long': 4.2.2
'@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0)(webpack@5.90.3)':
'@webpack-cli/configtest@1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))(webpack@5.90.3(webpack-cli@4.10.0))':
dependencies:
webpack: 5.90.3(webpack-cli@4.10.0)
webpack-cli: 4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3)
'@webpack-cli/info@1.5.0(webpack-cli@4.10.0)':
'@webpack-cli/info@1.5.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))':
dependencies:
envinfo: 7.10.0
webpack-cli: 4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3)
'@webpack-cli/serve@1.7.0(webpack-cli@4.10.0)':
'@webpack-cli/serve@1.7.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))':
dependencies:
webpack-cli: 4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3)
@@ -19675,9 +19672,9 @@ snapshots:
loader-utils: 2.0.4
make-dir: 3.1.0
schema-utils: 2.7.1
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
babel-loader@8.3.0(@babel/core@7.23.7)(webpack@5.90.3):
babel-loader@8.3.0(@babel/core@7.23.7)(webpack@5.90.3(webpack-cli@4.10.0)):
dependencies:
'@babel/core': 7.23.7
find-cache-dir: 3.3.2
@@ -20804,7 +20801,7 @@ snapshots:
copy-descriptor@0.1.1: {}
copy-webpack-plugin@9.1.0(webpack@5.90.3):
copy-webpack-plugin@9.1.0(webpack@5.90.3(webpack-cli@4.10.0)):
dependencies:
fast-glob: 3.3.1
glob-parent: 6.0.2
@@ -20996,7 +20993,7 @@ snapshots:
postcss-value-parser: 4.2.0
schema-utils: 3.3.0
semver: 7.6.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
css-mediaquery@0.1.2: {}
@@ -21009,7 +21006,7 @@ snapshots:
schema-utils: 3.3.0
serialize-javascript: 5.0.1
source-map: 0.6.1
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
css-select@4.3.0:
dependencies:
@@ -21944,7 +21941,7 @@ snapshots:
debug: 4.3.4(supports-color@8.1.1)
enhanced-resolve: 5.15.0
eslint: 8.57.0
eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0)
eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0)
get-tsconfig: 4.7.2
globby: 13.2.2
@@ -21968,7 +21965,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0):
eslint-module-utils@2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0):
dependencies:
debug: 3.2.7(supports-color@5.5.0)
optionalDependencies:
@@ -22041,7 +22038,7 @@ snapshots:
doctrine: 2.1.0
eslint: 8.57.0
eslint-import-resolver-node: 0.3.9
eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5)(eslint@8.57.0)
eslint-module-utils: 2.8.0(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.5.5(@typescript-eslint/parser@7.1.1(eslint@8.57.0)(typescript@5.4.5))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0)
hasown: 2.0.0
is-core-module: 2.13.1
is-glob: 4.0.3
@@ -22222,7 +22219,7 @@ snapshots:
micromatch: 4.0.8
normalize-path: 3.0.0
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
eslint@7.32.0:
dependencies:
@@ -22713,7 +22710,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
file-type@16.5.4:
dependencies:
@@ -22844,7 +22841,7 @@ snapshots:
semver: 5.7.2
tapable: 1.1.3
typescript: 5.2.2
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
worker-rpc: 0.1.1
optionalDependencies:
eslint: 7.32.0
@@ -23419,7 +23416,7 @@ snapshots:
url-loader: 4.1.1(file-loader@6.2.0(webpack@5.90.3))(webpack@5.90.3)
uuid: 3.4.0
v8-compile-cache: 2.4.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
webpack-dev-middleware: 4.3.0(webpack@5.90.3)
webpack-merge: 5.9.0
webpack-stats-plugin: 1.1.3
@@ -26531,7 +26528,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
webpack-sources: 1.4.3
minimalistic-assert@1.0.1: {}
@@ -26635,7 +26632,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
monaco-editor: 0.28.1
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
monaco-editor@0.28.1: {}
@@ -26937,7 +26934,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
nwsapi@2.2.7: {}
@@ -27518,7 +27515,7 @@ snapshots:
postcss: 8.4.35
schema-utils: 3.3.0
semver: 7.6.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
postcss-loader@5.3.0(postcss@8.4.35)(webpack@5.90.3):
dependencies:
@@ -27526,7 +27523,7 @@ snapshots:
klona: 2.0.6
postcss: 8.4.35
semver: 7.6.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
postcss-merge-longhand@5.1.7(postcss@8.4.35):
dependencies:
@@ -27786,12 +27783,6 @@ snapshots:
object.assign: 4.1.5
reflect.ownkeys: 0.2.0
prop-types@15.7.2:
dependencies:
loose-envify: 1.4.0
object-assign: 4.1.1
react-is: 16.13.1
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@@ -28007,7 +27998,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
rc@1.2.8:
dependencies:
@@ -28042,7 +28033,7 @@ snapshots:
shell-quote: 1.7.2
strip-ansi: 6.0.0
text-table: 0.2.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
optionalDependencies:
typescript: 5.2.2
transitivePeerDependencies:
@@ -28223,10 +28214,10 @@ snapshots:
react: 16.14.0
react-dom: 16.14.0(react@16.14.0)
react-youtube@7.14.0(react@16.14.0):
react-youtube@10.1.0(react@16.14.0):
dependencies:
fast-deep-equal: 3.1.3
prop-types: 15.7.2
prop-types: 15.8.1
react: 16.14.0
youtube-player: 5.5.2
transitivePeerDependencies:
@@ -29552,7 +29543,7 @@ snapshots:
dependencies:
loader-utils: 2.0.4
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
style-to-object@0.3.0:
dependencies:
@@ -29752,7 +29743,7 @@ snapshots:
term-size@2.2.1: {}
terser-webpack-plugin@5.3.10(webpack@5.90.3):
terser-webpack-plugin@5.3.10(webpack@5.90.3(webpack-cli@4.10.0)):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1
@@ -29761,6 +29752,15 @@ snapshots:
terser: 5.28.1
webpack: 5.90.3(webpack-cli@4.10.0)
terser-webpack-plugin@5.3.10(webpack@5.90.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.25
jest-worker: 27.5.1
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.28.1
webpack: 5.90.3
terser-webpack-plugin@5.3.9(webpack@5.90.3):
dependencies:
'@jridgewell/trace-mapping': 0.3.22
@@ -29768,7 +29768,7 @@ snapshots:
schema-utils: 3.3.0
serialize-javascript: 6.0.1
terser: 5.20.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
terser@5.20.0:
dependencies:
@@ -30418,7 +30418,7 @@ snapshots:
loader-utils: 2.0.4
mime-types: 2.1.35
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
optionalDependencies:
file-loader: 6.2.0(webpack@5.90.3)
@@ -30633,9 +30633,9 @@ snapshots:
webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3):
dependencies:
'@discoveryjs/json-ext': 0.5.7
'@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0)(webpack@5.90.3)
'@webpack-cli/info': 1.5.0(webpack-cli@4.10.0)
'@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0)
'@webpack-cli/configtest': 1.2.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))(webpack@5.90.3(webpack-cli@4.10.0))
'@webpack-cli/info': 1.5.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))
'@webpack-cli/serve': 1.7.0(webpack-cli@4.10.0(webpack-bundle-analyzer@4.10.1)(webpack@5.90.3))
colorette: 2.0.20
commander: 7.2.0
cross-spawn: 7.0.3
@@ -30656,7 +30656,7 @@ snapshots:
mime-types: 2.1.35
range-parser: 1.2.1
schema-utils: 3.3.0
webpack: 5.90.3(webpack-cli@4.10.0)
webpack: 5.90.3
webpack-merge@5.9.0:
dependencies:
@@ -30678,6 +30678,37 @@ snapshots:
transitivePeerDependencies:
- supports-color
webpack@5.90.3:
dependencies:
'@types/eslint-scope': 3.7.5
'@types/estree': 1.0.5
'@webassemblyjs/ast': 1.11.6
'@webassemblyjs/wasm-edit': 1.11.6
'@webassemblyjs/wasm-parser': 1.11.6
acorn: 8.11.3
acorn-import-assertions: 1.9.0(acorn@8.11.3)
browserslist: 4.23.0
chrome-trace-event: 1.0.3
enhanced-resolve: 5.15.0
es-module-lexer: 1.3.1
eslint-scope: 5.1.1
events: 3.3.0
glob-to-regexp: 0.4.1
graceful-fs: 4.2.11
json-parse-even-better-errors: 2.3.1
loader-runner: 4.3.0
mime-types: 2.1.35
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.10(webpack@5.90.3)
watchpack: 2.4.0
webpack-sources: 3.2.3
transitivePeerDependencies:
- '@swc/core'
- esbuild
- uglify-js
webpack@5.90.3(webpack-cli@4.10.0):
dependencies:
'@types/eslint-scope': 3.7.5
@@ -30701,7 +30732,7 @@ snapshots:
neo-async: 2.6.2
schema-utils: 3.3.0
tapable: 2.2.1
terser-webpack-plugin: 5.3.10(webpack@5.90.3)
terser-webpack-plugin: 5.3.10(webpack@5.90.3(webpack-cli@4.10.0))
watchpack: 2.4.0
webpack-sources: 3.2.3
optionalDependencies: