feat(socketio): get users online and offline (users status)
This commit is contained in:
parent
b0e8794d94
commit
526804c291
|
|
@ -17,18 +17,20 @@ let users = []
|
||||||
|
|
||||||
io.on('connection', (socket) => {
|
io.on('connection', (socket) => {
|
||||||
console.log(`🚀: ${socket.id} user just connected!`);
|
console.log(`🚀: ${socket.id} user just connected!`);
|
||||||
|
const sessionId = socket.id
|
||||||
|
|
||||||
socket.on("newuser", (data) => {
|
socket.on("newuser", (data) => {
|
||||||
users.push(data)
|
users.push(data)
|
||||||
console.log(data)
|
console.log(data)
|
||||||
|
console.log("total users: ", users)
|
||||||
|
io.emit('users', users)
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("getusers", () => {
|
|
||||||
socket.broadcast.emit('users', )
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log('🔥: A user disconnected');
|
console.log('🔥: A user disconnected');
|
||||||
|
users.splice(users.findIndex(x => x.id === sessionId), 1);
|
||||||
|
console.log("users after disconection: ", users)
|
||||||
|
io.emit('users', users)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
# ----------------------------- Local Environment ---------------------------- #
|
# ----------------------------- Local Environment ---------------------------- #
|
||||||
|
|
||||||
NEXT_PUBLIC_REST_ENPOINT="http://localhost:8000/api"
|
NEXT_PUBLIC_REST_ENPOINT="http://localhost:8000/api"
|
||||||
|
NEXT_PUBLIC_WS_ENPOINT="http://localhost:5000/"
|
||||||
# ---------------------------------------------------------------------------- #
|
# ---------------------------------------------------------------------------- #
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
2398
frontend/package-lock.json
generated
2398
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
|
|
@ -33,6 +33,8 @@
|
||||||
"react-apexcharts": "1.4.0",
|
"react-apexcharts": "1.4.0",
|
||||||
"react-dom": "18.2.0",
|
"react-dom": "18.2.0",
|
||||||
"simplebar-react": "^3.2.1",
|
"simplebar-react": "^3.2.1",
|
||||||
|
"socket.io-client": "^4.6.2",
|
||||||
|
"styled-components": "^6.0.0-rc.3",
|
||||||
"yup": "1.0.0"
|
"yup": "1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|
|
||||||
59
frontend/src/contexts/socketio-context.js
Normal file
59
frontend/src/contexts/socketio-context.js
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { createContext, useContext, useEffect, useState } from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
|
import io from 'socket.io-client';
|
||||||
|
import { useAuth } from 'src/hooks/use-auth';
|
||||||
|
|
||||||
|
// The role of this context is to propagate socketio io state through app tree
|
||||||
|
export const WsContext = createContext({ undefined });
|
||||||
|
|
||||||
|
export const WsProvider = (props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const [users, setUsers] = useState([])
|
||||||
|
const auth = useAuth()
|
||||||
|
const initialize = async () => {
|
||||||
|
// Prevent from calling twice in development mode with React.StrictMode enable
|
||||||
|
const socket = io(process.env.NEXT_PUBLIC_WS_ENPOINT)
|
||||||
|
|
||||||
|
socket.on('connect', () => {
|
||||||
|
console.log('[IO] Connect => A new connection has been established')
|
||||||
|
|
||||||
|
socket.on("users", (data) => {
|
||||||
|
setUsers(data)
|
||||||
|
console.log("data received from users event: ", users)
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.emit("newuser",{
|
||||||
|
id:socket.id,
|
||||||
|
name: window.sessionStorage.getItem("email")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(
|
||||||
|
() => {
|
||||||
|
if(auth.isAuthenticated){
|
||||||
|
initialize();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[auth.isAuthenticated]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WsContext.Provider
|
||||||
|
value={{
|
||||||
|
users,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</WsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
WsProvider.propTypes = {
|
||||||
|
children: PropTypes.node
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WsConsumer = WsContext.Consumer;
|
||||||
|
|
||||||
|
export const useWsContext = () => useContext(WsContext);
|
||||||
|
|
@ -1,12 +1,8 @@
|
||||||
import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon';
|
import ChartBarIcon from '@heroicons/react/24/solid/ChartBarIcon';
|
||||||
import CogIcon from '@heroicons/react/24/solid/CogIcon';
|
import CogIcon from '@heroicons/react/24/solid/CogIcon';
|
||||||
import LockClosedIcon from '@heroicons/react/24/solid/LockClosedIcon';
|
import ChatBubbleLeftRightIcon from '@heroicons/react/24/solid/ChatBubbleLeftRightIcon'
|
||||||
import ShoppingBagIcon from '@heroicons/react/24/solid/ShoppingBagIcon';
|
import MapIcon from '@heroicons/react/24/solid/MapIcon'
|
||||||
import UserIcon from '@heroicons/react/24/solid/UserIcon';
|
|
||||||
import UserPlusIcon from '@heroicons/react/24/solid/UserPlusIcon';
|
|
||||||
import UsersIcon from '@heroicons/react/24/solid/UsersIcon';
|
|
||||||
import CpuChip from '@heroicons/react/24/solid/CpuChipIcon';
|
import CpuChip from '@heroicons/react/24/solid/CpuChipIcon';
|
||||||
import XCircleIcon from '@heroicons/react/24/solid/XCircleIcon';
|
|
||||||
import { SvgIcon } from '@mui/material';
|
import { SvgIcon } from '@mui/material';
|
||||||
|
|
||||||
export const items = [
|
export const items = [
|
||||||
|
|
@ -28,6 +24,24 @@ export const items = [
|
||||||
</SvgIcon>
|
</SvgIcon>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Chat',
|
||||||
|
path: '/chat',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<ChatBubbleLeftRightIcon/>
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Map',
|
||||||
|
path: '/',
|
||||||
|
icon: (
|
||||||
|
<SvgIcon fontSize="small">
|
||||||
|
<MapIcon/>
|
||||||
|
</SvgIcon>
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Settings',
|
title: 'Settings',
|
||||||
path: '/settings',
|
path: '/settings',
|
||||||
|
|
|
||||||
147
frontend/src/pages/chat.js
Normal file
147
frontend/src/pages/chat.js
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
import React, { useEffect, useState, useContext } from "react";
|
||||||
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
import PhoneIcon from "@heroicons/react/24/solid/PhoneIcon";
|
||||||
|
import PhoneXMarkIcon from "@heroicons/react/24/solid/PhoneXMarkIcon"
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
Box,
|
||||||
|
CardContent,
|
||||||
|
Container,
|
||||||
|
SvgIcon,
|
||||||
|
CircularProgress,
|
||||||
|
Avatar,
|
||||||
|
Backdrop,
|
||||||
|
} from "@mui/material";
|
||||||
|
import { WsContext } from "src/contexts/socketio-context";
|
||||||
|
|
||||||
|
const Page = () => {
|
||||||
|
|
||||||
|
//const [isConnected, setIsConnected] = useState(socket.connected);
|
||||||
|
const [users, setUsers] = useState([])
|
||||||
|
//const [onlineUsers, setOnlineUsers] = useState([])
|
||||||
|
|
||||||
|
const ws = useContext(WsContext)
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
var myHeaders = new Headers();
|
||||||
|
myHeaders.append("Content-Type", "application/json");
|
||||||
|
myHeaders.append("Authorization", localStorage.getItem("token"));
|
||||||
|
|
||||||
|
var requestOptions = {
|
||||||
|
method: 'GET',
|
||||||
|
headers: myHeaders,
|
||||||
|
redirect: 'follow'
|
||||||
|
};
|
||||||
|
|
||||||
|
fetch(`${process.env.NEXT_PUBLIC_REST_ENPOINT}/users`,requestOptions)
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(result => {
|
||||||
|
// let teste = JSON.stringify(JSON.parse(result), null, 2)
|
||||||
|
setUsers(result)
|
||||||
|
})
|
||||||
|
.catch(error => console.log('error', error));
|
||||||
|
},[])
|
||||||
|
|
||||||
|
const renderUsers = () => {
|
||||||
|
console.log("users: ", users)
|
||||||
|
console.log("wsUsers: ", ws.users)
|
||||||
|
if(users.length == 0){
|
||||||
|
console.log("users is empty")
|
||||||
|
return (
|
||||||
|
<div style={{display:'flex', justifyContent:'center'}} height={'100%'} >
|
||||||
|
<CircularProgress color="inherit" width='100%'/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}else {
|
||||||
|
return (
|
||||||
|
<Card sx={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent:'center',
|
||||||
|
}}>
|
||||||
|
<CardContent>
|
||||||
|
|
||||||
|
<Container sx={{display:'flex',justifyContent:'center'}}>
|
||||||
|
{users.map((x)=> {
|
||||||
|
|
||||||
|
let color = "#CB1E02"
|
||||||
|
let status = "offline"
|
||||||
|
|
||||||
|
if (ws.users.findIndex(y => y.name === x.email) >= 0){
|
||||||
|
console.log("user: "+x.email+" is online")
|
||||||
|
//color = "#11ADFB"
|
||||||
|
color = "#17A000"
|
||||||
|
status = "online"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x.email !== window.sessionStorage.getItem("email")){
|
||||||
|
return (
|
||||||
|
<Box sx={{margin:"30px",textAlign:'center'}}>
|
||||||
|
<Avatar
|
||||||
|
sx={{
|
||||||
|
height: 150,
|
||||||
|
width: 150,
|
||||||
|
border: '3px solid '+color
|
||||||
|
}}
|
||||||
|
src={"/assets/avatars/default-avatar.png"}
|
||||||
|
/>
|
||||||
|
<div style={{marginTop:'10px'}}>
|
||||||
|
</div>
|
||||||
|
<SvgIcon
|
||||||
|
sx={{cursor:'pointer'}}
|
||||||
|
>
|
||||||
|
{status === "online" ?
|
||||||
|
<PhoneIcon
|
||||||
|
color={color}
|
||||||
|
onClick={()=>{
|
||||||
|
console.log("call", x.email)
|
||||||
|
}}
|
||||||
|
title={"call"}
|
||||||
|
/>
|
||||||
|
:
|
||||||
|
<PhoneXMarkIcon
|
||||||
|
color={color}
|
||||||
|
onClick={()=>{
|
||||||
|
console.log("call", x.email)
|
||||||
|
}}
|
||||||
|
title={"offline"}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</SvgIcon>
|
||||||
|
<p style={{marginTop:'-2.5px'}}>{x.email}</p>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</Container>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(ws.users.length > 1 ?
|
||||||
|
<Box
|
||||||
|
component="main"
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
py: 10,
|
||||||
|
alignItems: 'center',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Container maxWidth="md">
|
||||||
|
{renderUsers()}
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
:
|
||||||
|
<CircularProgress color="inherit" />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Page.getLayout = (page) => (
|
||||||
|
<DashboardLayout>
|
||||||
|
{page}
|
||||||
|
</DashboardLayout>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Page;
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Head from 'next/head';
|
import Head from 'next/head';
|
||||||
|
import React, { useState } from 'react';
|
||||||
import { subDays, subHours } from 'date-fns';
|
import { subDays, subHours } from 'date-fns';
|
||||||
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
import { Box, Container, Unstable_Grid2 as Grid } from '@mui/material';
|
||||||
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
import { Layout as DashboardLayout } from 'src/layouts/dashboard/layout';
|
||||||
|
|
@ -13,7 +14,9 @@ import { OverviewTraffic } from 'src/sections/overview/overview-traffic';
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
const Page = () => (
|
const Page = () => {
|
||||||
|
|
||||||
|
return(
|
||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>
|
<title>
|
||||||
|
|
@ -109,8 +112,8 @@ const Page = () => (
|
||||||
</Grid>
|
</Grid>
|
||||||
</Container>
|
</Container>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</>)
|
||||||
);
|
};
|
||||||
|
|
||||||
Page.getLayout = (page) => (
|
Page.getLayout = (page) => (
|
||||||
<DashboardLayout>
|
<DashboardLayout>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user