From 57e396da7b53d94b717e0a3ce0db536c6f2041f8 Mon Sep 17 00:00:00 2001 From: Valentin Gimonnet Date: Wed, 28 Feb 2024 14:51:28 +0100 Subject: [PATCH] update collaboration mode and fix cropper --- public/black_bg/black_1600x900.jpg | Bin 0 -> 4626 bytes public/black_bg/black_1920x1080.jpg | Bin 0 -> 6465 bytes public/black_bg/black_2560x1440.jpg | Bin 0 -> 11177 bytes public/black_bg/black_3840x2160.jpg | Bin 0 -> 24683 bytes public/black_bg/black_7680x4320.jpg | Bin 0 -> 97632 bytes server/controllers/projects.ts | 8 ++++ server/translations/defaultLocales.ts | 4 +- src/components/collaboration/AlertModal.tsx | 16 +++++-- .../DiaporamaPlayer/DiaporamaPlayer.tsx | 6 +-- .../create/ProjectCard/ProjectCard.tsx | 5 +- src/components/create/SaveProjectModal.tsx | 3 ++ .../ui/ImageCropper/ImageCropper.tsx | 12 +---- src/pages/create/2-questions/index.tsx | 2 +- src/pages/create/3-storyboard/edit.tsx | 12 +---- src/pages/join.tsx | 45 ++++++------------ src/styles/globals.scss | 13 +++++ src/utils/crop-image.tsx | 36 ++++++++++++++ 17 files changed, 100 insertions(+), 62 deletions(-) create mode 100644 public/black_bg/black_1600x900.jpg create mode 100644 public/black_bg/black_1920x1080.jpg create mode 100644 public/black_bg/black_2560x1440.jpg create mode 100644 public/black_bg/black_3840x2160.jpg create mode 100644 public/black_bg/black_7680x4320.jpg diff --git a/public/black_bg/black_1600x900.jpg b/public/black_bg/black_1600x900.jpg new file mode 100644 index 0000000000000000000000000000000000000000..beba00cef087619655bbb8906fb32c5c000dd1ff GIT binary patch literal 4626 zcmex=md{&$>(+lpN)7mbh(VBpxrNPvnNf*> zNsy6Qkn#T!22rpx8IS?{|62?kAjdN>OhgtRWsioyXb6mkz-S1JhQMeDjE2By2#kin zhzS8;Q&|MkCPwx$xTVY_fGjl19u0vZ90L4!5<2r}LLb60JL+_l5CA810q`gXlK2DY g*axFMgTX{lRuDv2aR5^0GXhl@spC<>3{`Ox0E&1=5C8xG literal 0 HcmV?d00001 diff --git a/public/black_bg/black_1920x1080.jpg b/public/black_bg/black_1920x1080.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5003cdf5a05808b397a29201411ad0b3e39ee424 GIT binary patch literal 6465 zcmex=md{&$>(+lpN)7mbh(VBp#e%(onNf*> zNsy6Qkn#T!22rpx8IS?{|62?kAjdN>OhgtRWsioyXb6mkz-S1JhQMeDjE2By2#kin zXb6mkz-S1JhQMeD43!W7_W4C1oqJ>#gS-4p0?0z6?9mVy4S^9A0(i;>=FzfYL?wk$ w&yI!wN(g|;1_AJ-0Fv+n=u`otJwvS`NQEG}iUW|A1tU;JJv$y1%up3K0TROV literal 0 HcmV?d00001 diff --git a/public/black_bg/black_2560x1440.jpg b/public/black_bg/black_2560x1440.jpg new file mode 100644 index 0000000000000000000000000000000000000000..22641738332e79b0c224e8d21d6d43b613f53e89 GIT binary patch literal 11177 zcmeI&F=_%q6oBFP9D)*~?6`@DgcJd>NMoU$pjZgm3#1Vw2e7qGvlqw(LOSmea)6Y{ z+ISMj+0|l9VUsTYGg}Og$1?M=@L1lsZ?};snLbWMRYd(1x0Y0@eq2>+^?I!l+qltg zHJhz=_x8@u?y&RF@y~ED?)UoR!91BvlKEoxG+U(0Wx9G>Wtr2zzQBEmtz5iciqU&1 zSv6MOr`#0VlyHfpW-$A_Iw+h2^77fHw6oyzMfB*srAaI5PC+ei3PM+Zh!I2R_009J!1oAo=6<1G# g@aAwKRY&r&P0M+X{hjl*|L0Vn^K4FJm_x?T4maLNQ2+n{ literal 0 HcmV?d00001 diff --git a/public/black_bg/black_3840x2160.jpg b/public/black_bg/black_3840x2160.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3693c4823f934c3e3bd0996a1c68cbe202f1fdf8 GIT binary patch literal 24683 zcmeI)y=nqM5Ww--yCMgEuETBB*3 zjc%vi?sNyeOScE({!QOK#G1>B_l z#otK1|0TtseGvO0&IllY00IagfB*srAbZF_&GKHU2!dk1^Nb8_4`Iy`C|ciPQnyK{MQesS5oy6X0Bd;NY+e=U$7qo`B| z^(d#^C>5z(r2G_{<2GY*E#!VwM$JduOvZf^AV7cs0RjXF5FkK+009C72oNAZfB*pk z1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyFkc`) z#YRfgIk_K|RH{yg-3bsNK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWSW&m@{WzHfR6Of3Efk5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAVA=kz-Z3CI{lvgVEmqaigX&+-#NZ=eCGJb@t)%?$3vbKyFaHZ literal 0 HcmV?d00001 diff --git a/server/controllers/projects.ts b/server/controllers/projects.ts index 48806487..1d584622 100644 --- a/server/controllers/projects.ts +++ b/server/controllers/projects.ts @@ -252,6 +252,14 @@ projectController.post({ path: '/', userType: UserType.CLASS }, async (req, res, return; } + // set collaboration mode to false on each user project + await getConnection() + .createQueryBuilder() + .update(Project) + .set({ isCollaborationActive: false, joinCode: null }) + .where({ userId: user.id, isCollaborationActive: true }) + .execute(); + const data = req.body; if (!postProjectValidator(data)) { sendInvalidDataError(postProjectValidator); diff --git a/server/translations/defaultLocales.ts b/server/translations/defaultLocales.ts index c40b049b..e97f716e 100644 --- a/server/translations/defaultLocales.ts +++ b/server/translations/defaultLocales.ts @@ -284,8 +284,8 @@ export const locales = { validation_return_back: "Êtes-vous sûr de vouloir retourner à l'étape précédente ? Vous risquez de perdre le travail en cours.", // -- Collaboration -- collaboration_alert_modal_student_content_feedback: 'Le professeur vous a fait des retours. Regardez les et modifiez votre travail, puis renvoyer le à validation.', - collaboration_alert_modal_student_content_premounting: 'Le professeur à valider votre travail. Vous avez terminé, bravo !', - collaboration_alert_modal_student_content_storyboard: 'Le professeur à valider votre travail. Vous pouvez passer à l\'étape n°4.', + collaboration_alert_modal_student_content_premounting: 'Le professeur a validé votre travail. Vous avez terminé, bravo !', + collaboration_alert_modal_student_content_storyboard: 'Le professeur a validé votre travail. Vous pouvez passer à l\'étape suivante !', collaboration_alert_modal_student_title_feedback: 'Retour du professeur', collaboration_alert_modal_student_title_ok: 'Validation du professeur', collaboration_alert_modal_teacher_content: 'Le groupe d\'élèves affecté à la séquence n° {{sequency}} a terminé la partie {{step}}. Vous pouvez vérifier leur travail.', diff --git a/src/components/collaboration/AlertModal.tsx b/src/components/collaboration/AlertModal.tsx index db575d8c..5428330f 100644 --- a/src/components/collaboration/AlertModal.tsx +++ b/src/components/collaboration/AlertModal.tsx @@ -7,6 +7,7 @@ import { useCurrentProject } from 'src/hooks/useCurrentProject'; import { useTranslation } from 'src/i18n/useTranslation'; import { COLORS } from 'src/utils/colors'; import { useQueryNumber, useQueryString } from 'src/utils/useQueryId'; +import type { Question } from 'types/models/question.type'; import { QuestionStatus } from 'types/models/question.type'; export const AlertModal: React.FunctionComponent = () => { @@ -44,11 +45,16 @@ export const AlertModal: React.FunctionComponent = () => { setModalContent(t('collaboration_alert_modal_student_content_feedback')); } else { setModalTitle(t('collaboration_alert_modal_student_title_ok')); - const status = - project !== undefined && sequency !== undefined && project.questions !== undefined - ? project?.questions[sequency]?.status || QuestionStatus.ONGOING - : QuestionStatus.ONGOING; - setModalContent(t(`collaboration_alert_modal_student_content_${status === QuestionStatus.ONGOING ? 'storyboard' : 'premounting'}`)); + + let status = 'storyboard'; + if (project !== undefined && sequency !== undefined && project.questions !== undefined) { + const questions: Question[] = project.questions; + const question: Question | undefined = questions.find((q) => q.index === sequency); + if (question !== undefined) { + status = question.status !== undefined && question.status < QuestionStatus.SUBMITTED ? 'storyboard' : 'premounting'; + } + } + setModalContent(t(`collaboration_alert_modal_student_content_${status}`)); } setShowModal(true); }; diff --git a/src/components/create/DiaporamaPlayer/DiaporamaPlayer.tsx b/src/components/create/DiaporamaPlayer/DiaporamaPlayer.tsx index b443f761..14091d58 100644 --- a/src/components/create/DiaporamaPlayer/DiaporamaPlayer.tsx +++ b/src/components/create/DiaporamaPlayer/DiaporamaPlayer.tsx @@ -391,13 +391,13 @@ export const DiaporamaPlayer = ({ if (Math.abs(delta) < 1000 * count) { updateLastDuration(delta); } else { - const dt = delta / count; + const dt = newDuration / count; const plans = question.plans || []; const newTitle = question.title; if (newTitle) { - newTitle.duration += dt; + newTitle.duration = dt; } - const newPlans = plans.map((plan) => ({ ...plan, duration: (plan.duration || 0) + dt })); + const newPlans = plans.map((plan) => ({ ...plan, duration: dt })); const newQuestion = { ...questions[0], plans: newPlans, diff --git a/src/components/create/ProjectCard/ProjectCard.tsx b/src/components/create/ProjectCard/ProjectCard.tsx index e73f98b9..1c2df08b 100644 --- a/src/components/create/ProjectCard/ProjectCard.tsx +++ b/src/components/create/ProjectCard/ProjectCard.tsx @@ -6,6 +6,7 @@ import * as React from 'react'; import styles from './project-card.module.scss'; import { IconButton } from 'src/components/layout/Button/IconButton'; import { Title } from 'src/components/layout/Typography'; +import { useCollaboration } from 'src/hooks/useCollaboration'; import { useTranslation } from 'src/i18n/useTranslation'; type ProjectCardProps = { @@ -17,8 +18,10 @@ type ProjectCardProps = { }; export const ProjectCard = ({ title = '', themeName = '', href, onClickEdit, className }: ProjectCardProps) => { const { t } = useTranslation(); + const { setIsCollaborationActive } = useCollaboration(); + return ( - + setIsCollaborationActive(false)}> {title} diff --git a/src/components/create/SaveProjectModal.tsx b/src/components/create/SaveProjectModal.tsx index 427457a0..7a4f34e8 100644 --- a/src/components/create/SaveProjectModal.tsx +++ b/src/components/create/SaveProjectModal.tsx @@ -5,6 +5,7 @@ import { Field, Form, Input } from '../layout/Form'; import { Modal } from '../layout/Modal'; import { sendToast } from '../ui/Toasts'; import { useCreateProjectMutation } from 'src/api/projects/projects.post'; +import { useCollaboration } from 'src/hooks/useCollaboration'; import { useCurrentProject } from 'src/hooks/useCurrentProject'; import { useTranslation } from 'src/i18n/useTranslation'; @@ -19,6 +20,7 @@ export const SaveProjectModal = ({ isOpen, onClose }: SaveProjectModalProps) => const [title, setTitle] = React.useState(''); const createProjectMutation = useCreateProjectMutation(); + const { setIsCollaborationActive } = useCollaboration(); const onCreateProject = async () => { if (!project) { @@ -32,6 +34,7 @@ export const SaveProjectModal = ({ isOpen, onClose }: SaveProjectModalProps) => } try { + setIsCollaborationActive(false); const newProject = await createProjectMutation.mutateAsync({ title, themeId: project.themeId, diff --git a/src/components/ui/ImageCropper/ImageCropper.tsx b/src/components/ui/ImageCropper/ImageCropper.tsx index 857225d5..b74e9d8f 100644 --- a/src/components/ui/ImageCropper/ImageCropper.tsx +++ b/src/components/ui/ImageCropper/ImageCropper.tsx @@ -7,7 +7,7 @@ import Cropper from 'react-easy-crop'; import { Button } from 'src/components/layout/Button'; import { Modal } from 'src/components/layout/Modal'; import { Title } from 'src/components/layout/Typography'; -import getCroppedImg, { getMeta } from 'src/utils/crop-image'; +import getCroppedImg, { getMergeImagesParams, getMeta } from 'src/utils/crop-image'; type ImageCropperProps = { image: string | null; @@ -43,15 +43,7 @@ export const ImageCropper: React.FunctionComponent = ({ image if (err) throw new Error(err instanceof Event ? err.toString() : err); if (img === null) return; - const imgHeight = img.naturalHeight; - const imgWidth = img.naturalWidth; - - const params = [ - { src: '/black.jpg', x: 0, y: 0 }, // 1920 * 1080 - { src: url, x: imgWidth < 1920 ? (1920 - imgWidth) / 2 : imgWidth, y: imgHeight < 1080 ? (1080 - imgHeight) / 2 : imgHeight }, - ]; - - mergeImages(params).then((base64: string) => setImageUrl(base64)); + mergeImages(getMergeImagesParams(img, url)).then((base64: string) => setImageUrl(base64)); }); } catch (error) { console.error(error); diff --git a/src/pages/create/2-questions/index.tsx b/src/pages/create/2-questions/index.tsx index 7e73f405..de5fc476 100644 --- a/src/pages/create/2-questions/index.tsx +++ b/src/pages/create/2-questions/index.tsx @@ -284,7 +284,7 @@ const QuestionsPage = () => { diff --git a/src/pages/create/3-storyboard/edit.tsx b/src/pages/create/3-storyboard/edit.tsx index 9f45f6a5..26cc85cd 100644 --- a/src/pages/create/3-storyboard/edit.tsx +++ b/src/pages/create/3-storyboard/edit.tsx @@ -33,7 +33,7 @@ import { useCollaboration } from 'src/hooks/useCollaboration'; import { useCurrentProject } from 'src/hooks/useCurrentProject'; import { useSocket } from 'src/hooks/useSocket'; import { useTranslation } from 'src/i18n/useTranslation'; -import getCroppedImg, { getMeta } from 'src/utils/crop-image'; +import getCroppedImg, { getMergeImagesParams, getMeta } from 'src/utils/crop-image'; import { serializeToQueryUrl } from 'src/utils/serializeToQueryUrl'; import { isString } from 'src/utils/type-guards/is-string'; import { useQueryNumber } from 'src/utils/useQueryId'; @@ -209,15 +209,7 @@ const EditPlan = () => { if (err) throw new Error(err instanceof Event ? err.toString() : err); if (img === null) return; - const imgHeight = img.naturalHeight; - const imgWidth = img.naturalWidth; - - const params = [ - { src: '/black.jpg', x: 0, y: 0 }, // 1920 * 1080 - { src: url, x: imgWidth < 1920 ? (1920 - imgWidth) / 2 : imgWidth, y: imgHeight < 1080 ? (1080 - imgHeight) / 2 : imgHeight }, - ]; - - mergeImages(params).then((base64: string) => setTemporaryImageUrl(base64)); + mergeImages(getMergeImagesParams(img, url)).then((base64: string) => setTemporaryImageUrl(base64)); }); } catch (error) { console.error(error); diff --git a/src/pages/join.tsx b/src/pages/join.tsx index 0e98c672..863fd343 100644 --- a/src/pages/join.tsx +++ b/src/pages/join.tsx @@ -75,31 +75,23 @@ const LoginPage = () => { /* eslint-disable @typescript-eslint/no-explicit-any */ const QrReaderResult = async (result: any) => { - if (result) { - const loginData = JSON.parse(result); + if (result && result.text) { + try { + const loginData = JSON.parse(result.text); - if (loginData.projectId && loginData.sequencyId && loginData.teacherId) { - postLoginStudent({ - projectId: loginData.projectId, - sequencyId: loginData.sequencyId, - teacherId: loginData.teacherId, - }); + if (loginData.projectId && loginData.sequencyId && loginData.teacherId) { + postLoginStudent({ + projectId: loginData.projectId, + sequencyId: loginData.sequencyId, + teacherId: loginData.teacherId, + }); + } + } catch (e) { + console.error(e); } } }; - // TODO: Remove this code before MEP - START - // const testStudentLogin = async (e: Event) => { - // e.preventDefault(); - // // change this with your ids to test - // postLoginStudent({ - // projectId: 1, - // sequencyId: 1, - // teacherId: 2, - // }); - // }; - // TODO: Remove this code before MEP - STOP - const toggleShowQrReader = () => { setShowQrReader(!showQrReader); }; @@ -145,20 +137,11 @@ const LoginPage = () => { }} isFullWidth required + className="inputNumber-withoutArrow" /> } > - {/* // TODO: Remove this code before MEP - START */} - {/* */} - {/* // TODO: Remove this code before MEP - STOP */} {showQrReader && } @@ -181,6 +165,7 @@ const LoginPage = () => { key={index} >

{t('collaboration_join_sequency_number', { sequency: index + 1 })}

+

{q.question}

); diff --git a/src/styles/globals.scss b/src/styles/globals.scss index 4783d6a0..5ef70f3d 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -421,3 +421,16 @@ p { } } } + +.inputNumber-withoutArrow { + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; + } + + /* Firefox */ + &[type=number] { + -moz-appearance: textfield; + } +} \ No newline at end of file diff --git a/src/utils/crop-image.tsx b/src/utils/crop-image.tsx index cc001632..100f4a27 100644 --- a/src/utils/crop-image.tsx +++ b/src/utils/crop-image.tsx @@ -88,3 +88,39 @@ export default async function getCroppedImg(imageSrc: string, pixelCrop: Area, r // }, 'image/jpeg'); }); } + +type MERGE_IMAGE = { src: string; x: number; y: number }; + +const MERGE_IMAGE_OPTIONS: MERGE_IMAGE[] = [ + { src: '/black_bg/black_1600x900.jpg', x: 1600, y: 900 }, + { src: '/black_bg/black_1920x1080.jpg', x: 1920, y: 1080 }, + { src: '/black_bg/black_2560x1440.jpg', x: 2560, y: 1440 }, + { src: '/black_bg/black_3840x2160.jpg', x: 3840, y: 2160 }, + { src: '/black_bg/black_7680x4320.jpg', x: 7680, y: 4320 }, +]; + +export const getMergeImagesParams = (img: HTMLImageElement, url: string): MERGE_IMAGE[] => { + let option: MERGE_IMAGE = MERGE_IMAGE_OPTIONS[0]; + + const imgWidth = img.naturalWidth; + const imgHeight = img.naturalHeight; + + if (imgWidth > 3840 || imgHeight > 2160) { + option = MERGE_IMAGE_OPTIONS[4]; + } else if (imgWidth > 2560 || imgHeight > 1440) { + option = MERGE_IMAGE_OPTIONS[3]; + } else if (imgWidth > 1920 || imgHeight > 1080) { + option = MERGE_IMAGE_OPTIONS[2]; + } else if (imgWidth > 1600 || imgHeight > 900) { + option = MERGE_IMAGE_OPTIONS[1]; + } + + return [ + { src: option.src, x: 0, y: 0 }, + { + src: url, + x: imgWidth < option.x ? (option.x - imgWidth) / 2 : 0, + y: imgHeight < option.y ? (option.y - imgHeight) / 2 : 0, + }, + ]; +};