diff --git a/.eslintrc.js b/.eslintrc.js index c7389a095..21b14a9e1 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,10 +2,6 @@ module.exports = { extends: [ "@opencast/eslint-config-ts-react", ], - rules: { - // Currently 120 warnings. - "@typescript-eslint/no-explicit-any": "off", - }, overrides: [ { files: ["./*.js"], diff --git a/src/config.ts b/src/config.ts index 4341e3d5d..714e6b0f9 100644 --- a/src/config.ts +++ b/src/config.ts @@ -136,10 +136,15 @@ export const init = async () => { // Get settings from URL query. const urlParams = new URLSearchParams(window.location.search); - const rawUrlSettings = {}; + type UrlSettings = { + [key: string]: string | UrlSettings; + } + + const rawUrlSettings: UrlSettings = {}; urlParams.forEach((value, key) => { // Create empty objects for full path (if the key contains ".") and set // the value at the end. + // eslint-disable-next-line @typescript-eslint/no-explicit-any let obj: { [k: string]: any; } = rawUrlSettings; if (key.startsWith("opencast.") || key === "allowedCallbackPrefixes") { return; @@ -232,9 +237,11 @@ const loadContextSettings = async () => { * `srcDescription` is just a string for error messages specifying where `obj` * comes from. * */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any const validate = (obj: Record | null, allowParse: boolean, src: string, sourceDescription: string) => { // Validates `obj` with `schema`. `path` is the current path used for error // messages. + // eslint-disable-next-line @typescript-eslint/no-explicit-any const validate = (schema: any, obj: Record | null, path: string) => { if (typeof schema === "function") { return validateValue(schema, obj, path); @@ -246,7 +253,9 @@ const validate = (obj: Record | null, allowParse: boolean, src: str // Validate a settings value with a validation function. Returns the final // value of the setting or `null` if it should be ignored. const validateValue = ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any validation: (arg0: any, arg1: boolean, arg2: string) => any, + // eslint-disable-next-line @typescript-eslint/no-explicit-any value: Record | null, path: string ) => { @@ -264,9 +273,11 @@ const validate = (obj: Record | null, allowParse: boolean, src: str // Validate a settings object/namespace. `schema` and `obj` need to be // objects. + // eslint-disable-next-line @typescript-eslint/no-explicit-any const validateObj = (schema: any, obj: Record | null, path: string) => { // We iterate through all keys of the given settings object, checking if // each key is valid and recursively validating the value of that key. + // eslint-disable-next-line @typescript-eslint/no-explicit-any const out: { [k: string]: any; } = {}; for (const key in obj) { const newPath = path ? `${path}.${key}` : key; @@ -294,11 +305,13 @@ const validate = (obj: Record | null, allowParse: boolean, src: str // Validation functions for different types. const types = { - "string": (v: any, _allowParse: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + "string": (v: string, _allowParse: any) => { if (typeof v !== "string") { throw new Error("is not a string, but should be"); } }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any "boolean": (v: string, allowParse: any) => { if (typeof v === "boolean") { return; @@ -316,7 +329,8 @@ const types = { throw new Error("is not a boolean"); } }, - "array": (v: any, _allowParse: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + "array": (v: string | [], _allowParse: any) => { if (!Array.isArray(v)) { throw new Error("is not an array, but should be"); } @@ -326,7 +340,8 @@ const types = { } } }, - "map": (v: any, _allowParse: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + "map": (v: { [k: string]: string }, _allowParse: any) => { for (const key in v) { if (typeof key !== "string") { throw new Error("is not a string, but should be"); @@ -336,6 +351,7 @@ const types = { } } }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any "objectsWithinObjects": (v: any, _allowParse: any) => { for (const catalogName in v) { if (typeof catalogName !== "string") { @@ -405,4 +421,5 @@ const merge = (a: iSettings, b: iSettings) => { return deepmerge(a, b, { arrayMerge }); }; merge.all = (array: object[]) => deepmerge.all(array, { arrayMerge }); +// eslint-disable-next-line @typescript-eslint/no-explicit-any const arrayMerge = (_destinationArray: any, sourceArray: any, _options: any) => sourceArray; diff --git a/src/cssStyles.tsx b/src/cssStyles.tsx index 3752e669a..1ddde5004 100644 --- a/src/cssStyles.tsx +++ b/src/cssStyles.tsx @@ -7,6 +7,7 @@ import emotionNormalize from "emotion-normalize"; import { checkFlexGapSupport } from "./util/utilityFunctions"; import { createTheme } from "@mui/material/styles"; import { Theme, useTheme } from "./themes"; +import { StylesConfig } from "react-select"; /** * An emotion component that inserts styles globally @@ -212,9 +213,16 @@ export const errorBoxStyle = (errorStatus: boolean, theme: Theme) => { ); }; -export function selectFieldStyle(theme: Theme) { +type MyOptionType = { + label: string; + value: string; +}; + +type IsMulti = false; + +export function selectFieldStyle(theme: Theme): StylesConfig { return { - control: (provided: any, state: any) => ({ + control: (provided, state) => ({ ...provided, background: theme.menu_background, ...(state.isFocused && { borderColor: theme.metadata_highlight }), @@ -224,28 +232,28 @@ export function selectFieldStyle(theme: Theme) { boxShadow: `0 0 0 1px ${theme.metadata_highlight}`, }, }), - menu: (provided: any) => ({ + menu: provided => ({ ...provided, background: theme.menu_background, outline: theme.dropdown_border, // kill the gap marginTop: 0, }), - singleValue: (provided: any) => ({ + singleValue: provided => ({ ...provided, color: theme.text, }), - multiValue: (provided: any) => ({ + multiValue: provided => ({ ...provided, color: theme.inverted_text, background: theme.multiValue, cursor: "default", }), - multiValueLabel: (provided: any) => ({ + multiValueLabel: provided => ({ ...provided, color: theme.inverted_text, }), - option: (provided: any, state: any) => ({ + option: (provided, state) => ({ ...provided, background: state.isFocused ? theme.focused : theme.menu_background && state.isSelected ? theme.selected : theme.menu_background, @@ -253,23 +261,23 @@ export function selectFieldStyle(theme: Theme) { color: state.isFocused ? theme.focus_text : theme.text && state.isSelected ? theme.selected_text : theme.text, }), - placeholder: (provided: any) => ({ + placeholder: provided => ({ ...provided, color: theme.text, }), - clearIndicator: (provided: any) => ({ + clearIndicator: provided => ({ ...provided, color: theme.indicator_color, }), - dropdownIndicator: (provided: any) => ({ + dropdownIndicator: provided => ({ ...provided, color: theme.indicator_color, }), - valueContainer: (provided: any) => ({ + valueContainer: provided => ({ ...provided, cursor: "text", }), - input: (provided: any) => ({ + input: provided => ({ ...provided, color: theme.text, }), diff --git a/src/import-png.d.ts b/src/import-png.d.ts index 1b8a12db5..15aaa0dae 100644 --- a/src/import-png.d.ts +++ b/src/import-png.d.ts @@ -1,5 +1,6 @@ // Must declare module to be able to serve static images declare module "*.png" { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const value: any; export default value; } diff --git a/src/main/Metadata.tsx b/src/main/Metadata.tsx index 5ef9ffffc..49cfcb3b1 100644 --- a/src/main/Metadata.tsx +++ b/src/main/Metadata.tsx @@ -216,9 +216,11 @@ const Metadata: React.FC = () => { * @param input * @param output */ - const helperHandleArrays = (library: any[] | null, input: any, output: any[]) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const helperHandleArrays = (library: any[] | null, input: string, output: any[]) => { // If the value is hid inside an array, we need to extract it if (Array.isArray(input)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any input.forEach((subArray: any) => { output.push(helperHandleArrays(library, subArray, output)); }); @@ -233,6 +235,7 @@ const Metadata: React.FC = () => { * @param catalogs */ const getInitialValues = (catalogs: Catalog[]) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const initValues: { [n: string]: any; } = {}; catalogs.forEach((catalog: Catalog, catalogIndex: number) => { @@ -244,9 +247,11 @@ const Metadata: React.FC = () => { // Since react-select creates different values if (field.collection) { const library = generateReactSelectLibrary(field); + // eslint-disable-next-line @typescript-eslint/no-explicit-any let searchValue: any = field.value; if (Array.isArray(searchValue)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const result: any[] = []; helperHandleArrays(library, field.value, result); searchValue = result; @@ -270,13 +275,13 @@ const Metadata: React.FC = () => { * Validator for required fields * @param value */ - const required = (value: any) => (value ? undefined : t("metadata.validation.required")); + const required = (value: string) => (value ? undefined : t("metadata.validation.required")); /** * Validator for the duration field * @param value */ - const duration = (value: any) => { + const duration = (value: string) => { const re = /^[0-9][0-9]:[0-9][0-9]:[0-9][0-9]$/; return re.test(value) ? undefined : t("metadata.validation.duration-format"); }; @@ -285,7 +290,7 @@ const Metadata: React.FC = () => { * Validator for the date time fields * @param date */ - const dateTimeValidator = (date: any) => { + const dateTimeValidator = (date: Date | string) => { // Empty field is valid value in Opencast if (!date) { return undefined; @@ -293,7 +298,7 @@ const Metadata: React.FC = () => { let dt = undefined; if (Object.prototype.toString.call(date) === "[object Date]") { - dt = LuxonDateTime.fromJSDate(date); + dt = LuxonDateTime.fromJSDate(date as Date); } if (typeof date === "string") { dt = LuxonDateTime.fromISO(date); @@ -337,6 +342,7 @@ const Metadata: React.FC = () => { * @param value value for the field * @param fieldId String of the form "catalog{catalogIndex}.name" */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const submitSingleField = (value: any, fieldId: string) => { const catalogIndexString = fieldId.substring( fieldId.indexOf("g") + 1, @@ -366,7 +372,11 @@ const Metadata: React.FC = () => { * @param e * @param input */ - const blurWithSubmit = (e: any, input: any) => { + const blurWithSubmit = ( + e: React.FocusEvent | React.FocusEvent, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + input: any + ) => { input.onBlur(e); submitSingleField(input.value, input.name); }; @@ -377,12 +387,15 @@ const Metadata: React.FC = () => { * @param field * @param value */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const parseValue = (field: MetadataField | null, value: any) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any let returnValue: any = value; // Parse values out react-multi-select and put them in an array if (Array.isArray(value)) { returnValue = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any value.forEach((subValue: any) => { returnValue.push(parseValue(null, subValue)); // Pass field as null to avoid each value into an array later on }); @@ -423,12 +436,14 @@ const Metadata: React.FC = () => { * Saves values in redux state and sends them to Opencast * @param values */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const onSubmit = (values: { [x: string]: { [x: string]: any; }; }) => { // For each submitted value, get the catalog it belongs to Object.keys(values).forEach((formCatalogName: string) => { const catalogIndex = parseInt(formCatalogName.replace("catalog", "")); // For each field in the submitted values + // eslint-disable-next-line @typescript-eslint/no-explicit-any Object.keys(values[formCatalogName]).forEach((formFieldName: any) => { // Find the corresponding field index in the redux catalog for (let fieldIndex = 0; fieldIndex < catalogs[catalogIndex].fields.length; fieldIndex++) { @@ -460,7 +475,8 @@ const Metadata: React.FC = () => { const generateReactSelectLibrary = (field: MetadataField) => { if (field.collection) { // For whatever reason react-select uses "value" as their key, which is not at all confusing - const library: [{ value: any, label: any, submitValue: any; }] = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const library: [{ value: any, label: string, submitValue: any; }] = [{ value: "", label: "No value", submitValue: "" }]; Object.entries(field.collection).forEach(([key, value]) => { // // Parse License @@ -502,6 +518,7 @@ const Metadata: React.FC = () => { * @param field * @param input */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const generateComponent = (field: MetadataField, input: any) => { input.id = input.name; if (field.collection) { @@ -602,6 +619,7 @@ const Metadata: React.FC = () => { * can"t handle empty string as a value (which is what Opencast uses to * represent no date/time) */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any const generateComponentWithModifiedInput = (field: MetadataField, input: FieldInputProps) => { if ((field.type === "date" || field.type === "time") && input.value === "") { const { value, ...other } = input; diff --git a/src/main/SubtitleSelect.tsx b/src/main/SubtitleSelect.tsx index f7cd53219..71de6c67d 100644 --- a/src/main/SubtitleSelect.tsx +++ b/src/main/SubtitleSelect.tsx @@ -12,7 +12,7 @@ import { selectSubtitles, setSelectedSubtitleId, setSubtitle } from "../redux/su import { useAppDispatch, useAppSelector } from "../redux/store"; import { setIsDisplayEditView } from "../redux/subtitleSlice"; import { LuPlus } from "react-icons/lu"; -import { Form } from "react-final-form"; +import { withTypes } from "react-final-form"; import { Select } from "mui-rff"; import { useState } from "react"; import { useTranslation } from "react-i18next"; @@ -211,6 +211,12 @@ const SubtitleAddButton: React.FC<{ const [isPlusDisplay, setIsPlusDisplay] = useState(true); + // Form types + interface FormSubmitValues { + selectedSubtitle: string; + } + const { Form } = withTypes(); + // Parse language data into a format the dropdown understands const selectData = () => { const data = []; @@ -222,7 +228,7 @@ const SubtitleAddButton: React.FC<{ return data; }; - const onSubmit = (values: { selectedSubtitle: any; }) => { + const onSubmit = (values: FormSubmitValues) => { // Create new subtitle for the given language const id = values.selectedSubtitle; const relatedSubtitle = subtitlesForDropdown.find(tag => tag.id === id); diff --git a/src/main/Thumbnail.tsx b/src/main/Thumbnail.tsx index 819d04414..67fe85a25 100644 --- a/src/main/Thumbnail.tsx +++ b/src/main/Thumbnail.tsx @@ -34,7 +34,7 @@ import { setCurrentlyAt, } from "../redux/videoSlice"; import { ThemedTooltip } from "./Tooltip"; -import VideoPlayers from "./VideoPlayers"; +import VideoPlayers, { VideoPlayerForwardRef } from "./VideoPlayers"; import VideoControls from "./VideoControls"; @@ -50,7 +50,7 @@ const Thumbnail: React.FC = () => { const originalThumbnails = useAppSelector(selectOriginalThumbnails); // Generate Refs - const generateRefs = React.useRef([]); + const generateRefs = React.useRef<(VideoPlayerForwardRef | null)[]>([]); // Upload Refs const inputRefs = React.useRef<(HTMLInputElement | null)[]>([]); @@ -58,7 +58,7 @@ const Thumbnail: React.FC = () => { // *track: Generate to // *index: Generate from const generate = (track: Track, index: number) => { - const uri = generateRefs.current[index].captureVideo(); + const uri = generateRefs.current[index]?.captureVideo(); dispatch(setThumbnail({ id: track.id, uri: uri })); dispatch(setHasChanges(true)); }; @@ -161,7 +161,7 @@ const Thumbnail: React.FC = () => { */ const ThumbnailTable: React.FC<{ inputRefs: React.MutableRefObject<(HTMLInputElement | null)[]>, - generateRefs: React.MutableRefObject, + generateRefs: React.MutableRefObject<(VideoPlayerForwardRef | null)[]>, generate: (track: Track, index: number) => void, upload: (index: number) => void, uploadCallback: (event: React.ChangeEvent, track: Track) => void, @@ -234,7 +234,7 @@ const ThumbnailTableRow: React.FC<{ track: Track, index: number, inputRefs: React.MutableRefObject<(HTMLInputElement | null)[]>, - generateRef: any, + generateRef: VideoPlayerForwardRef | null, generate: (track: Track, index: number) => void, upload: (index: number) => void, uploadCallback: (event: React.ChangeEvent, track: Track) => void, @@ -244,8 +244,11 @@ const ThumbnailTableRow: React.FC<{ const { t } = useTranslation(); const theme = useTheme(); - // The "+40" comes from padding that is not included in the "getWidth" function - const videoWidth = generateRef ? generateRef.getWidth() + 40 : undefined; + const videoWidth = generateRef ? + // The "+40" comes from padding that is not included in the "getWidth" function + generateRef.getWidth() + 40 : + // Random default + 440; const renderPriority = (thumbnailPriority: number) => { if (isNaN(thumbnailPriority)) { diff --git a/src/main/Timeline.tsx b/src/main/Timeline.tsx index aabb7ebda..4c98241f5 100644 --- a/src/main/Timeline.tsx +++ b/src/main/Timeline.tsx @@ -37,7 +37,7 @@ const Timeline: React.FC<{ styleByActiveSegment?: boolean, selectCurrentlyAt: (state: RootState) => number, selectIsPlaying: (state: RootState) => boolean, - setClickTriggered: ActionCreatorWithPayload, + setClickTriggered: ActionCreatorWithPayload, setCurrentlyAt: ActionCreatorWithPayload, setIsPlaying: ActionCreatorWithPayload, }> = ({ @@ -440,6 +440,7 @@ export const Waveforms: React.FC<{ timelineHeight: number; }> = ({ timelineHeigh const file = new File([blob], blob); // Start waveform worker with blob + // eslint-disable-next-line @typescript-eslint/no-explicit-any const waveformWorker: any = new Waveform({ type: "img", width: "2000", height: "230", samples: 100000, media: file, }); @@ -450,7 +451,7 @@ export const Waveforms: React.FC<{ timelineHeight: number; }> = ({ timelineHeigh }; // When done, save path to generated waveform img - waveformWorker.oncomplete = (image: any, _numSamples: any) => { + waveformWorker.oncomplete = (image: string, _numSamples: number) => { newImages.push(image); waveformsProcessed++; // If all images are generated, rerender diff --git a/src/main/VideoPlayers.tsx b/src/main/VideoPlayers.tsx index aa83fb322..5af37414b 100644 --- a/src/main/VideoPlayers.tsx +++ b/src/main/VideoPlayers.tsx @@ -30,15 +30,16 @@ import { useTranslation } from "react-i18next"; import { sleep } from "./../util/utilityFunctions"; import { RootState } from "../redux/store"; -import { ActionCreatorWithPayload } from "@reduxjs/toolkit"; +import { ActionCreatorWithPayload, AsyncThunk } from "@reduxjs/toolkit"; import { useTheme } from "../themes"; import { backgroundBoxStyle, flexGapReplacementStyle } from "../cssStyles"; import { BaseReactPlayerProps } from "react-player/base"; +import { AsyncThunkConfig } from "@reduxjs/toolkit/dist/createAsyncThunk"; const VideoPlayers: React.FC<{ - refs?: React.MutableRefObject, + refs?: React.MutableRefObject<(VideoPlayerForwardRef | null)[]>, widthInPercent?: number, maxHeightInPixel?: number; }> = ({ @@ -100,34 +101,39 @@ const VideoPlayers: React.FC<{ ); }; +export interface VideoPlayerForwardRef { + captureVideo: () => string | undefined, + getWidth: () => number, +} + +interface VideoPlayerProps { + dataKey: number, + url: string | undefined, + isPrimary: boolean, + subtitleUrl: string, + first: boolean, + last: boolean, + selectIsPlaying: (state: RootState) => boolean, + selectIsMuted: (state: RootState) => boolean, + selectVolume: (state: RootState) => number, + selectCurrentlyAtInSeconds: (state: RootState) => number, + selectPreviewTriggered: (state: RootState) => boolean, + selectClickTriggered: (state: RootState) => boolean, + selectAspectRatio: (state: RootState) => number, + setIsPlaying: ActionCreatorWithPayload, + setPreviewTriggered: ActionCreatorWithPayload, + setClickTriggered: ActionCreatorWithPayload, + setCurrentlyAt: ActionCreatorWithPayload | AsyncThunk, + setAspectRatio: ActionCreatorWithPayload<{ dataKey: number; } & { width: number, height: number; }, string>, +} + /** * A single video player * @param {string} url - URL to load video from * @param {boolean} isPrimary - If the player is the main control */ -export const VideoPlayer = React.forwardRef( - (props: { - dataKey: number, - url: string | undefined, - isPrimary: boolean, - subtitleUrl: string, - first: boolean, - last: boolean, - selectIsPlaying: (state: RootState) => boolean, - selectIsMuted: (state: RootState) => boolean, - selectVolume: (state: RootState) => number, - selectCurrentlyAtInSeconds: (state: RootState) => number, - selectPreviewTriggered: (state: RootState) => boolean, - selectClickTriggered: (state: RootState) => boolean, - selectAspectRatio: (state: RootState) => number, - setIsPlaying: ActionCreatorWithPayload, - setPreviewTriggered: ActionCreatorWithPayload, - setClickTriggered: ActionCreatorWithPayload, - setCurrentlyAt: any, - setAspectRatio: ActionCreatorWithPayload<{ dataKey: number; } & { width: number, height: number; }, string>, - }, - forwardRefThing - ) => { +export const VideoPlayer = React.forwardRef( + (props, forwardRefThing) => { const { dataKey, url, diff --git a/src/redux/metadataSlice.ts b/src/redux/metadataSlice.ts index 170434b48..4d7d27678 100644 --- a/src/redux/metadataSlice.ts +++ b/src/redux/metadataSlice.ts @@ -17,7 +17,8 @@ export interface MetadataField { type: string; // irrelevant? value: string, required: boolean, - collection: any | undefined, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + collection: { [key: string]: any } | undefined, } // interface metadata { diff --git a/src/redux/subtitleSlice.ts b/src/redux/subtitleSlice.ts index 0adbd1894..3fff70fc6 100644 --- a/src/redux/subtitleSlice.ts +++ b/src/redux/subtitleSlice.ts @@ -1,7 +1,7 @@ import { Segment, SubtitleCue, SubtitlesInEditor } from "./../types"; -import { createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit"; +import { createAsyncThunk, createSlice, nanoid, PayloadAction } from "@reduxjs/toolkit"; import { roundToDecimalPlace } from "../util/utilityFunctions"; -import type { AppDispatch, RootState } from "../redux/store"; +import type { RootState } from "../redux/store"; import { video } from "./videoSlice"; export interface subtitle { @@ -63,7 +63,7 @@ export const subtitleSlice = createSlice({ setIsPlayPreview: (state, action: PayloadAction) => { state.isPlayPreview = action.payload; }, - setPreviewTriggered: (state, action) => { + setPreviewTriggered: (state, action: PayloadAction) => { state.previewTriggered = action.payload; }, setCurrentlyAt: (state, action: PayloadAction) => { @@ -250,8 +250,8 @@ export const { * Will grab the state from videoState to skip past deleted segment if preview * mode is active. */ -export function setCurrentlyAtAndTriggerPreview(milliseconds: number) { - return (dispatch: AppDispatch, getState: () => RootState) => { +export const setCurrentlyAtAndTriggerPreview = createAsyncThunk("subtitleState/setCurrentlyAtAndTriggerPreview", + async (milliseconds: number, { getState, dispatch }) => { milliseconds = roundToDecimalPlace(milliseconds, 0); if (milliseconds < 0) { @@ -285,7 +285,6 @@ export function setCurrentlyAtAndTriggerPreview(milliseconds: number) { if (triggered) { dispatch(setPreviewTriggered(true)); } - }; -} + }); export default subtitleSlice.reducer; diff --git a/src/redux/videoSlice.ts b/src/redux/videoSlice.ts index fdcbb0aa1..444058957 100644 --- a/src/redux/videoSlice.ts +++ b/src/redux/videoSlice.ts @@ -130,10 +130,10 @@ const videoSlice = createSlice({ setVolume: (state, action: PayloadAction) => { state.volume = action.payload; }, - setPreviewTriggered: (state, action) => { + setPreviewTriggered: (state, action: PayloadAction) => { state.previewTriggered = action.payload; }, - setClickTriggered: (state, action) => { + setClickTriggered: (state, action: PayloadAction) => { state.clickTriggered = action.payload; }, setCurrentlyAt: (state, action: PayloadAction) => {