mirror of
https://github.com/sergeyyarkov/educt-client.git
synced 2026-10-11 10:19:22 +03:00
feat: upload video to lesson from src link
This commit is contained in:
parent
b4a94e3da5
commit
f098b2d096
53
package-lock.json
generated
53
package-lock.json
generated
@ -31,7 +31,7 @@
|
|||||||
"react-player": "^2.9.0",
|
"react-player": "^2.9.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spinners": "^0.11.0",
|
"react-spinners": "^0.11.0",
|
||||||
"react-virtuoso": "^2.2.8",
|
"react-virtuoso": "^2.13.1",
|
||||||
"socket.io-client": "^4.4.1",
|
"socket.io-client": "^4.4.1",
|
||||||
"validator": "^13.6.0",
|
"validator": "^13.6.0",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
@ -1988,11 +1988,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@virtuoso.dev/react-urx": {
|
"node_modules/@virtuoso.dev/react-urx": {
|
||||||
"version": "0.2.8",
|
"version": "0.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/@virtuoso.dev/react-urx/-/react-urx-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz",
|
||||||
"integrity": "sha512-QV9ZJ9Yla88ySgN+DuHLDYoQa0UDRrEq88sLqQESZUWzLughrkpg1BLmQT7FuiU2M78A+rQ9DR3GBlWrJIx+Bw==",
|
"integrity": "sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@virtuoso.dev/urx": "^0.2.8"
|
"@virtuoso.dev/urx": "^0.2.13"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
@ -2002,9 +2002,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@virtuoso.dev/urx": {
|
"node_modules/@virtuoso.dev/urx": {
|
||||||
"version": "0.2.8",
|
"version": "0.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/@virtuoso.dev/urx/-/urx-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@virtuoso.dev/urx/-/urx-0.2.13.tgz",
|
||||||
"integrity": "sha512-40uiTn5z1J/7at9Ao/aesyFhE7sS1cpb2MrbP/JSS6dcLMsQJcAZGedCY3bKkyjANH9DX/ciUpkpmT3TXmUBOQ=="
|
"integrity": "sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw=="
|
||||||
},
|
},
|
||||||
"node_modules/@vitejs/plugin-react": {
|
"node_modules/@vitejs/plugin-react": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
@ -4849,18 +4849,19 @@
|
|||||||
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
"integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="
|
||||||
},
|
},
|
||||||
"node_modules/react-virtuoso": {
|
"node_modules/react-virtuoso": {
|
||||||
"version": "2.2.8",
|
"version": "2.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-2.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-2.13.1.tgz",
|
||||||
"integrity": "sha512-DArJ29ZrhdygdLKdXs/GB3VNDxaAF7YjdgIjlWPuK6UsV848hNmtw9px3LPdcQBErxaz7XCtHNfc7yInX+/M7g==",
|
"integrity": "sha512-Wek6hYPYU7W1r9fGEF1/sMrFOH8VV8PW64fCRxx9OSSlq5q+5Cp0uf+7eP0jFWqpZtC2DrqFoj+seZXjkiyHrA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@virtuoso.dev/react-urx": "^0.2.8",
|
"@virtuoso.dev/react-urx": "^0.2.12",
|
||||||
"@virtuoso.dev/urx": "^0.2.8"
|
"@virtuoso.dev/urx": "^0.2.12"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16"
|
"react": ">=16 || >=17 || >= 18",
|
||||||
|
"react-dom": ">=16 || >=17 || >= 18"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/redux": {
|
"node_modules/redux": {
|
||||||
@ -7131,17 +7132,17 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@virtuoso.dev/react-urx": {
|
"@virtuoso.dev/react-urx": {
|
||||||
"version": "0.2.8",
|
"version": "0.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/@virtuoso.dev/react-urx/-/react-urx-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@virtuoso.dev/react-urx/-/react-urx-0.2.13.tgz",
|
||||||
"integrity": "sha512-QV9ZJ9Yla88ySgN+DuHLDYoQa0UDRrEq88sLqQESZUWzLughrkpg1BLmQT7FuiU2M78A+rQ9DR3GBlWrJIx+Bw==",
|
"integrity": "sha512-MY0ugBDjFb5Xt8v2HY7MKcRGqw/3gTpMlLXId2EwQvYJoC8sP7nnXjAxcBtTB50KTZhO0SbzsFimaZ7pSdApwA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@virtuoso.dev/urx": "^0.2.8"
|
"@virtuoso.dev/urx": "^0.2.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@virtuoso.dev/urx": {
|
"@virtuoso.dev/urx": {
|
||||||
"version": "0.2.8",
|
"version": "0.2.13",
|
||||||
"resolved": "https://registry.npmjs.org/@virtuoso.dev/urx/-/urx-0.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/@virtuoso.dev/urx/-/urx-0.2.13.tgz",
|
||||||
"integrity": "sha512-40uiTn5z1J/7at9Ao/aesyFhE7sS1cpb2MrbP/JSS6dcLMsQJcAZGedCY3bKkyjANH9DX/ciUpkpmT3TXmUBOQ=="
|
"integrity": "sha512-iirJNv92A1ZWxoOHHDYW/1KPoi83939o83iUBQHIim0i3tMeSKEh+bxhJdTHQ86Mr4uXx9xGUTq69cp52ZP8Xw=="
|
||||||
},
|
},
|
||||||
"@vitejs/plugin-react": {
|
"@vitejs/plugin-react": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
@ -9200,12 +9201,12 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-virtuoso": {
|
"react-virtuoso": {
|
||||||
"version": "2.2.8",
|
"version": "2.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-2.2.8.tgz",
|
"resolved": "https://registry.npmjs.org/react-virtuoso/-/react-virtuoso-2.13.1.tgz",
|
||||||
"integrity": "sha512-DArJ29ZrhdygdLKdXs/GB3VNDxaAF7YjdgIjlWPuK6UsV848hNmtw9px3LPdcQBErxaz7XCtHNfc7yInX+/M7g==",
|
"integrity": "sha512-Wek6hYPYU7W1r9fGEF1/sMrFOH8VV8PW64fCRxx9OSSlq5q+5Cp0uf+7eP0jFWqpZtC2DrqFoj+seZXjkiyHrA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@virtuoso.dev/react-urx": "^0.2.8",
|
"@virtuoso.dev/react-urx": "^0.2.12",
|
||||||
"@virtuoso.dev/urx": "^0.2.8"
|
"@virtuoso.dev/urx": "^0.2.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"redux": {
|
"redux": {
|
||||||
|
|||||||
@ -32,7 +32,7 @@
|
|||||||
"react-player": "^2.9.0",
|
"react-player": "^2.9.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"react-spinners": "^0.11.0",
|
"react-spinners": "^0.11.0",
|
||||||
"react-virtuoso": "^2.2.8",
|
"react-virtuoso": "^2.13.1",
|
||||||
"socket.io-client": "^4.4.1",
|
"socket.io-client": "^4.4.1",
|
||||||
"validator": "^13.6.0",
|
"validator": "^13.6.0",
|
||||||
"yup": "^0.32.9"
|
"yup": "^0.32.9"
|
||||||
|
|||||||
13
src/App.tsx
13
src/App.tsx
@ -18,6 +18,19 @@ import { SocketContext } from './contexts';
|
|||||||
import { HistoryMessageType, UserOnlineListType, UserSeesionType } from './types';
|
import { HistoryMessageType, UserOnlineListType, UserSeesionType } from './types';
|
||||||
import { useToast } from '@chakra-ui/react';
|
import { useToast } from '@chakra-ui/react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Virtuoso's resize observer can this error,
|
||||||
|
* which is caught by DnD and aborts dragging.
|
||||||
|
*/
|
||||||
|
window.addEventListener('error', e => {
|
||||||
|
if (
|
||||||
|
e.message === 'ResizeObserver loop completed with undelivered notifications.' ||
|
||||||
|
e.message === 'ResizeObserver loop limit exceeded'
|
||||||
|
) {
|
||||||
|
e.stopImmediatePropagation();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
const {
|
const {
|
||||||
userStore,
|
userStore,
|
||||||
|
|||||||
@ -58,9 +58,10 @@ const CourseForm: React.FC<CourseFormPropsType> = ({ onSubmit, buttonLabel, isLo
|
|||||||
*/
|
*/
|
||||||
const loadUsersOptions = async (): Promise<OptionType[] | undefined> => {
|
const loadUsersOptions = async (): Promise<OptionType[] | undefined> => {
|
||||||
try {
|
try {
|
||||||
const users = await UserServiceInstance.fetchAll({ limit: 12, role: UserRoleEnum.TEACHER });
|
const teachers = await UserServiceInstance.fetchAll({ role: UserRoleEnum.TEACHER });
|
||||||
|
const admins = await UserServiceInstance.fetchAll({ role: UserRoleEnum.ADMIN });
|
||||||
|
|
||||||
return users.data.map(user => ({
|
return teachers.data.concat(admins.data).map(user => ({
|
||||||
label: user.fullname,
|
label: user.fullname,
|
||||||
value: user.id,
|
value: user.id,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@ -27,6 +27,7 @@ const CreateLessonForm: React.FC = () => {
|
|||||||
description: data.description,
|
description: data.description,
|
||||||
duration: data.duration,
|
duration: data.duration,
|
||||||
video: data.video,
|
video: data.video,
|
||||||
|
linked_video_url: data.linked_video_url,
|
||||||
materials: Array.from(data.materials || []),
|
materials: Array.from(data.materials || []),
|
||||||
course_id,
|
course_id,
|
||||||
});
|
});
|
||||||
|
|||||||
@ -27,6 +27,7 @@ const EditLessonForm: React.FC<EditLessonFormPropsType> = ({ lesson }) => {
|
|||||||
title: lesson.title,
|
title: lesson.title,
|
||||||
description: lesson.description,
|
description: lesson.description,
|
||||||
duration: lesson.duration,
|
duration: lesson.duration,
|
||||||
|
linked_video_url: lesson.linked_video_url || undefined,
|
||||||
video: undefined,
|
video: undefined,
|
||||||
materials: undefined,
|
materials: undefined,
|
||||||
},
|
},
|
||||||
@ -41,6 +42,7 @@ const EditLessonForm: React.FC<EditLessonFormPropsType> = ({ lesson }) => {
|
|||||||
description: data.description,
|
description: data.description,
|
||||||
video: data.video,
|
video: data.video,
|
||||||
duration: data.duration,
|
duration: data.duration,
|
||||||
|
linked_video_url: data.linked_video_url,
|
||||||
materials: data.materials?.length !== 0 ? Array.from(data.materials || []) : null,
|
materials: data.materials?.length !== 0 ? Array.from(data.materials || []) : null,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import {
|
||||||
FormControl,
|
FormControl,
|
||||||
FormHelperText,
|
FormHelperText,
|
||||||
@ -26,6 +26,7 @@ export type InputFields = {
|
|||||||
description: string;
|
description: string;
|
||||||
duration: string;
|
duration: string;
|
||||||
video: File;
|
video: File;
|
||||||
|
linked_video_url?: string | undefined;
|
||||||
materials?: FileList | undefined;
|
materials?: FileList | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,6 +54,8 @@ const LessonForm: React.FC<LessonFormPropsType> = ({
|
|||||||
formState: { errors, isDirty },
|
formState: { errors, isDirty },
|
||||||
} = reactHookForm;
|
} = reactHookForm;
|
||||||
|
|
||||||
|
const [isLinkedVideoUrl, setIsLinkedVideoUrl] = useState<boolean>(false);
|
||||||
|
|
||||||
const watchDescription = watch('description');
|
const watchDescription = watch('description');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -85,7 +88,19 @@ const LessonForm: React.FC<LessonFormPropsType> = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
|
||||||
<FormControl isRequired id='video'>
|
{isLinkedVideoUrl ? (
|
||||||
|
<FormControl id='linked_video_url'>
|
||||||
|
<FormLabel>Video URL</FormLabel>
|
||||||
|
<Input placeholder='Link to video source. (YouTube, Vimeo, .mp4)' {...register('linked_video_url')} />
|
||||||
|
<Text as='small' color='red.500'>
|
||||||
|
{errors.linked_video_url?.message}
|
||||||
|
</Text>
|
||||||
|
<FormHelperText textDecoration={'underline'} cursor='pointer' onClick={() => setIsLinkedVideoUrl(false)}>
|
||||||
|
choose from files
|
||||||
|
</FormHelperText>
|
||||||
|
</FormControl>
|
||||||
|
) : (
|
||||||
|
<FormControl id='video'>
|
||||||
<FormLabel>Video</FormLabel>
|
<FormLabel>Video</FormLabel>
|
||||||
<Controller
|
<Controller
|
||||||
control={control}
|
control={control}
|
||||||
@ -94,6 +109,7 @@ const LessonForm: React.FC<LessonFormPropsType> = ({
|
|||||||
<VideoUploader
|
<VideoUploader
|
||||||
onChange={file => onChange(file)}
|
onChange={file => onChange(file)}
|
||||||
file={file}
|
file={file}
|
||||||
|
setIsLinkedVideoUrl={setIsLinkedVideoUrl}
|
||||||
preloadedVideoUrl={preloadedVideo && constants.BACKEND_URL + preloadedVideo.url}
|
preloadedVideoUrl={preloadedVideo && constants.BACKEND_URL + preloadedVideo.url}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
@ -108,6 +124,7 @@ const LessonForm: React.FC<LessonFormPropsType> = ({
|
|||||||
.join(', ')}
|
.join(', ')}
|
||||||
</FormHelperText>
|
</FormHelperText>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
|
)}
|
||||||
|
|
||||||
<FormControl isRequired id='duration'>
|
<FormControl isRequired id='duration'>
|
||||||
<FormLabel>Video duration</FormLabel>
|
<FormLabel>Video duration</FormLabel>
|
||||||
|
|||||||
@ -18,7 +18,8 @@ const CreateLessonFormSchema = yup
|
|||||||
.test('fileSize', `File size is too large, maximum size is ${helpers.transformBytes(VIDEO_FILE_SIZE)}`, value =>
|
.test('fileSize', `File size is too large, maximum size is ${helpers.transformBytes(VIDEO_FILE_SIZE)}`, value =>
|
||||||
!value ? true : value.size <= VIDEO_FILE_SIZE
|
!value ? true : value.size <= VIDEO_FILE_SIZE
|
||||||
)
|
)
|
||||||
.required('Video field name is required'),
|
.optional(),
|
||||||
|
linked_video_url: yup.string().matches(new RegExp('^(http|https|ftp)://'), 'URL is incorrect'),
|
||||||
materials: yup
|
materials: yup
|
||||||
.mixed<File[]>()
|
.mixed<File[]>()
|
||||||
.test(
|
.test(
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const Select: React.FC<ISelectPropsType> = props => {
|
|||||||
>
|
>
|
||||||
{options?.find(option => option.value === value)?.label || props?.placeholder || 'Select Option'}
|
{options?.find(option => option.value === value)?.label || props?.placeholder || 'Select Option'}
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList>
|
<MenuList maxH='175px' m='0' p='0' overflowY={'scroll'}>
|
||||||
{options && options.length !== 0 ? (
|
{options && options.length !== 0 ? (
|
||||||
options.map(option => (
|
options.map(option => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -49,6 +49,9 @@ const Select: React.FC<ISelectPropsType> = props => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.onChange(option.value);
|
props.onChange(option.value);
|
||||||
}}
|
}}
|
||||||
|
pb='0'
|
||||||
|
pt='0'
|
||||||
|
minH='35px'
|
||||||
>
|
>
|
||||||
{option.label}
|
{option.label}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { Dispatch, SetStateAction, useEffect, useRef, useState } from 'react';
|
||||||
import { Flex, Box, Icon, Text, Button, Input, AspectRatio, useColorModeValue } from '@chakra-ui/react';
|
import { Flex, Box, Icon, Text, Button, Input, AspectRatio, useColorModeValue } from '@chakra-ui/react';
|
||||||
import { MdUpload } from 'react-icons/md';
|
import { MdLink, MdUpload } from 'react-icons/md';
|
||||||
import { VideoSupportedFormatsEnum } from '@educt/enums';
|
import { VideoSupportedFormatsEnum } from '@educt/enums';
|
||||||
import { LessonVideoType } from '@educt/types';
|
import { LessonVideoType } from '@educt/types';
|
||||||
|
|
||||||
@ -8,9 +8,15 @@ type VideoUploaderPropsType = {
|
|||||||
onChange: (file: File) => void;
|
onChange: (file: File) => void;
|
||||||
file?: File | LessonVideoType | undefined;
|
file?: File | LessonVideoType | undefined;
|
||||||
preloadedVideoUrl?: string | undefined;
|
preloadedVideoUrl?: string | undefined;
|
||||||
|
setIsLinkedVideoUrl?: Dispatch<SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
const VideoUploader: React.FC<VideoUploaderPropsType> = ({ onChange, file, preloadedVideoUrl }) => {
|
const VideoUploader: React.FC<VideoUploaderPropsType> = ({
|
||||||
|
onChange,
|
||||||
|
file,
|
||||||
|
preloadedVideoUrl,
|
||||||
|
setIsLinkedVideoUrl,
|
||||||
|
}) => {
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
const videoRef = useRef<HTMLVideoElement>(null);
|
const videoRef = useRef<HTMLVideoElement>(null);
|
||||||
const [videoObjectUrl, setVideoObjectUrl] = useState<string | undefined>(undefined);
|
const [videoObjectUrl, setVideoObjectUrl] = useState<string | undefined>(undefined);
|
||||||
@ -42,11 +48,23 @@ const VideoUploader: React.FC<VideoUploaderPropsType> = ({ onChange, file, prelo
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ChangeVideoButton = () => (
|
const ChangeVideoButton = () => (
|
||||||
<Button onClick={onSelectFileHandler} leftIcon={<MdUpload />} variant={'outline'} size='sm' mt='2' float='right'>
|
<Button mr='1' onClick={onSelectFileHandler} leftIcon={<MdUpload />} variant={'outline'} size='sm' mt='2'>
|
||||||
Change video
|
Change video
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const ChangeUrlVideoButton = () => (
|
||||||
|
<Button
|
||||||
|
onClick={() => setIsLinkedVideoUrl && setIsLinkedVideoUrl(true)}
|
||||||
|
leftIcon={<MdLink />}
|
||||||
|
variant={'outline'}
|
||||||
|
size='sm'
|
||||||
|
mt='2'
|
||||||
|
>
|
||||||
|
From URL
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (videoRef.current) {
|
if (videoRef.current) {
|
||||||
videoRef.current.load();
|
videoRef.current.load();
|
||||||
@ -91,9 +109,14 @@ const VideoUploader: React.FC<VideoUploaderPropsType> = ({ onChange, file, prelo
|
|||||||
</Text>
|
</Text>
|
||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
<Button onClick={onSelectFileHandler} mt='6'>
|
<Flex mt='4'>
|
||||||
|
<Button size='sm' onClick={onSelectFileHandler} mr='2'>
|
||||||
Select file
|
Select file
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button size='sm' onClick={() => setIsLinkedVideoUrl && setIsLinkedVideoUrl(true)}>
|
||||||
|
From URL
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<Box>
|
<Box>
|
||||||
@ -102,7 +125,10 @@ const VideoUploader: React.FC<VideoUploaderPropsType> = ({ onChange, file, prelo
|
|||||||
<source src={preloadedVideoUrl} />
|
<source src={preloadedVideoUrl} />
|
||||||
</video>
|
</video>
|
||||||
</AspectRatio>
|
</AspectRatio>
|
||||||
|
<Flex justifyContent={'flex-end'}>
|
||||||
<ChangeVideoButton />
|
<ChangeVideoButton />
|
||||||
|
<ChangeUrlVideoButton />
|
||||||
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -57,6 +57,7 @@ export interface ILesson {
|
|||||||
progress: LessonProgress;
|
progress: LessonProgress;
|
||||||
content: ILessonContent;
|
content: ILessonContent;
|
||||||
video?: LessonVideoType | undefined | null;
|
video?: LessonVideoType | undefined | null;
|
||||||
|
linked_video_url?: string | null;
|
||||||
created_at: string;
|
created_at: string;
|
||||||
updated_at: string;
|
updated_at: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -138,6 +138,8 @@ const LessonPage: React.FC<IPageProps> = () => {
|
|||||||
|
|
||||||
if (me === null) return <LoadingPage />;
|
if (me === null) return <LoadingPage />;
|
||||||
|
|
||||||
|
const videoURL = lesson.video ? constants.BACKEND_URL + lesson.video.url : lesson.linked_video_url;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Page>
|
<Page>
|
||||||
<Helmet>
|
<Helmet>
|
||||||
@ -153,13 +155,13 @@ const LessonPage: React.FC<IPageProps> = () => {
|
|||||||
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }}>
|
<Grid templateColumns={{ base: '1fr', xl: '3fr 1fr' }}>
|
||||||
<GridItem>
|
<GridItem>
|
||||||
<Box overflowY='hidden'>
|
<Box overflowY='hidden'>
|
||||||
{lesson.video ? (
|
{lesson.video || lesson.linked_video_url ? (
|
||||||
<ReactPlayer
|
<ReactPlayer
|
||||||
style={{ backgroundColor: '#000' }}
|
style={{ backgroundColor: '#000' }}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
height={'547px'}
|
height={'547px'}
|
||||||
controls
|
controls
|
||||||
url={constants.BACKEND_URL + lesson.video.url}
|
url={videoURL || ''}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<Flex h='547px' justifyContent={'center'} alignItems={'center'}>
|
<Flex h='547px' justifyContent={'center'} alignItems={'center'}>
|
||||||
|
|||||||
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
@ -207,6 +207,7 @@ export type CreateLessonParamsType = {
|
|||||||
duration: string;
|
duration: string;
|
||||||
course_id: string;
|
course_id: string;
|
||||||
video: File;
|
video: File;
|
||||||
|
linked_video_url?: string | undefined;
|
||||||
materials?: File[] | undefined;
|
materials?: File[] | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -216,6 +217,7 @@ export type UpdateLessonParamsType = {
|
|||||||
duration?: string | undefined;
|
duration?: string | undefined;
|
||||||
course_id?: string | undefined;
|
course_id?: string | undefined;
|
||||||
video?: File | undefined;
|
video?: File | undefined;
|
||||||
|
linked_video_url?: string | undefined;
|
||||||
materials?: File[] | undefined | null;
|
materials?: File[] | undefined | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user