Add possibility to send picture through the app for certains questions

This commit is contained in:
Lucien Astié 2024-08-09 23:20:04 +02:00
parent cbd500e13b
commit 28e1a0c4f8
5 changed files with 171 additions and 89 deletions

View file

@ -3,8 +3,10 @@ import { useState, useEffect, useRef } from 'react'
import { io } from "socket.io-client" import { io } from "socket.io-client"
import { getRandomQuestion } from "./questions" import { getRandomQuestion } from "./questions"
import { useForceUpdate } from "./forceUpdate" import { useForceUpdate } from "./forceUpdate"
import { IconCrown } from "@tabler/icons-react" import { IconCrown, IconPhotoUp, IconCamera } from "@tabler/icons-react"
import { defaultAvatarImage } from '../avatarImage' import { defaultAvatarImage } from '../avatarImage'
import { resizeBase64Image, getBase64OfImage } from '../utils'
import Webcam from 'react-webcam'
interface roomProps { interface roomProps {
params: { params: {
@ -23,7 +25,8 @@ export default function Home({ params }: roomProps) {
const [gameStarted, setGameStarted] = useState(false) const [gameStarted, setGameStarted] = useState(false)
const [gameEnded, setGameEnded] = useState(false) const [gameEnded, setGameEnded] = useState(false)
const [questionDisplayed, setQuestionDisplayed] = useState("") const [questionReply, setQuestionReply] = useState({})
const [questionDisplayed, setQuestionDisplayed] = useState({})
const [possibleChoice, setPossibleChoice] = useState([]) const [possibleChoice, setPossibleChoice] = useState([])
const [totalVotes, setTotalVotes] = useState(0) const [totalVotes, setTotalVotes] = useState(0)
const [choice, setChoice] = useState("") const [choice, setChoice] = useState("")
@ -33,6 +36,9 @@ export default function Home({ params }: roomProps) {
const [questionAlreadyDone, setQuestionAlreadyDone] = useState([]) const [questionAlreadyDone, setQuestionAlreadyDone] = useState([])
const socketRef = useRef() const socketRef = useRef()
const inputPhotoRef = useRef()
const photoModalRef = useRef()
const webcamRef = useRef()
const duration = 15 const duration = 15
const questionLimit = 10 const questionLimit = 10
@ -76,6 +82,11 @@ export default function Home({ params }: roomProps) {
setTotalVotes(0) setTotalVotes(0)
}) })
socketRef.current.on("question_reply", (params) => {
console.log(params)
setQuestionReply(params)
})
socketRef.current.on("reset_game", (params) => { socketRef.current.on("reset_game", (params) => {
setGameStarted(false) setGameStarted(false)
setCountdown(0) setCountdown(0)
@ -187,8 +198,36 @@ export default function Home({ params }: roomProps) {
socketRef.current.emit("player_choice", {roomId: id, choice: playerName, player: name}) socketRef.current.emit("player_choice", {roomId: id, choice: playerName, player: name})
} }
function setAndSendPhoto(mode){
console.log(mode)
if(mode == "webcam"){
const imageSrc = webcamRef.current.getScreenshot()
resizeBase64Image(imageSrc).then((data) => {
setQuestionReply({photo: data})
socketRef.current.emit("question_reply", { roomId: id, data:{photo: data}})
})
}
if(mode == "file"){
getBase64OfImage(inputPhotoRef.current.files[0], (data) => resizeBase64Image(data).then((data) =>{
setQuestionReply({photo: data})
socketRef.current.emit("question_reply", { roomId: id, data:{photo: data}})
}))
}
}
return ( return (
<main className="flex min-h-screen flex-col items-center space-y-16 p-4"> <main className="flex min-h-screen flex-col items-center space-y-16 p-4">
<dialog className="modal" ref={photoModalRef}>
<div className="modal-box w-full h-full">
<Webcam ref={webcamRef} />
<div className="modal-action">
<form method="dialog">
{/* if there is a button, it will close the modal */}
<button className="btn" onClick={() => setAndSendPhoto("webcam")}>Close</button>
</form>
</div>
</div>
</dialog>
{ !gameStarted && { !gameStarted &&
<> <>
<div className="flex flex-col"> <div className="flex flex-col">
@ -231,7 +270,7 @@ export default function Home({ params }: roomProps) {
{ gameStarted && { gameStarted &&
<> <>
<div className="flex flex-col space-y-4 items-center"> <div className="flex flex-col space-y-4 items-center">
<h1 className="text-3xl">{questionDisplayed}</h1> <h1 className="text-3xl">{questionDisplayed.text}</h1>
<span className="indicator-item badge indicator-bottom indicator-center opacity-30">{questionNbr}/{questionLimit}</span> <span className="indicator-item badge indicator-bottom indicator-center opacity-30">{questionNbr}/{questionLimit}</span>
</div> </div>
{ countdown > 0 && { countdown > 0 &&
@ -269,6 +308,43 @@ export default function Home({ params }: roomProps) {
</div> </div>
)})} )})}
</div> </div>
{ (questionDisplayed.type == "photo" && possibleChoice.sort((a,b) => b.nbrVotes - a.nbrVotes)[0].browserId == browserId) &&
<>
{ questionReply.photo == undefined &&
<div className="flex flex-col items-center space-y-4">
<span className="text-xl">À toi d'envoyer une photo !</span>
<input type="file" ref={inputPhotoRef} onChange={() => setAndSendPhoto("file")} className="hidden" />
<div className="flex flex-row space-x-4">
<button className="btn btn-primary" onClick={() => inputPhotoRef.current.click()}><IconPhotoUp /></button>
<button className="btn btn-primary" onClick={() => photoModalRef.current.showModal()}><IconCamera /></button>
</div>
</div>
}
{ questionReply.photo != undefined &&
<div>
<img src={questionReply.photo} />
<span className="text-zinc-500">Photo received from {possibleChoice.sort((a,b) => b.nbrVotes - a.nbrVotes)[0].name}...</span>
</div>
}
</>
}
{ (questionDisplayed.type == "photo" && possibleChoice.sort((a,b) => b.nbrVotes - a.nbrVotes)[0].browserId != browserId) &&
<>
{ questionReply.photo == undefined &&
<div>
<span className="text-zinc-500">Waiting for {possibleChoice.sort((a,b) => b.nbrVotes - a.nbrVotes)[0].name}...</span>
</div>
}
{ questionReply.photo != undefined &&
<div>
<img src={questionReply.photo} />
<span className="text-zinc-500">Photo received from {possibleChoice.sort((a,b) => b.nbrVotes - a.nbrVotes)[0].name}...</span>
</div>
}
</>
}
{ (role == "owner" && questionNbr < questionLimit) && { (role == "owner" && questionNbr < questionLimit) &&
<button className="btn btn-primary" onClick={nextQuestion}>Next question</button> <button className="btn btn-primary" onClick={nextQuestion}>Next question</button>
} }

View file

@ -45,57 +45,57 @@ export const questions = [
//"Qui a le plus de connaissances historiques ?", //"Qui a le plus de connaissances historiques ?",
//"Qui est le/la plus passionné·e de jardinage ?", //"Qui est le/la plus passionné·e de jardinage ?",
//"Qui a les meilleurs talents d'acteur·rice ?", //"Qui a les meilleurs talents d'acteur·rice ?",
"Qui va sauter dans la piscine ?", { text: "Qui va sauter dans la piscine ?", type: "text"},
"Qui date le plus âgé ?", { text: "Qui date le plus âgé ?", type: "text"},
"Qui est le plus flexible avec démonstration ?", { text: "Qui est le plus flexible avec démonstration ?", type: "text"},
"Qui va faire un striptease ?", { text: "Qui va faire un striptease ?", type:"text"},
"Qui sera la pire parent ?", { text: "Qui sera la pire parent ?", type:"text"},
"Qui est le plus alcoolique ?", { text: "Qui est le plus alcoolique ?", type:"text"},
"Qui serait capable de chier dans la seine ?", { text: "Qui serait capable de chier dans la seine ?", type:"text"},
"Qui va montrer son dernier message envoyé ?", { text: "Qui va montrer son dernier message envoyé ?", type:"text"},
"Qui va faire une roulette de photos ?", { text: "Qui va faire une roulette de photos ?", type:"text"},
"Qui a déjà bouffé les deux boules d'un mec ?", { text: "Qui a déjà bouffé les deux boules d'un mec ?", type:"text"},
"Qui va nous décrire son crush ?", { text: "Qui va nous décrire son crush ?", type: "text"},
"Qui est le plus addict ?", { text: "Qui est le plus addict ?", type: "text"},
"Qui est le plus croquant ?", { text: "Qui est le plus croquant ?", type: "text"},
"Qui s'auto-suce le plus ?", { text: "Qui s'auto-suce le plus ?", type: "text"},
"Qui va prendre une fessée avec la tongue de l'archi Vergnaud ?", { text: "Qui va prendre une fessée avec la tongue de l'archi Vergnaud ?", type: "text"},
"Qui est le plus à droite ?", { text: "Qui est le plus à droite ?", type: "text"},
"Qui conduit le mieux ?", { text: "Qui conduit le mieux ?", type: "text"},
"Qui gagne au bras de fer ? (Preuve)", { text: "Qui gagne au bras de fer ? (Preuve)", type: "text"},
"Qui va faire une roulade ?", { text: "Qui va faire une roulade ?", type: "text"},
"Qui a le plus une tête de con•ne ?", { text: "Qui a le plus une tête de con•ne ?", type: "text"},
"Qui est le meilleur menteur ?", { text: "Qui est le meilleur menteur ?", type: "text"},
"Qui rikou ?", { text: "Qui rikou ?", type: "text"},
"Qui va boire un shot d'huile ?", { text: "Qui va boire un shot d'huile ?", type: "text"},
"Qui va manger une cuillère a soupe de mayonnaise ?", { text: "Qui va manger une cuillère a soupe de mayonnaise ?", type: "text"},
"Qui va choisir ce que l'autre va manger ?", { text: "Qui va choisir ce que l'autre va manger ?", type: "text"},
"Qui va nous organiser les prochaines vacances ?", { text: "Qui va nous organiser les prochaines vacances ?", type: "text"},
"Qui a la plus belle fesse ?", { text: "Qui a la plus belle fesse ?", type: "text"},
"Qui est le plus intrusif ?", { text: "Qui est le plus intrusif ?", type: "text"},
"Qui a le plus haut bodycount ?", { text: "Qui a le plus haut bodycount ?", type: "text"},
"Qui va imiter Nicolas Pham ?", { text: "Qui va imiter Nicolas Pham ?", type: "text"},
"Qui va dire vinaigrette sans Vi ?", { text: "Qui va dire vinaigrette sans Vi ?", type: "text"},
"Qui va faire son âge divisé par deux en pompes ?", { text: "Qui va faire son âge divisé par deux en pompes ?", type: "text"},
"Qui va faire l'intro pour le dernier tiktok ?", { text: "Qui va faire l'intro pour le dernier tiktok ?", type: "text"},
"Qui va faire une photo sexy avec son voisin de droite ?", { text: "Qui va faire une photo sexy avec son voisin de droite ?", type: "photo"},
"Qui va brouter de l'herbe ?", { text: "Qui va brouter de l'herbe ?", type: "text"},
"Qui va boire un Ricard piscine ?", { text: "Qui va boire un Ricard piscine ?", type: "text"},
"Qui est susceptible de se marier ?", { text: "Qui est susceptible de se marier ?", type: "text"},
"À qui donneriez vous vos enfants ?", { text: "À qui donneriez vous vos enfants ?", type: "text"},
"Qui serait le meilleur DDP ?", { text: "Qui serait le meilleur DDP ?", type: "text"},
"À qui tu te confierais le plus ?", { text: "À qui tu te confierais le plus ?", type: "text"},
"Qui est le plus béru ?", { text: "Qui est le plus béru ?", type: "text"},
"Qui est le plus kinky ?", { text: "Qui est le plus kinky ?", type: "text"},
"Qui va nous chanter le menu ?", { text: "Qui va nous chanter le menu ?", type: "text"},
"Qui lance un monôme tout nu ?", { text: "Qui lance un monôme tout nu ?", type: "text"},
"Qui est le plus rapide ?", { text: "Qui est le plus rapide ?", type: "text"},
"Qui est le plus susceptible d'être célèbre ?", { text: "Qui est le plus susceptible d'être célèbre ?", type: "text"},
"Qui est le plus romantique ?", { text: "Qui est le plus romantique ?", type: "text"},
"Qui va raconter la chose la plus folle de sa vie ?", { text: "Qui va raconter la chose la plus folle de sa vie ?", type: "text"},
"Qui se masturbe le plus ?", { text: "Qui se masturbe le plus ?", type: "text"},
"Qui est le plus gros fan de star wars ?", { text: "Qui est le plus gros fan de star wars ?", type: "text"},
"Qui a pleuré en dernier ?", { text: "Qui a pleuré en dernier ?", type: "text"},
] ]
export function getRandomQuestion(alreadyDone = []){ export function getRandomQuestion(alreadyDone = []){

View file

@ -5,6 +5,7 @@ import { useState, useEffect, useRef } from 'react'
import { IconPencilMinus, IconCamera, IconDice6 } from "@tabler/icons-react" import { IconPencilMinus, IconCamera, IconDice6 } from "@tabler/icons-react"
import { defaultAvatarImage } from './avatarImage' import { defaultAvatarImage } from './avatarImage'
import { getUsername } from './usernameGenerate' import { getUsername } from './usernameGenerate'
import { resizeBase64Image, getBase64OfImage } from './utils'
import Webcam from 'react-webcam' import Webcam from 'react-webcam'
export default function Home() { export default function Home() {
@ -40,41 +41,6 @@ export default function Home() {
setShowWebcam(false) setShowWebcam(false)
} }
function getBase64OfImage(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
function resizeBase64Image(base64Image) {
return new Promise((resolve, reject) => {
const maxSizeInKB = 500;
const maxSizeInBytes = maxSizeInKB * 1024;
const img = new Image();
img.src = base64Image;
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
const width = img.width;
const height = img.height;
const aspectRatio = width / height;
const newWidth = Math.sqrt(maxSizeInBytes * aspectRatio);
const newHeight = Math.sqrt(maxSizeInBytes / aspectRatio);
canvas.width = newWidth;
canvas.height = newHeight;
ctx.drawImage(img, 0, 0, newWidth, newHeight);
let quality = 0.8;
let dataURL = canvas.toDataURL('image/jpeg', quality);
resolve(dataURL);
};
});
}
useEffect(() => { useEffect(() => {
let localName = localStorage.getItem("name") let localName = localStorage.getItem("name")
let localAvatar = localStorage.getItem("avatar") let localAvatar = localStorage.getItem("avatar")

36
app/utils.ts Normal file
View file

@ -0,0 +1,36 @@
export function resizeBase64Image(base64Image) {
return new Promise((resolve, reject) => {
const maxSizeInKB = 500;
const maxSizeInBytes = maxSizeInKB * 1024;
const img = new Image();
img.src = base64Image;
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext('2d');
const width = img.width;
const height = img.height;
const aspectRatio = width / height;
const newWidth = Math.sqrt(maxSizeInBytes * aspectRatio);
const newHeight = Math.sqrt(maxSizeInBytes / aspectRatio);
canvas.width = newWidth;
canvas.height = newHeight;
ctx.drawImage(img, 0, 0, newWidth, newHeight);
let quality = 0.8;
let dataURL = canvas.toDataURL('image/jpeg', quality);
resolve(dataURL);
};
});
}
export function getBase64OfImage(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}

View file

@ -93,6 +93,10 @@ app.prepare().then(() => {
socket.to(params.roomId).emit("next_question",{possibleChoice: params.possibleChoice, question: params.question, duration: params.duration}) socket.to(params.roomId).emit("next_question",{possibleChoice: params.possibleChoice, question: params.question, duration: params.duration})
}) })
socket.on("question_reply", (params) => {
socket.to(params.roomId).emit("question_reply", params.data)
})
socket.on("player_choice", (params) => { socket.on("player_choice", (params) => {
console.log(params) console.log(params)
socket.to(params.roomId).emit("player_choice", {choice: params.choice, player: params.player}) socket.to(params.roomId).emit("player_choice", {choice: params.choice, player: params.player})