Commit 1c67334e authored by Kannan V M's avatar Kannan V M
Browse files

Merge branch 'devel' into 'master'

Add multi room support and moderation

See merge request !5
parents f24452df d11149b6
module.exports = {
env: {
node: true,
es2021: true,
},
extends: ['airbnb-base'],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
rules: {
'no-unused-expressions': 'warn',
'comma-dangle': [
'error',
{
arrays: 'always-multiline',
objects: 'always-multiline',
imports: 'never',
exports: 'never',
functions: 'never',
}],
},
};
......@@ -201,4 +201,7 @@ tags
# End of https://www.gitignore.io/api/vim
# config file
config.js
\ No newline at end of file
config.js
# log file
sfcamp
module.exports = {
port: 3000,
filename: "knowledge.json",
username: "treehouse",
password: "fsc2kMod!"
rooms: {"":"en", "":"ml", "":"hi", "":"ma"},
modNamespace: "",
}
var config = require('./config');
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var path = require('path');
var fs = require('fs')
var port = config.port || 3000;
var filename = config.filename || 'knowledge.json';
var filepath = path.join('/tmp', filename);
//asking server to listen to 3000
const fs = require('fs');
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server);
const path = require('path');
const config = require('./config');
const port = config.port || 3000;
// const filename = config.filename || 'knowledge.json';
// asking server to listen to 3000
server.listen(port, () => {
console.log('Server listening at port %d', port);
console.log('Server listening at port %d', port);
});
//assign static path to public folder
app.use(express.static(path.join(__dirname, '/')));
// assign static path to public folder
app.use(express.static(path.join(__dirname, '/public/')));
const modRooms = [];
app.get('/mod', (req, res) => {
res.sendFile(__dirname + '/mod.html');
Object.keys(config.rooms).forEach((key) => {
modRooms.push(`/${key}`);
});
app.get('/', (req, res) => {
res.sendFile(__dirname + '/index.html');
app.get(modRooms, (req, res) => {
res.sendFile(path.join(__dirname, '/public/mod.html'));
});
const user = io.of('/');
const mod = io.of('/mod');
app.get(['/en', '/ml', '/hi'], (req, res) => {
res.sendFile(path.join(__dirname, '/public/user.html'));
});
const userNamespace = io.of('/user');
const modNamespace = io.of(config.modNamespace);
var leafids = [];
for (let i=7; i<332; i++) {
leafID = "path" + i.toString();
i += 3;
leafids.push(leafID);
const leafIds = [];
for (let i = 7; i < 332; i += 1) {
const leafId = `path${i.toString()}`;
i += 3;
leafIds.push(leafId);
}
var leavesdata = {}; //object of structure {socketid1: {leaftext:"text",leafid:"leafid"}, ...}
var leafno = 0;
var socketids = [];
user.on('connection', (socket) => {
let id = socket.id;
socket.emit('leaves data', leavesdata);
socket.on('leaf text', (leafdata) => {
if(!socketids.includes(id)) { //checks if socketid is already assigned with leafid
socketids.push(id);
leafdata["leafid"] = leafids[leafno]; //if not assign leafid
leafno = leafno+1;
leavesdata[id] = leafdata;
} else {
leavesdata[id]["leaftext"] = leafdata["leaftext"]; //if yes update the message
leafdata = leavesdata[id];
};
io.emit('leaf data', leafdata);
io.of('mod').emit('leaves data', leavesdata);
fs.writeFileSync(filepath, JSON.stringify(leavesdata), function(err) {
if(err) return console.error(err);
});
const roomLeafIds = {
en: { leafIds: [...leafIds] },
ml: { leafIds: [...leafIds] },
hi: { leafIds: [...leafIds] },
};
const leavesData = {
en: {},
ml: {},
hi: {},
ma: {},
};
// object of structure {socketid1: {userName:"", leafText:"",leafId:""}, ...}
const fileSave = function fileSave(roomId) {
const date = new Date();
const filename = `sfcamp_${roomId}${date.getDate()}${date.getHours()}${date.getMinutes()}`;
const filepath = path.join(__dirname, 'sfcamp', filename);
if (Object.entries(leavesData[roomId]).length !== 0) {
fs.writeFileSync(filepath, JSON.stringify(leavesData[roomId]), (err) => {
if (err) return console.error(err);
});
}
};
const notifyEveryone = function notifyUserStateChangeToEveryone(roomId) {
userNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
modNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
fileSave(roomId);
};
const updateLeavesData = function updateLeavesDataAndNotify(socketId, roomId, leafData) {
if (!(leavesData[roomId][socketId].leafId)) {
leavesData[roomId][socketId].leafId = roomLeafIds[roomId].leafIds.shift();
}
leavesData[roomId][socketId] = { ...leavesData[roomId][socketId], ...leafData };
notifyEveryone(roomId);
};
const deleteLeaf = function deleteLeafAndNotifyEveryone(socketId, roomId) {
const { leafId } = leavesData[roomId][socketId];
delete leavesData[roomId][socketId].leafText;
delete leavesData[roomId][socketId].leafId;
roomLeafIds[roomId].leafIds.push(leafId);
userNamespace.to(roomId).emit('leaf delete', leafId);
modNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
fileSave(roomId);
};
userNamespace.on('connection', (socket) => {
const socketId = socket.id;
const { userName, roomId } = socket.handshake.query;
if (roomId === 'en' || roomId === 'ml' || roomId === 'hi') {
socket.join(roomId);
leavesData[roomId][socketId] = { userName }; // user
socket.emit('leaves data', leavesData[roomId]);
modNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
socket.on('leaf text', (leafText) => {
const leafData = {};
leafData.leafText = leafText;
if (!(leavesData[roomId][socketId].leafId)) {
leafData.leafId = roomLeafIds[roomId].leafIds.shift();
}
updateLeavesData(socketId, roomId, leafData);
});
} else {
socket.disconnect();
}
});
mod.on('connection', (socket) => {
io.of('mod').emit('leaves data', leavesdata);
socket.on('message delete', id => {
io.emit('delete text', leavesdata[id]["leafid"]);
delete leavesdata[id];
const index = socketids.indexOf(id);
if (index > -1) {
socketids.splice(index, 1);
}
io.of('mod').emit('leaves data', leavesdata);
modNamespace.on('connection', (socket) => {
const { roomName } = socket.handshake.query;
const roomId = config.rooms[roomName];
if (roomId) {
socket.join(roomId);
modNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
socket.on('allow user', (socketId) => {
userNamespace.to(socketId).emit('enter knowledge');
});
socket.on('leaf delete', (socketId) => {
deleteLeaf(socketId, roomId);
});
socket.on('leaf update', (data) => {
updateLeavesData(data.socketId, roomId, data.leafData);
});
socket.on('user delete', (socketId) => {
deleteLeaf(socketId, roomId);
userNamespace.sockets.get(socketId).disconnect();
delete leavesData[roomId][socketId];
modNamespace.to(roomId).emit('leaves data', leavesData[roomId]);
fileSave(roomId);
});
socket.on('save exit', filename => {
console.log("nsdiisd");
leavesdata = {};
leafno = 0;
socketids = [];
io.emit('leaves data', leavesdata);
io.of('mod').emit('leaves data', leavesdata);
socket.on('save exit', () => { // save data into file
fileSave(roomId);
userNamespace.in(roomId).disconnectSockets();
Object.keys(leavesData[roomId]).forEach((key) => {
delete leavesData[roomId][key];
});
roomLeafIds[roomId].leafIds = leafIds;
notifyEveryone(roomId);
});
} else {
socket.disconnect();
}
});
module.exports = {
"src/**/\*.js": "eslint --cache --fix",
};
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Document</title>
</head>
<body>
<ul id="listid">
</ul>
<br/>
<br/>
<br/>
<br/>
<button onclick="savenExit()">Save and Reset</button>
<script src="socket.io.js"></script>
<script>
const mod = io.connect('/mod');
mod.on('leaves data', (leavesdata) => {
var text = {};
var x;
document.getElementById("listid").innerHTML = '';
for (x in leavesdata) {
document.getElementById("listid").innerHTML += '<li>'+leavesdata[x]["leaftext"]+'<button onclick="contentRemove('+"'"+x+"'"+')">Remove</button></li>';
};
});
function contentRemove(socketid){
mod.emit('message delete', socketid);
};
function savenExit(){
var d = new Date();
var filename = d.getTime();
mod.emit('save exit', filename);
}
</script>
</body>
</html>
This diff is collapsed.
{
"name": "knowledge-tree",
"version": "0.0.8",
"description": "A tree to show use inputs",
"license": "GPLv3",
"version": "0.9.1",
"description": "Knowledge Tree game for fsci camp",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"repository": {
"type": "git",
"url": "gitlab@fsci:community/knowledge-tree.git"
},
"keywords": [
"Knowledge",
"Tree"
],
"author": "",
"license": "AGPL-3.0-or-later",
"dependencies": {
"express.js": "^1.0.0",
"socket.io": "^2.3.0"
"express": "^4.17.1",
"socket.io": "^4.3.2"
},
"devDependencies": {
"eslint": "^8.2.0",
"eslint-config-airbnb-base": "^15.0.0",
"lint-staged": "^11.2.6"
}
}
module.exports = {
singleQuote: true,
};
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Knowledge Tree - Free Software Camp</title>
<style>
body {
margin: 0;
background: #3c3c3c;
}
svg {
height: 80vh;
margin-top: 5vh;
}
svg path.leaf-inactive {
fill: rgba(145, 158, 94, 0.2);
-webkit-transform: scale(0.7);
-webkit-transform-origin: 50% 50%;
transform: scale(0.7);
transform-origin: 50% 50%;
transform-box: fill-box;
}
svg path.leaf-active {
animation: shake-and-grow 3s cubic-bezier(.36, .07, .19, .97) both;
fill: rgba(145, 158, 94);
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
transform-box: fill-box;
cursor: pointer;
}
@keyframes shake-and-grow {
0% {
transform: scale(0.7);
}
2%,
18% {
transform: translate3d(-1px, 0, 0) scale(0.7);
}
4%,
16% {
transform: translate3d(2px, 0, 0) scale(0.7);
}
6%,
10%,
14% {
transform: translate3d(-2px, 0, 0) scale(0.7);
}
8%,
12% {
transform: translate3d(1px, 0, 0) scale(0.7);
}
18% {
transform: sclae(0.8);
}
100% {
transform: scale(1);
}
}
svg path.leaf:hover {
animation: shake 0.82s cubic-bezier(.36, .07, .19, .97) both;
transform: translate3d(0, 0, 0) scale(0.7);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%,
90% {
transform: translate3d(-1px, 0, 0) scale(0.7);
}
20%,
80% {
transform: translate3d(2px, 0, 0) scale(0.7);
}
30%,
50%,
70% {
transform: translate3d(-2px, 0, 0) scale(0.7);
}
40%,
60% {
transform: translate3d(1px, 0, 0) scale(0.7);
}
}
input[type="submit"], button {
border: none;
border-radius: 5px;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
margin: 4px 2px;
cursor: pointer;
}
#knowledge-form {
text-align: center;
}
#knowledge-input, #knowledge-submit, #knowledge-edit {
margin: 10px auto;
}
#knowledge-input {
border: none;
border-radius: 5px;
width: 420px;
padding: 10px;
}
#knowledge-submit {
background: #008CBA;
font-size: 14px;
color: white;
}
#knowledge-edit {
background: #e5721f;
display: none;
}
#knowledge {
display: none;
position: absolute;
top: 50%;
left: 50%;
width: 57vw;
min-height: 35vw;
margin: 0 auto;
transform: translate(-50%, -60%);
z-index: -1;
}
#knowledge p {
display: none;
position: absolute;
top: 50%;
left: 50%;
margin: 0 auto;
font-size: 4vw;
text-align: center;
text-shadow: 3px 3px 3px #AAAAAA;
text-transform: capitalize;
transform: translate(-50%, -50%);
}
#close-button {
display: none;
width: 120px;
margin: -10vh auto;
text-align: center;
}
#close-button button {
background: #f44336;
}
@media only screen and (max-width: 480px) {
#knowledge-form {
margin-top: -15vh;
}
#knowledge-input {
width: 280px;
}
#close-button {
margin-top: -15vh;
}
</style>
<link rel="stylesheet" href="style.css">
</head>
<body>
<svg id="tree" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="100%" height="100%" viewBox="0 0 3508 2481" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" id="svg405" sodipodi:docname="tree_white_bg.svg" inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<div class="dropdown">
<button id="lang-select" onclick="myFunction()" class="dropbtn">Select Language</button>
<div id="myDropdown" class="dropdown-content">
<a href="/en">English</a>
<a href="/hi">Hindi</a>
<a href="/ma">Marathi</a>
<a href="/ml">Malayalam</a>
</div>
</div>
<svg id="tree" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="100%" height="100%" viewBox="0 0 3508 2481" version="1.1" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;" id="svg405" sodipodi:docname="tree_white_bg.svg" inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)">
<metadata id="metadata411">
<rdf:RDF>
<cc:Work rdf:about="">
......@@ -506,120 +334,24 @@
</g>
</svg>
<form id="knowledge-form">
<input id="knowledge-input" type="text" maxlength="50">
<input id="knowledge-submit" type="submit" value="Submit" onclick="event.preventDefault(); addKnowledgeItem();">
<input id="knowledge-edit" type="submit" value="Edit" onclick="event.preventDefault(); editKnowledgeItem();">
</form>
<div id="knowledge"></div>
<div id="close-button">
<button onclick="closeLeaf()">Close</button>
</div>
<script src="socket.io.js"></script>
<script>
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-3.0-or-later
var socket = io();
socket.on('leaves data', (leavesdata) => {
let i = 0;
for(leaf in leavesdata) {
leafdata = leavesdata[leaf];
growLeaves(i, leafdata);
i++;
}
function growLeaves(i, leafdata) {
setTimeout(function() {
growLeaf(leafdata);
}, 300 * i);
}
});
function addKnowledgeItem() {
leaftext = document.getElementById('knowledge-input').value;
var leafdata = { "leaftext": leaftext }
socket.emit("leaf text", leafdata);
document.getElementById("knowledge-input").style.display = "none";