This commit is contained in:
Alexandre POMMIER 2023-06-23 16:01:09 +02:00
commit cdd2836e7c
18 changed files with 249 additions and 78 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@ -3,6 +3,8 @@ import { motion } from 'framer-motion'
import { GrTrophy } from "react-icons/gr";
import '../../styles/Messages.css'
import React from "react";
import { MdQrCodeScanner } from "react-icons/md";
import { GiCrownedSkull, GiWingedSword } from "react-icons/gi";
const dropIn = {
hidden: {
@ -18,10 +20,11 @@ const dropIn = {
interface AlertProps {
handleClose: Function,
text: string
text: string,
icon: number
}
function YellowAlert ({handleClose, text}: AlertProps) {
function YellowAlert ({handleClose, text, icon}: AlertProps) {
return(
<Backdrop onClick={handleClose}>
<motion.div
@ -32,8 +35,23 @@ function YellowAlert ({handleClose, text}: AlertProps) {
animate="visible"
exit="exit"
>
{icon === 0 ? (
<GrTrophy/>
<p>{text}</p>
):("")}
{icon === 1 ? (
<MdQrCodeScanner/>
):("")}
{icon === 2 ? (
<GiCrownedSkull/>
):("")}
{icon === 3 ? (
<GiWingedSword/>
):("")}
<h5>{text}</h5>
</motion.div>
{setTimeout(handleClose, 3000)}
</Backdrop>

View File

@ -4,10 +4,14 @@ import {Link} from 'react-router-dom';
import DefaultPicture from '../assets/profile.jpg'
import { motion, AnimatePresence } from 'framer-motion'
import Modal from './Sidebar/Modal.tsx';
import YellowAlert from './Alert/YellowAlert.tsx';
import '../styles/Header.css';
import api from '../script/axiosApi.tsx';
import { MdQrCodeScanner } from 'react-icons/md';
import { GiWingedSword, GiCrownedSkull } from 'react-icons/gi';
function Header() {
// const [sidebar, setSidebar] = useState(false);
// const showSidebar = () => setSidebar(!sidebar);
@ -15,6 +19,8 @@ function Header() {
const close = () => setModalOpen(false);
const open = () => setModalOpen(true);
const [success, setSuccess] = useState([]);
const [profilePicture, setProfilePicture] = useState('');
useEffect(() => {
@ -23,6 +29,8 @@ function Header() {
const user = await api.get("/profile");
const pic = await api.post("/getPicture", {username: user.data.username})
setProfilePicture(pic.data);
// console.log("test ===", user.data)
setSuccess(user.data);
// console.log(`profile pic222= ${pic.data}`)
} catch (error) {
console.error('Error fetching profile picture:', error);
@ -32,10 +40,6 @@ function Header() {
fetchProfilePicture();
}, []);
// console.log(`profile pic= ${profilePicture}`)
// photo.toString('base64')
return (
<div className='Header'>
<motion.div
@ -45,6 +49,7 @@ function Header() {
</Link>
</motion.div>
<div className='end'>
<Link to="/profile" className='menu-bars'>
<div>

View File

@ -108,15 +108,19 @@ function Chats(){
const convs = await api.get("/conv")
const tmpInvite = await api.get("/partyInvite")
const tmpUser = await api.get("/profile")
const tmpUsers = await api.get("/users");
console.log(convs);
// console.log("invite data use effect= ", tmpInvite.data);
setPartyInvite(tmpInvite.data);
setUser(tmpUser.data);
setConversation(convs.data);
console.log(`connection....`);
socket.current = io('http://localhost:4001', { transports: ['polling'] });
console.log(`connection done`);
setUsers(tmpUsers.data);
// console.log(`connection....`);
socket.current = io('http://' + process.env.REACT_APP_BASE_URL + ':4001', { transports: ['polling'] });
// console.log(`connection done`);
socket.current.emit('connection', {username: tmpUser.data.username})
socket.current.on('message', (data) => { //data should be a message ?)
setIncomingMessage(data);
@ -282,6 +286,10 @@ function Chats(){
const [newGameModalOpen, setNewGameModalOpen] = useState(false);
const [newConversationModalOpen, setNewConversationModalOpen] = useState(false);
const [selectTags, setSelectTag] = useState([{ id: 1, selectedOption: ''}]);
const [users, setUsers] = useState<User[]>([]);
const openNewGameModal = () => {
setNewGameModalOpen(true);
};
@ -361,6 +369,15 @@ function Chats(){
setShowBlockAlert(false);
};
const handleOptionChange = (selectId: number, selectedOption: string) => {
console.log("selected Option=", selectedOption)
setSelectTag((prevTags) =>
prevTags.map((tag) =>
tag.id === selectId ? { ...tag, selectedOption } : tag
)
);
};
//========================================================================================================
//========================================================================================================
// HTML
@ -425,8 +442,25 @@ function Chats(){
):("")}
</div> */}
<div className="end">
<input className="lookForFriends" type="text" value={friend} onChange={handleFriend} />
<div className="end">
{selectTags.map((selectTag) => (
<div key={selectTag.id}>
<select
value={selectTag.selectedOption}
className="lookForFriends"
onChange={(a) => handleOptionChange(selectTag.id, a.target.value)}
>
<option value="">{
selectTag.selectedOption ? selectTag.selectedOption : "Select an option"
}</option>
{users.filter((item) => !selectTags.some((tag) => tag.selectedOption === item.username)).map((item, index) => (
<option key={index} value={item.username}>
{item.username}
</option>
))}
</select>
</div>
))}
<TouchDiv>
<motion.div onClick={handleAddFriend}>
<MdOutlineGroupAdd />

View File

@ -153,6 +153,10 @@ const Modal = ({handleClose}) => {
))}
</select>
)}
{channel.private ? (
<input className="mdp" placeholder="password" type="text" />
):("")}
<div className="div_submit">
<Link to='#' className="submit" onClick={ joinChannel }>Join</Link>

View File

@ -40,6 +40,13 @@ const ModalSetting = ({handleClose, convId, socket }: ModalSettingProps) => {
const [selectedUser, setSelectedUser] = useState("");
const [newName, setNewName] = useState("");
const [newPassword, setNewPassword] = useState("");
const [privateConv, setPrivateConv] = useState(false);
const dark = () => setPrivateConv(true);
const light = () => setPrivateConv(false);
const [mute, setMute] = useState(false);
const darkMute = () => setMute(false);
const lightMute = () => setMute(true);
useEffect(()=> {
@ -183,15 +190,19 @@ const ModalSetting = ({handleClose, convId, socket }: ModalSettingProps) => {
{/* First selection */}
<div className="settingFirstPart">
<div>
<p className="checkbox">Private<input className="check"type="checkbox" value="private" onChange={handleCheckPriv}/></p>
<div>
<Link to="#" onClick={light} className={ privateConv ? "submit" : "darkSubmit"}>Public</Link>
<Link to="#" onClick={dark} className={ privateConv ? "darkSubmit" : "submit"}>Private</Link>
</div>
{/* <p className="checkbox">Private<input className="check"type="checkbox" value="private" onChange={handleCheckPriv}/></p> */}
<p className="checkbox">Password<input type="checkbox" value="password" checked={password} onChange={handleCheckPass}/> </p>
{password ? (
{password || privateConv ? (
<input
onChange={(e) => setNewPassword(e.target.value)}
onKeyDown={handlePassword}
type="text"
type="password"
className="in"
placeholder="Password"/>
):
@ -237,11 +248,14 @@ const ModalSetting = ({handleClose, convId, socket }: ModalSettingProps) => {
<div>
<Link to="#" onClick={handleInvite} className="submit">Send</Link>
<Link to="#" onClick={handleBan} className="submit">Ban</Link>
<Link to="#" onClick={handleMute} className="submit">Mute</Link>
<Link to="#" onClick={mute ? darkMute : lightMute} className={mute ? "darkSubmit": "submit"}>Mute</Link>
<Link to="#" onClick={handleAdmin} className="submit">Admin</Link>
</div>
</div>
{mute ? (
<input type="text" className="in_howLong" placeholder="How long ?" />
):("")}
</motion.div>
</Backdrop>

View File

@ -20,26 +20,6 @@ const dropIn = {
},
}
// function showBar (){
// return (
// {SidebarData.map((item, index) => {
// return (
// <motion.div
// className="nav-menu"
// // whileHover={{scale: 1.1}}
// >
// <li key={index} className={item.cName}>
// <Link to={item.path}>
// {item.icon}
// <span>{item.title}</span>
// </Link>
// </li>
// </motion.div>
// )
// })}
// )
// }
interface CloseProps {
handleclose: Function;
}

View File

@ -6,7 +6,7 @@
/* By: apommier <apommier@student.42.fr> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2023/06/09 08:19:04 by apommier #+# #+# */
/* Updated: 2023/06/20 15:27:00 by apommier ### ########.fr */
/* Updated: 2023/06/23 15:58:14 by apommier ### ########.fr */
/* */
/* ************************************************************************** */
@ -21,6 +21,9 @@ import { motion, AnimatePresence } from 'framer-motion'
import { Link } from "react-router-dom";
import ModalEdit from "../components/Profile/EditName.tsx";
import {AiOutlineHistory} from 'react-icons/ai'
import { MdQrCodeScanner, MdOutlinePhotoLibrary } from 'react-icons/md';
import { GiWingedSword, GiCrownedSkull } from 'react-icons/gi';
// import { Link } from "react-router-dom";
// import {UserProfile} from "../DataBase/DataUserProfile";
// import axios from "axios";
@ -33,6 +36,7 @@ import { IoCloseCircleOutline } from "react-icons/io5";
import React, { useState, useEffect, useRef, ChangeEventHandler } from "react";
import { useParams } from 'react-router-dom';
import {User, Conv} from "../../interfaces.tsx"
import YellowAlert from '../components/Alert/YellowAlert.tsx';
// axios.get("http://localhost/api")
// .then((response) => {
// response = response.json()
@ -58,24 +62,6 @@ function Profile () {
const [profilePicture, setProfilePicture] = useState('');
// const handleFileChange = (event: { target: { files: React.SetStateAction<null>[]; }; }) => {
// // const file = event.target.files[0];
// setSelectedPhoto(event.target.files[0]);
// // try{
// // api.post("/picture", {picture: URL.createObjectURL(file)})
// // }
// // catch(err){
// // console.log(err);
// // }
// };
// const handleFileChange = (event: { target: { files: React.SetStateAction<null>[] | FileList; }; }) => {
// const files = event.target.files;
// if (event.target.files && event.target.files.length > 0) {
// setSelectedPhoto(event.target.files[0]);
// }
// };
const handleFileChange = async (event: { target: { files: any; }; }) => {
// const files = event.target.files;
// if (files && files.length > 0) {
@ -182,18 +168,22 @@ function Profile () {
{mine ? (
<div>
<motion.div onClick={() => (modalOpen ? close() : open())}>
<Link to="#" className="edit_name">
<motion.div >
<Link to="#" className="edit_name" onClick={() => (modalOpen ? close() : open())}>
{modalOpen === true ? <IoCloseCircleOutline/> : <CgEditMarkup/>}
</Link>
{modalOpen === true ? ("") : (
<>
<label htmlFor="file-input" className="edit_name"><MdOutlinePhotoLibrary/></label>
<input type="file" id="file-input" className="file-input" accept="image/*" onChange={handleFileChange} />
</>
)}
</motion.div>
<div className="file-upload-container">
<label htmlFor="file-input" className="file-label">Choose File</label>
<input type="file" id="file-input" className="file-input" accept="image/*" onChange={handleFileChange} />
{/* <div className="file-upload-container"> */}
{/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
{/* <button onClick={handleUpload} className="upload-button">Upload</button> */}
</div>
{/* </div> */}
</div>
) : (
<></>
@ -212,13 +202,49 @@ function Profile () {
function Home () {
const [move, setmove ] = useState(false);
const [user, setUser] = useState([]);
const [successQr, setSuccessQr] = useState(false);
const [successSword, setSuccessSword] = useState(false);
const [successCrown, setSuccessCrown] = useState(false);
const closeQr = () => setSuccessQr(false);
const closeSword = () => setSuccessSword(false);
const closeCrown = () => setSuccessCrown(false);
useEffect(() => {
const fetchSuccess = async () => {
try {
const tmpUser = await api.get("/profile");
setUser(tmpUser.data);
}
catch (error)
{
console.log(error);
}
};
fetchSuccess();
})
return (
<motion.div className="page"
initial={{opacity: -1}}
animate={{opacity: 1}}
exit={{opacity: -1}}>
<div>
{user.otp_verified ? (
<MdQrCodeScanner className='success' onClick={() => setSuccessQr(true)}/>
):("")}
{user.win >= 2 ? (
<GiWingedSword className="success" onClick={() => setSuccessSword(true)}/>
):("")}
{user.win >= 5 ? (
<GiCrownedSkull className="success" onClick={() => setSuccessCrown(true)}/>
):("")}
</div>
<div className="home">
<motion.div animate={{x: move ? -200: 170}}
<motion.div animate={{x: move ? -200: 120}}
transition={{type: "tween", duration: 0.5}}>
<Profile/>
</motion.div>
@ -232,6 +258,19 @@ function Home () {
onClick={ () => setmove(!move)}>
<Link to="#" className="history"><AiOutlineHistory/> Match History</Link>
</motion.div>
<AnimatePresence initial={false} onExitComplete={() => null}>
{successQr ? (
<YellowAlert handleClose={closeQr} text={"Success: You have the 2fa success!"} icon={1} />
) : ("")}
{successCrown ? (
<YellowAlert handleClose={closeCrown} text={"Success: 5 victory ? You won king slayer success!"} icon={2}/>
) : ("")}
{successSword ? (
<YellowAlert handleClose={closeSword} text={"Success: 2 victory ? You won the noobi warrior success!"} icon={3}/>
) : ("")}
</AnimatePresence>
</motion.div>
)
}

View File

@ -9,7 +9,7 @@
input.qr{
width: 20%;
border-radius: 5px;
background-color: rgb(66, 66, 66);
background-color: rgb(0, 0, 0);
margin : 1%;
color:white;
}

View File

@ -89,6 +89,17 @@ span {
border-radius: 50%;
}
.success {
height: 2%;
width: 2%;
/* border: solid; */
margin-top: 1%;
margin: 3vh;
/* margin-bottom: -12vh; */
/* border-color: black; */
/* border-radius: 50%; */
}
.header-pic{
text-align: end;
/* id: right; */

View File

@ -204,6 +204,18 @@ p {
margin: 1%;
}
.darkSubmit{
display: inline-block;
color: white;
background-color: #282155;
border-radius: 10px;
padding: 5px;
font-size: 1.2rem;
text-decoration: none;
font-weight:lighter;
margin: 1%;
}
.div_submit {
flex-direction: row;
align-items: center;
@ -276,6 +288,12 @@ p {
background-color: rgba(212, 175, 55, 0.7);
font-size: 25px;
color: rgba(255, 255, 255, 1);
flex-wrap: wrap;
}
.yellowAlert::p {
overflow-wrap: break-word;
max-width: 1000px;
}
.modalSetting{
@ -298,7 +316,7 @@ p {
.settingSecondPart{
margin-top: 10%;
margin-left: 10%;
margin-left: 5%;
/* margin-left: 20%; */
}
@ -316,3 +334,20 @@ input.in{
border-radius: 12px;
width: 70%;
}
input.in_howLong{
margin-top: 14.5%;
margin-left: 0px;
background-color: black;
color: white;
border-radius: 12px;
width: 15%;
}
.mdp{
background-color : black;
border-radius: 8px;
color: white;
width: 20%;
}

View File

@ -61,12 +61,15 @@
.page {
text-align: center;
overflow-y: scroll;
/* height: 50vh; */
/* width: 50vh; */
/* background-color: black; */
}
.profile {
align-items: center;
text-align: center;
flex-direction: row;
color: white;
}
@ -79,7 +82,7 @@
border-color: red;
/* border-image: linear-gradient(90deg, #5843e4, #5a0760); */
margin-top: 20px;
/* margin-top: 20px; */
}
.home{
@ -106,7 +109,7 @@
.div_history {
flex-direction: row;
align-items: center;
margin-top: -80px;
/* margin-top: -80px; */
}
.edit_name {

View File

@ -2,7 +2,7 @@
/* display: flex; */
/* flex-direction: column; */
/* background-color: red; */
height: 70vh;
height: 60vh;
/* padding: 15px; */
/* overflow: scroll; */

View File

@ -1,16 +1,21 @@
.playButton {
background-image: linear-gradient(90deg, #5843e4, #5a0760);;
background-image: linear-gradient(90deg, #5843e4, #5a0760);
display: flex;
flex-wrap: wrap;
overflow: hidden;
border-radius: 5vh;
color: white;
display: block;
/* display: block; */
margin: auto;
margin-top: 30vh;
padding: 2vh 5vw;
padding: 2vh 4vw;
height: 10vh;
width: 20vw;
font-size: 300%;
font-size: 250%;
text-align: center;
}
.field {
background-color: rgb(249, 249, 249);
/* border-radius: 5vh; */
@ -22,7 +27,7 @@
height: 80%;
width: 80%;
/* font-size: 300%; */
}
}
.clicked{
/* justify-content: center; */
@ -40,6 +45,29 @@
/* padding-top: 25; */
/* padding-top: 177.77% */
}
@media screen and (max-width: 900px){
.playButton{
font-size: 200%;
}
}
@media screen and (max-width: 700px){
.playButton{
font-size: 150%;
}
}
@media screen and (max-width: 530px){
.playButton{
font-size: 100%;
}
}
@media screen and (max-width: 350px){
.playButton{
font-size: 50%;
}
}
#myCanvas {
background-color: rgb(124, 47, 47);

View File

@ -11,7 +11,7 @@ services:
# command: sh -c "envsubst < /etc/nginx/conf.d/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"
ports:
- 80:80
- 8080:8080
volumes:
- ./conf/nginx.conf:/etc/nginx/conf.d/default.conf
# volumes:
@ -34,7 +34,7 @@ services:
# depends_on:
# - nginx
ports:
- 8080:8080
- 8001:8001
volumes:
- ./containers/react:/app
# - ./containers/react:/app