Skip to content

Commit

Permalink
Merge pull request #33 from parlemonde/features_improvement
Browse files Browse the repository at this point in the history
features improvement
DavidRobertAnsart authored Sep 5, 2024
2 parents a1514a8 + b577cde commit 1fb149e
Showing 18 changed files with 202 additions and 94 deletions.
14 changes: 12 additions & 2 deletions server/controllers/questions.ts
Original file line number Diff line number Diff line change
@@ -255,10 +255,20 @@ questionController.put({ path: '/:id', userType: UserType.CLASS }, async (req, r
question.soundVolume = data.soundVolume !== undefined ? data.soundVolume : question.soundVolume;
const dataStatus = data.status;
logger.info(`dataStatus: ${dataStatus}`);
if (dataStatus !== undefined && dataStatus !== null) {
const previousStatus = question.status;
if (previousStatus === QuestionStatus.STORYBOARD && dataStatus === QuestionStatus.PREMOUNTING) {
question.status = dataStatus;
question.feedback = [QuestionStatus.ONGOING, QuestionStatus.PREMOUNTING].includes(dataStatus) && data.feedback ? data.feedback : null;
question.feedbacks = null;
} else {
if (dataStatus !== undefined && dataStatus !== null) {
question.status = dataStatus;
question.feedbacks =
[QuestionStatus.ONGOING, QuestionStatus.PREMOUNTING].includes(dataStatus) && data.feedback
? [...(question.feedbacks || []), data.feedback]
: question.feedbacks || [];
}
}

await getRepository(Question).save(question);
res.sendJSON(question);
});
11 changes: 9 additions & 2 deletions server/entities/question.ts
Original file line number Diff line number Diff line change
@@ -49,6 +49,13 @@ export class Question implements QuestionInterface {
})
status: QuestionStatus;

@Column({ type: 'varchar', length: 2000, nullable: true })
public feedback: string | null;
@Column({
type: 'text',
nullable: true,
transformer: {
to: (value: string[] | null) => (value ? JSON.stringify(value) : null),
from: (value: string | null) => (value ? JSON.parse(value) : null),
},
})
public feedbacks: string[] | null;
}
11 changes: 6 additions & 5 deletions server/translations/defaultLocales.ts
Original file line number Diff line number Diff line change
@@ -80,6 +80,11 @@ export const locales = {
left: "gauche",
center: "centre",
right: "droite",
black: 'noir',
white: 'blanc',
red: 'rouge',
green: 'vert',
blue: 'bleu',
//--- part4 ---
part4_title: 'Prémontez votre <1>film</1>',
part4_subtitle1: 'Pour chaque séquence vous pouvez écrire et enregistrer une voix-off.',
@@ -107,13 +112,8 @@ export const locales = {
part6_subtitle1: 'À cette étape, vous pouvez pré-visualiser votre diaporama sonore achevé.',
part6_pdf_button: 'Télécharger le storyboard',
part6_mlt_button: 'Télécharger le fichier de montage',
part6_mp4_button: 'Générer votre vidéo',
part6_mp4_download_button: 'Télécharger votre vidéo !',
part6_mp4_generate_button: 'Générer une nouvelle vidéo',
part6_mp4_loading: 'Création de votre vidéo...',
part6_mp4_description_1: 'La génération de votre vidéo peut prendre du temps.',
part6_mp4_description_2: "Vous pouvez quitter et suivre à tout moment l'avancement du montage de votre vidéo sur cette page.",
part6_mp4_description_3: 'Votre vidéo sera disponible pendant 2 jours. Passé ce delai elle sera supprimée.',
part6_mp4_user_disabled: 'Connectez-vous et créez un projet pour générer une vidéo.',
part6_mp4_project_disabled: 'Créez un projet pour générer une vidéo.',
part6_subtitle2:
@@ -297,6 +297,7 @@ export const locales = {
collaboration_form_feedback_btn_feedback: 'Envoyer le retour',
collaboration_form_feedback_btn_ok: 'Valider le travail',
collaboration_form_feedback_error: 'Veuillez renseigner un message de retour.',
collaboration_previous_feedbacks_label: 'Retours précédents',
collaboration_form_feedback_label: 'Retours',
collaboration_form_feedback_placeholder: 'Vos retours (Raccourcir la durée de la séquence, monter le son, ...)',
collaboration_form_feedback_title: 'Travail à vérifier',
6 changes: 3 additions & 3 deletions server/xml/index.ts
Original file line number Diff line number Diff line change
@@ -184,9 +184,9 @@ export function projectToMlt(allQuestions: Question[], project: Project, urlTran
size: fontSize,
weight: '500',
style: 'normal',
fgcolour: '#000000',
bgcolour: '#ffffffff',
olcolour: '#ffffffff',
fgcolour: style.color || '#000000',
bgcolour: style.backgroundColor || '#ffffffff',
olcolour: style.backgroundColor || '#ffffffff',
halign: 'center',
valign: 'middle',
mlt_service: 'dynamictext',
13 changes: 12 additions & 1 deletion src/components/collaboration/FormFeedback.tsx
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ export const FormFeedback: React.FunctionComponent<FormFeedbackProps> = ({ quest
newQuestions[question.index] = {
...newQuestions[question.index],
status,
feedback: feedbackData,
feedbacks: feedbackData ? [...(question.feedbacks ?? []), feedbackData] : question.feedbacks,
};
const updatedProject = updateProject({ questions: newQuestions });
if (updatedProject) {
@@ -87,6 +87,17 @@ export const FormFeedback: React.FunctionComponent<FormFeedbackProps> = ({ quest
{t('collaboration_form_feedback_title')}
</h2>

{question.feedbacks && question.feedbacks.length > 0 && (
<div>
<div style={{ fontSize: '14px' }}>{t('collaboration_previous_feedbacks_label')} :</div>
<ul>
{question.feedbacks.map((feedbackItem, index) => (
<li key={index}>{feedbackItem}</li>
))}
</ul>
</div>
)}

<Field
marginTop="sm"
name="feedback"
12 changes: 11 additions & 1 deletion src/components/create/DiaporamaCard/DiaporamaCard.tsx
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@ export const DiaporamaCard = ({ projectId, questionIndex, sequence, isAuthorized
const [frameIndex, setFrameIndex] = React.useState<'title' | number>(sequence.title !== null ? 'title' : 0);
const [canvasRef, { height: canvasHeight }] = useResizeObserver<HTMLAnchorElement>();

const baseButtonStyle: React.CSSProperties = { width: '100%', height: '100%', pointerEvents: isAuthorized ? 'auto' : 'none' };
const buttonStyle = React.useMemo(() => {
const plan = frameIndex !== 'title' ? (sequence.plans || [])[frameIndex] : undefined;
if (plan && plan.imageUrl) {
@@ -48,6 +47,14 @@ export const DiaporamaCard = ({ projectId, questionIndex, sequence, isAuthorized
}
}, [sequence]);

const baseButtonStyle: React.CSSProperties = {
width: '100%',
height: '100%',
pointerEvents: isAuthorized ? 'auto' : 'none',
backgroundColor: style.backgroundColor || 'white',
color: style.color || 'black',
};

const updateFrameIndex = React.useCallback(() => {
setFrameIndex((prevFrame) => {
let nextFrame: 'title' | number = prevFrame === 'title' ? 0 : prevFrame + 1;
@@ -87,6 +94,9 @@ export const DiaporamaCard = ({ projectId, questionIndex, sequence, isAuthorized
left: `${style.x ?? 15}%`,
top: `${style.y ?? 30}%`,
width: `${style.width ?? 70}%`,
backgroundColor: style.backgroundColor || 'white',
textAlign: style.textAlign || 'center',
color: style.color || 'black',
}
}
>
2 changes: 1 addition & 1 deletion src/components/create/DiaporamaPlayer/DiaporamaPlayer.tsx
Original file line number Diff line number Diff line change
@@ -542,7 +542,7 @@ export const DiaporamaPlayer = ({
setVolume(newValue);
onUpdateVolume(newValue);
}}
max={200}
max={300}
min={0}
orientation="vertical"
/>
10 changes: 9 additions & 1 deletion src/components/create/DiaporamaPlayer/Frame.tsx
Original file line number Diff line number Diff line change
@@ -53,6 +53,9 @@ type FrameProps = {
time: number;
className?: string;
};

type TextAlign = 'left' | 'center' | 'right';

export const Frame = ({ questions, time, className }: FrameProps) => {
const [canvasRef, { height: canvasHeight }] = useResizeObserver<HTMLDivElement>();
const currentFrame = getCurrentFrame(questions, time);
@@ -62,7 +65,11 @@ export const Frame = ({ questions, time, className }: FrameProps) => {
style={{
width: '100%',
height: '100%',
backgroundColor: currentFrame !== null && currentFrame.kind === 'title' ? 'white' : 'unset',
backgroundColor:
currentFrame !== null && currentFrame.kind === 'title' ? (currentFrame.style.backgroundColor as string) || 'white' : 'white',
color: currentFrame !== null && currentFrame.kind === 'title' ? String(currentFrame.style.color) || 'black' : 'black',
textAlign:
currentFrame !== null && currentFrame.kind === 'title' ? (currentFrame.style.textAlign as TextAlign) || 'center' : 'center',
}}
ref={canvasRef}
>
@@ -81,6 +88,7 @@ export const Frame = ({ questions, time, className }: FrameProps) => {
left: `${currentFrame.style.x ?? 15}%`,
top: `${currentFrame.style.y ?? 30}%`,
width: `${currentFrame.style.width ?? 70}%`,
textAlign: (currentFrame.style.textAlign as TextAlign) || 'center',
}
}
>
68 changes: 66 additions & 2 deletions src/components/create/TitleCanvas/TitleCanvas.tsx
Original file line number Diff line number Diff line change
@@ -32,6 +32,8 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
const [fontFamily, setFontFamily] = React.useState<string>(style.fontFamily || 'serif');
const [fontSize, setFontSize] = React.useState<number>(style.fontSize || 8); // %
const [textAlign, setTextAlign] = React.useState<TextAlign>(style.textAlign || 'center');
const [backgroundColor, setBackgroundColor] = React.useState<string>(style.backgroundColor || 'white');
const [color, setColor] = React.useState<string>(style.color || 'black');
// relative pos
const [textXPer, setTextXPer] = React.useState<number>(style.x ?? 15); // %
const [textYPer, setTextYPer] = React.useState<number>(style.y ?? 30); // %
@@ -46,6 +48,8 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
setTextYPer(style.y ?? 35);
setTextWidthPer(style.width || 50);
setTextAlign(style.textAlign || 'center');
setBackgroundColor(style.backgroundColor || 'white');
setColor(style.color || 'black');
}, [title, style]);

const onChangeStyle = (newPartialSyle: Record<string, string | number>) => {
@@ -69,7 +73,7 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
if (textAreaRef.current) {
setTextAreaRefHeight(textAreaRef.current.scrollHeight);
}
}, [titleText, textWidthPer, canvasWidth, fontSize, fontFamily, textAlign]); // update textAreaRefHeight on title change.
}, [titleText, textWidthPer, canvasWidth, fontSize, fontFamily, textAlign, backgroundColor]); // update textAreaRefHeight on title change.

// Absolute pos
const { textX, textY, textWidth } = React.useMemo(
@@ -141,7 +145,42 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {

return (
<div style={{ width: '100%', maxWidth: '600px', margin: '2rem auto' }} ref={canvasRef}>
<KeepRatio ratio={9 / 16} style={{ border: '1px solid grey', borderRadius: '8px' }}>
<div
style={{
display: 'inline-flex',
flexDirection: 'row',
alignItems: 'center',
backgroundColor: PrimaryColor,
color: '#fff',
padding: '0.1rem',
borderTopLeftRadius: '4px',
borderTopRightRadius: '4px',
}}
>
<select
value={backgroundColor}
onChange={(event) => {
const newBackgroundColor = event.target.value;
setBackgroundColor(newBackgroundColor);
onChangeStyle({ backgroundColor: newBackgroundColor });
}}
style={{
margin: '0 0.5rem',
backgroundColor: PrimaryColor,
color: '#fff',
border: 'none',
outline: 'none',
cursor: 'pointer',
}}
>
<option value={'white'}>{t('white')}</option>
<option value={'black'}>{t('black')}</option>
<option value={'#fbe5d3'}>{t('red')}</option>
<option value={'#dad7fe'}>{t('blue')}</option>
<option value={'#e2fbd7'}>{t('green')}</option>
</select>
</div>
<KeepRatio ratio={9 / 16} style={{ border: '1px solid grey', borderRadius: '0 8px 8px 8px', backgroundColor: backgroundColor }}>
<div
style={{
display: 'inline-block',
@@ -209,6 +248,7 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
<option value={8}>{t('medium')}</option>
<option value={10}>{t('big')}</option>
</select>
<div style={{ height: '1rem', width: '1px', backgroundColor: '#fff' }}></div>
<select
value={textAlign}
onChange={(event) => {
@@ -229,6 +269,26 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
<option value={'center'}>{t('center')}</option>
<option value={'right'}>{t('right')}</option>
</select>
<div style={{ height: '1rem', width: '1px', backgroundColor: '#fff' }}></div>
<select
value={color}
onChange={(event) => {
const newColor = event.target.value;
setBackgroundColor(color);
onChangeStyle({ color: newColor });
}}
style={{
margin: '0 0.5rem',
backgroundColor: PrimaryColor,
color: '#fff',
border: 'none',
outline: 'none',
cursor: 'pointer',
}}
>
<option value={'black'}>{t('black')}</option>
<option value={'white'}>{t('white')}</option>
</select>
</div>
<textarea
value={titleText}
@@ -255,6 +315,8 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
fontFamily: fontFamily,
textAlign: textAlign,
resize: 'none',
backgroundColor: backgroundColor,
color: color,
}}
/>
<textarea
@@ -279,6 +341,8 @@ export function TitleCanvas({ title, onChange }: TitleCanvasProps) {
resize: 'none',
whiteSpace: 'pre-wrap',
wordBreak: 'break-word',
backgroundColor: backgroundColor,
color: color,
}}
></textarea>
<div
11 changes: 10 additions & 1 deletion src/components/create/TitleCard/TitleCard.tsx
Original file line number Diff line number Diff line change
@@ -30,7 +30,13 @@ export const TitleCard = ({ projectId, questionIndex, title, onDelete = () => {}
}
}, [title]);
const [canvasRef, { height: canvasHeight }] = useResizeObserver<HTMLAnchorElement>();
const buttonStyle: React.CSSProperties = { width: '100%', height: '100%', pointerEvents: canEdit ? 'auto' : 'none' };
const buttonStyle: React.CSSProperties = {
width: '100%',
height: '100%',
pointerEvents: canEdit ? 'auto' : 'none',
backgroundColor: style ? style.backgroundColor : 'white',
color: style ? style.color : 'black',
};

return (
<Link
@@ -52,6 +58,9 @@ export const TitleCard = ({ projectId, questionIndex, title, onDelete = () => {}
left: `${style.x ?? 15}%`,
top: `${style.y ?? 30}%`,
width: `${style.width ?? 70}%`,
backgroundColor: style.backgroundColor || 'white',
textAlign: style.textAlign || 'center',
color: style.color || 'black',
}
}
>
1 change: 0 additions & 1 deletion src/components/layout/Form/input.module.scss
Original file line number Diff line number Diff line change
@@ -3,7 +3,6 @@
.inputContainer {
display: block;
position: relative;
width: 300px;

&--is-full-width {
width: 100%;
6 changes: 3 additions & 3 deletions src/pages/create/3-storyboard/edit.tsx
Original file line number Diff line number Diff line change
@@ -154,7 +154,7 @@ const EditPlan = () => {
const { user } = React.useContext(userContext);
const isStudent = user?.type === UserType.STUDENT;
const [showButtonFeedback, setShowButtonFeedback] = React.useState(
(isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) as boolean,
(isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) as boolean,
);
const [showFeedback, setShowFeedback] = React.useState(false);
const imageUrl = React.useMemo(() => {
@@ -165,7 +165,7 @@ const EditPlan = () => {
}, [imageBlob, plan]);

React.useEffect(() => {
if (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) {
if (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) {
setShowButtonFeedback(true);
}
}, [isStudent, sequence]);
@@ -531,7 +531,7 @@ const EditPlan = () => {
<FeedbackModal
isOpen={showFeedback}
onClose={() => setShowFeedback(false)}
feedback={sequence && sequence.feedback ? sequence.feedback : ''}
feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''}
/>
</Container>
);
6 changes: 4 additions & 2 deletions src/pages/create/3-storyboard/index.tsx
Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@ const Scenario = ({

const plans = React.useMemo(() => sequence.plans || [], [sequence]);
const [showFeedback, setShowFeedback] = React.useState(false);
const showButtonFeedback = isStudent && sequence.feedback && QuestionStatus.ONGOING === sequence.status;
const showButtonFeedback = isStudent && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status;
const studentColor = COLORS[sequenceIndex];

const { project, isLoading: isProjectLoading, questions, updateProject } = useCurrentProject();
@@ -267,7 +267,9 @@ const Scenario = ({
<FeedbackModal
isOpen={showFeedback}
onClose={() => setShowFeedback(false)}
feedback={sequence && sequence.feedback ? sequence.feedback : ''}
feedback={
sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''
}
/>
</div>
{isCollaborationActive && !isStudent && sequence.status === QuestionStatus.STORYBOARD && (
6 changes: 3 additions & 3 deletions src/pages/create/3-storyboard/title.tsx
Original file line number Diff line number Diff line change
@@ -71,12 +71,12 @@

const isStudent = user?.type === UserType.STUDENT;
const [showButtonFeedback, setShowButtonFeedback] = React.useState(
(isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) as boolean,
(isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) as boolean,
);
const [showFeedback, setShowFeedback] = React.useState(false);

React.useEffect(() => {
if (isStudent && sequence && sequence.feedback && QuestionStatus.ONGOING === sequence.status) {
if (isStudent && sequence && sequence.feedbacks && QuestionStatus.ONGOING === sequence.status) {
setShowButtonFeedback(true);
}
}, [isStudent, sequence]);
@@ -89,7 +89,7 @@
connectTeacher(project);
}
}
}, [isCollaborationActive, socket, project, isStudent, questionIndex]);

Check warning on line 92 in src/pages/create/3-storyboard/title.tsx

GitHub Actions / lint

React Hook React.useEffect has missing dependencies: 'connectStudent' and 'connectTeacher'. Either include them or remove the dependency array

const backUrl = `/create/3-storyboard${serializeToQueryUrl({ projectId: project?.id || null })}`;

@@ -165,7 +165,7 @@
<FeedbackModal
isOpen={showFeedback}
onClose={() => setShowFeedback(false)}
feedback={sequence && sequence.feedback ? sequence.feedback : ''}
feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''}
/>
</Container>
);
6 changes: 3 additions & 3 deletions src/pages/create/4-pre-mounting/edit.tsx
Original file line number Diff line number Diff line change
@@ -83,12 +83,12 @@
const { user } = React.useContext(userContext);
const isStudent = user?.type === UserType.STUDENT;
const [showButtonFeedback, setShowButtonFeedback] = React.useState(
(isStudent && sequence && sequence.feedback && QuestionStatus.PREMOUNTING === sequence.status) as boolean,
(isStudent && sequence && sequence.feedbacks && QuestionStatus.PREMOUNTING === sequence.status) as boolean,
);
const [showFeedback, setShowFeedback] = React.useState(false);

React.useEffect(() => {
if (isStudent && sequence && sequence.feedback && QuestionStatus.PREMOUNTING === sequence.status) {
if (isStudent && sequence && sequence.feedbacks && QuestionStatus.PREMOUNTING === sequence.status) {
setShowButtonFeedback(true);
}
}, [isStudent, sequence]);
@@ -101,7 +101,7 @@
connectTeacher(project);
}
}
}, [isCollaborationActive, socket, project, isStudent, questionIndex]);

Check warning on line 104 in src/pages/create/4-pre-mounting/edit.tsx

GitHub Actions / lint

React Hook React.useEffect has missing dependencies: 'connectStudent' and 'connectTeacher'. Either include them or remove the dependency array

React.useEffect(() => {
if (isStudent && sequence && sequence.status !== QuestionStatus.PREMOUNTING) {
@@ -337,7 +337,7 @@
<FeedbackModal
isOpen={showFeedback}
onClose={() => setShowFeedback(false)}
feedback={sequence && sequence.feedback ? sequence.feedback : ''}
feedback={sequence && sequence.feedbacks && sequence.feedbacks.length > 0 ? sequence.feedbacks[sequence.feedbacks.length - 1] : ''}
/>
</Container>
);
6 changes: 3 additions & 3 deletions src/pages/create/4-pre-mounting/index.tsx
Original file line number Diff line number Diff line change
@@ -55,7 +55,7 @@
connectTeacher(project);
}
}
}, [isCollaborationActive, socket, project, isStudent, sequencyId]);

Check warning on line 58 in src/pages/create/4-pre-mounting/index.tsx

GitHub Actions / lint

React Hook React.useEffect has missing dependencies: 'connectStudent' and 'connectTeacher'. Either include them or remove the dependency array

React.useEffect(() => {
const question: Question | undefined = questions.find((q: Question) => q.id === sequencyId);
@@ -85,7 +85,7 @@
{questions.map((q, index) => {
if (isStudent && sequencyId !== q.id) return null;
const hasBeenEdited = q.title !== null || (q.plans || []).some((plan) => plan.description || plan.imageUrl);
const showButtonFeedback = isStudent && q.id === sequencyId && q.feedback && QuestionStatus.PREMOUNTING === q.status;
const showButtonFeedback = isStudent && q.id === sequencyId && q.feedbacks && QuestionStatus.PREMOUNTING === q.status;
return (
<div key={index}>
<Title color="primary" variant="h2" marginTop="lg" style={{ marginTop: '2rem', display: 'flex', alignItems: 'center' }}>
@@ -105,12 +105,12 @@
/>
</div>
) : (
<p>{t('part4_placeholder')}</p>
<p style={{ marginTop: '1rem' }}>{t('part4_placeholder')}</p>
)}
<FeedbackModal
isOpen={showFeedback}
onClose={() => setShowFeedback(false)}
feedback={q && q.feedback ? q.feedback : ''}
feedback={q && q.feedbacks && q.feedbacks.length > 0 ? q.feedbacks[q.feedbacks.length - 1] : ''}
/>
</div>
);
105 changes: 46 additions & 59 deletions src/pages/create/6-result/index.tsx
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ import { Button } from 'src/components/layout/Button';
import { Container } from 'src/components/layout/Container';
import { Flex } from 'src/components/layout/Flex';
import { LinearProgress } from 'src/components/layout/LinearProgress';
import { Modal } from 'src/components/layout/Modal';
import { Tooltip } from 'src/components/layout/Tooltip';
import { Title, Text } from 'src/components/layout/Typography';
import { Steps } from 'src/components/navigation/Steps';
@@ -81,7 +80,7 @@ const ResultPage = () => {
const { isCollaborationActive } = useCollaboration();
const { socket, connectTeacher } = useSocket();
const [isLoading, setIsLoading] = React.useState(false);
const [isVideoModalOpen, setIsVideoModalOpen] = React.useState(false);
const [isGenerating, setIsGenerating] = React.useState(false);
const {
projectVideo,
isLoading: isLoadingProjectVideo,
@@ -90,23 +89,34 @@ const ResultPage = () => {
enabled: project !== undefined && project.id !== 0,
});
const sounds = React.useMemo(() => getSounds(questions), [questions]);
const [isDownloading, setIsDownloading] = React.useState<boolean>((projectVideo !== undefined && projectVideo.progress < 100) as boolean);
const isDownloading = projectVideo !== undefined && projectVideo.progress < 100;

React.useEffect(() => {
setIsDownloading((projectVideo !== undefined && projectVideo.progress < 100) as boolean);
if (projectVideo !== undefined && isDownloading) {
if (projectVideo.progress > 99) {
setIsDownloading(false);
return;
}
// Automatically refresh the video download progress
const intervalRef = React.useRef<number | null>(null);
if (isDownloading && intervalRef.current === null) {
intervalRef.current = window.setInterval(() => {
refetch();
}, 1000);
}
if (!isDownloading && intervalRef.current !== null) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}

setTimeout(() => {
refetch();
}, 1000);
// Automatically download the video when it's ready
const willAutoDownload = React.useRef(false);
const downloadVideoRef = React.useRef<HTMLAnchorElement | null>(null);
React.useEffect(() => {
if (projectVideo !== undefined && projectVideo.url && willAutoDownload.current && downloadVideoRef.current) {
willAutoDownload.current = false;
downloadVideoRef.current.click();
}
}, [projectVideo]);

return () => {};
}, [projectVideo, isDownloading, refetch]);
if (isDownloading && !isGenerating) {
setIsGenerating(true);
willAutoDownload.current = true;
}

React.useEffect(() => {
if (isCollaborationActive && socket.connected === false && project !== undefined && project.id) {
@@ -158,17 +168,18 @@ const ResultPage = () => {
if (!project || project.id === 0 || !data) {
return;
}
setIsGenerating(true);
createProjectVideoMutation.mutate(
{
projectId: project.id,
data,
},
{
onSettled: () => {
setIsVideoModalOpen(false);
setIsDownloading(true);
onSuccess: () => {
willAutoDownload.current = true;
},
onError: () => {
setIsGenerating(false);
sendToast({ message: t('unknown_error'), type: 'error' });
},
},
@@ -221,23 +232,7 @@ const ResultPage = () => {
<div>loading</div>
) : (
<>
{videoUrl && (
<>
<Button
label={t('part6_mp4_download_button')}
as="a"
href={videoUrl}
className="full-width"
variant="contained"
color="secondary"
style={{ width: '100%' }}
leftIcon={<VideoIcon style={{ marginRight: '10px', width: '24px', height: '24px' }} />}
download
></Button>
<Or />
</>
)}
{videoProgress && videoProgress !== 100 ? (
{!videoUrl && isGenerating ? (
<div
style={{
width: '100%',
@@ -253,21 +248,32 @@ const ResultPage = () => {
<Text className="color-secondary" style={{ fontSize: '0.8rem', marginBottom: '0.5rem' }}>
{t('part6_mp4_loading')}
</Text>
<LinearProgressWithLabel value={videoProgress} />
<LinearProgressWithLabel value={videoProgress === 100 ? 0 : videoProgress ?? 0} />
</div>
) : videoUrl && isGenerating ? (
<Button
label={t('part6_mp4_download_button')}
as="a"
ref={downloadVideoRef}
href={videoUrl}
className="full-width"
variant="contained"
color="secondary"
style={{ width: '100%' }}
leftIcon={<VideoIcon style={{ marginRight: '10px', width: '24px', height: '24px' }} />}
download
/>
) : (
<Tooltip
content={user === null ? t('part6_mp4_user_disabled') : !hasProject ? t('part6_mp4_project_disabled') : ''}
hasArrow
>
<Button
label={t(videoUrl ? 'part6_mp4_generate_button' : 'part6_mp4_button')}
label={t('part6_mp4_download_button')}
className="full-width"
variant="contained"
color="secondary"
onClick={() => {
setIsVideoModalOpen(true);
}}
onClick={generateMP4}
disabled={user === null || !hasProject}
style={{ width: '100%' }}
leftIcon={<VideoIcon style={{ marginRight: '10px', width: '24px', height: '24px' }} />}
@@ -287,25 +293,6 @@ const ResultPage = () => {
style={{ width: '100%' }}
></Button>
</Flex>
<Modal
isOpen={isVideoModalOpen}
onClose={() => {
setIsVideoModalOpen(false);
}}
isLoading={createProjectVideoMutation.isLoading}
title={t('part6_mp4_button')}
confirmLabel={t('generate')}
onConfirm={generateMP4}
width="md"
isFullWidth
confirmLevel="secondary"
>
<ul style={{ margin: 0 }}>
<li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_1')}</li>
<li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_2')}</li>
<li style={{ marginBottom: '0.5rem' }}>{t('part6_mp4_description_3')}</li>
</ul>
</Modal>
</div>
<Loader isLoading={isLoading} />
</Container>
2 changes: 1 addition & 1 deletion types/models/question.type.ts
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ export interface Question {
soundUrl: string | null;
soundVolume: number | null;
status?: QuestionStatus;
feedback?: string | null;
feedbacks?: string[] | null;
}

export interface QuestionTemplate {

0 comments on commit 1fb149e

Please sign in to comment.