How to associate a socket.id to an array item? - javascript

I'm trying to make a chat-room where connected users' names are displayed in an 'Online Users' section.
The code below will add each users' name to an array and display the contents of the array.
However, if a user leaves, their username isn't removed from the array. If I pop the array, it'll likely not remove the correct username. This makes me think I should, somehow, associate the socket-id with the username given, and create a disconnect event that removes the correct username from the array, and displays the updated version of the array. How could I alter this code to incorporate this?
script.js:
var user = user;
if (!user) {
user = prompt('Please choose a username:');
if (!user) {
alert('Your name has been set to "Anonymous"');
user = "Anonymous"
items.push(user);
} else {
alert('Your name has been set to "'+ user +'"');
}
}
socket.emit('theitems', user);
socket.on('theitems', function (data) {
$('.dispUser').html(data);
console.log(data);
});
server.js:
var newitems = [];
server(socket('theitems', ctx => { newitems.push(ctx.data); console.log(ctx.data); ctx.io.emit('theitems', newitems); }));

I believe that on user connection you update your array of users with new user, something like:
let users = [];
sockets.on('connection', socket) => {
// This is the user associated with a new socket connection.
// User that you will need to remove when connection is closed.
const user = { /* create your user */ };
users.push(user);
// Then you can subscribe to socket disconnect event
socket.on('disconnect', () => {
users = users.filter(u => u !== user); // removing disconnected user
// And then here you can notify your front-end with an updated users array
});
});

Related

object of data specific to current logged in user

The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.
I am trying to create an object i.e. userA and update it, as the
user saves each field value (i.e. BaseMap: basemapSaved), save
the updated state in local storage, then retrieve saved state using
local storage. So, when the user logs out, then logs back in, their
saved data is still there specific to their username.
Below is my most recent attempt (full js): Any pointers? Am I going about this all wrong?
UPDATED ATTEMPT BELOW WITH BOUNTY.
I am simply trying to save an object of data and a field within it (i.e. userA.BaseMap.basemapSaved;) with local storage, on click.
I later want to parse that saved object in local storage, get that field, and update my API object i.e. object.data.field (userA.BaseMap.basemapSaved;) with the value saved and gathered. I can do this pro grammatically pretty easy, but the idea is to save the state per user, so they can log out, then log back in and have their selection saved.
// Here I am trying to initialize the variables
var currentUser;
var basemapSaved;
var userA[key] = {};
// This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
currentUser = node.textContent;
var key = currentUser;
console.log("current user is:" + key);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', ()=>{
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
function BaseMapSaver() {
savebtnBM.addEventListener('click', ()=>{
checkUser();
// This is where I get the data from my API to save, gathers fine
basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
// Below I am trying to set it, at first without the object key but would like to use the key
var saveMap = localStorage.setItem('userA', JSON.stringify(userA));
console.log(userA);
});
}
// Home button
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', ()=>{
checkUser();
// Here I try to parse the value from local storage object
const userAParseValue = JSON.parse(localStorage.getItem('userA'));
// Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
}
};
});
It Should work check addEventListener function:-
Hbtn.addEventListener('click', ()=>{
checkUser();
const userAParseValue = JSON.parse(localStorage.getItem('userA'));
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
myApp.widgets.bigData.Gallery.map = {
Item: {
id: userA.BaseMap.basemapSaved
}
};
});
You can use localStorage and the approach you're trying to make work, but you'll end up with localStorage having a separate object for each user. If that's OK then you use localStorage after load to check if a user is logged in and then load the users' data. Then update the data to the localStorage when the values change. You may check inline comments for details:
HTML if there is a user logged in:
<h3>User <span class="username"><?php echo $user; ?></span> is logged in</h3>
<form method="post">
<input type="hidden" name="action" value="logout"/>
<button type="submit">Logout</button>
</form>
<hr/>
<div>
<h2>User counter: <span id="counter"></span></h2>
<div>
<button id="inc-counter">Increase</button>
<button id="dec-counter">Decrease</button>
</div>
</div>
Javascript to handle localStorage:
// Get user stored on page HTML
const user = document.querySelector("span.username");
// Something to update/alter using user data and/or user input
const counter = document.querySelector("#counter");
const incCounter = document.querySelector("#inc-counter");
const decCounter = document.querySelector("#dec-counter");
if(user) { // If there is a user logged in
// Get the username
const username = user.textContent;
// Get the localStorage the belongs to that user (using username for key)
let storageUser = JSON.parse(localStorage.getItem(username) || 'null');
// Use default user object if the user has no previous settings stored
let currentUser = storageUser || {
BaseMap: {
counter: 0
}
};
// Display the user data
function displayCounter() {
const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;
counter.textContent = userCounter;
}
// Alter the user data and save it to localStorage user settings object
function alterCounter(addToCounter) {
// Check if BaseMap object exists or default
const BaseMap = 'BaseMap' in currentUser ? currentUser.BaseMap : {};
// Check if data exists or default
let userCounter = 'counter' in BaseMap ? BaseMap.counter : 0;
// Alter user data according to user input
userCounter += addToCounter;
// Change user settings object
currentUser['BaseMap']['counter'] = userCounter;
// Save user settings object
localStorage.setItem(username, JSON.stringify(currentUser));
// Display altered user data
displayCounter();
}
// Initialize by display retrieved/default data
displayCounter();
// Add event listeners to user inputs
incCounter.addEventListener('click', () => alterCounter(1));
decCounter.addEventListener('click', () => alterCounter(-1));
}
You can check an online example that I've made at the link below:
https://zikro.gr/dbg/so/60010743/ (Users userA, userB both with password 1234 can be used for demonstration)
That will work and retrieve/save user data to the localStorage using username for each user. Keep in mind that this method will only save the user settings for a specific browser location. If you want to have user settings when the user logs in from anywhere, then you should go with the traditional workaround which is based on server session, but it's not so flexible when it comes to user settings because you'll have to update each data/setting using server requests each time the user makes a change which it's possible but it requires server + client implementation.
A combination of both server side settings storage + server session + client localStorage would be the best approach to this situation.
here is my answer
<html>
<body>
<span class="border username">121</span>
<div class="border basemap">Base Map</div>
<div class="border saveBtn">Save</div>
<div id="home" class="border">Home</div>
<style>
.border{
border: solid gray 1px;
border-radius: 2px;
text-align: center;
background: #eee;
margin: 5px;
width: 100px;
}
</style>
<script type="text/javascript">
// Here I am trying to initialize the variables
var key = 1;
var currentUser;
var basemapSaved;
var userA = {
BaseMap: {
id: 1234
}
};
var app = {
widget: {
Manager: {
Gallery: {
activeBasemap: {
Item: {
id: {
portalItem: {
id: 1234 // this is where I want to load saved value from local storage object
}
}
}
}
}
}
}
};
// This function I am getting the logged in username, I want to set this as the object key in userA i.e. userA[key]
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
currentUser = node.textContent;
var key = currentUser;
console.log("current user is:" + key);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', ()=>{
console.log("basemapMod click");
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
function BaseMapSaver() {
var savebtnBM = document.getElementsByClassName("saveBtn")[0];
savebtnBM.addEventListener('click', ()=>{
console.log("savebtnBM click");
checkUser();
// This is where I get the data from my API to save, gathers fine
basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id.portalItem.id;
/** saving users, instead of userA */
const userAParseValue = JSON.parse(localStorage.getItem('users'));
userA.BaseMap.basemapSaved = basemapSaved;
const finalUsers = {...userAParseValue, userA}
// Below I am trying to set it, at first without the object key but would like to use the key
var saveMap = localStorage.setItem('users', JSON.stringify(finalUsers));
console.log(userA);
});
}
// Home button
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', ()=>{
console.log("defaultViewHbtn click");
checkUser();
// Here I try to parse the value from local storage object
const userAParseValue = JSON.parse(localStorage.getItem('users'));
// Errors with Uncaught TypeError: Cannot read property 'BaseMap' of undefined
userBaseMap = userAParseValue.userA.BaseMap.basemapSaved;
console.log(userBaseMap);
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userA.BaseMap.basemapSaved // this is where I want to load saved value from local storage object
}
};
});
</script>
</body>
</html>
I changed a few structures which were not coherent. Saving and loading them was causing discrepancies. I also suggest storing all users in a single object and accessing the data from userMap because multiple users can use same browser.
Based on the two requirements that you have defined in your original question, this should do what you ask.
The goal is the user can save up to 7 field vals in obj userA, logout, log back in and the saved vals are there, retrievable. Specific to each user.
I am trying to create an object i.e. userA and update it, as the user saves each field value (i.e. BaseMap: basemapSaved), save the updated state in local storage, then retrieve saved state using local storage. So, when the user logs out, then logs back in, their saved data is still there specific to their username.
// retrieve user from localstorage. defaults to {}
// This looks to retrieve the user from local storage by username.
// Returns a `userObj` an object with two properties.
// `username` - the name of the user
// `user` - the stored object that was retrieved from local storage.
// defaults to {} if nothing in user storage
// Not a good strategy btw, a lot of us share the same names :)
function getUser(username) {
let user = localStorage.getItem(username) || {};
try {
user = JSON.parse(user);
} catch (e) {
user = {};
}
return { username, user }
}
// Store user object in localstorage
// Store a user in local storage, keyed by their username
function setUser(username, user) {
localStorage.setItem(username, JSON.stringify(user));
}
// set a key/ value on user object in localstorage
// Don't allow anymore than 7 properties to be stored on the user object
function setUserProperty(userObj, key, value) {
let { username, user } = userObj;
if (Object.keys(user).length > 7) {
throw new Error('user properties exceeds 7')
}
user[key] = value;
setUser(username, user);
}
// modified to return a user from local storage or {}
function checkUser() {
var node = document.querySelectorAll("span.username")[0];
const currentUser = node.textContent;
return getUser(currentUser);
}
// This is just a handler to wait to my basemap gallery loads before trying to save
var basemapMod = document.getElementsByClassName("basemap")[0];
basemapMod.addEventListener('click', () => {
setTimeout(
function() {
BaseMapSaver();
}, 2000);
});
// Fyi Capitals indicate constructors - not functions!
function BaseMapSaver() {
savebtnBM.addEventListener('click', () => {
const userObj = checkUser(); // get the user from localstorage
const basemapSaved = app.widget.Manager.Gallery.activeBasemap.Item.id;
setUserProperty(userObj, 'basemap', basemapSaved) // store the basemap on the user object in local storage with the key 'basemap'
console.log(JSON.stringify(userObj));
});
}
var defaultViewHbtn = document.getElementById("home");
defaultViewHbtn.addEventListener('click', () => {
// get user from localstorage
const { user } = checkUser();
const userBaseMap = user.basemap
// if we have a store basemap
if (userBaseMap) {
app.widget.Manager.Gallery.activeBasemap.Item.id = {
portalItem: {
id: userBaseMap // load it
}
};
}
});
There are many ways to handle this depending upon your use case. You have specifically mentioned LocalStorage hence everyone is suggesting the same but cookies will fit your bill as well as long as you handle the expiry time properly for them.
Local Storage
Make an Object of fields you will like to store for that user
let obj = {'keyname' : value, 'keyname' : value};
//store it - mapping it with user
localStorage.setItem('userID', JSON.stringify(obj));
//retrieve and use on login success
let ret_obj= localStorage.getItem('userID');
Cookies
You can set an arbitrary expiration time and then you again have choice of choosing just one variable or store it as a JSON itself.
document.cookie = "userName=Robert; expires=Fri, 31 Dec 9999 23:59:59 GMT";
*Cookies will hold limited amount of data, as in not huge data (Which I don't think is the use case here because I checked your jsfiddle example, you are basically trying to store some data)
If you want to store JSON data in cookies check this out Click Here
*Why am I suggesting cookies? Many enterprises already do something similar for example even post logging out when you visit a website
they will display your name and ask you to sign-in, it is just a
personalisation addition.

Create a blank profile when user signs up

I know this question has been asked on here once before but I need to know if parse has done anything about it.
My default user table has a pointer field to a UserProfile class.
On signup I have this cloud code below.
Parse.Cloud.beforeSave(Parse.User,async (request)=>{
const user = request.object;
//Making a new Teacherprofile Class
const Objectextension = Parse.Object.extend("TeacherProfile");
const teacherProfile = new Objectextension();
teacherProfile.set("name", "harry");
//Putting teacher profile pointer
user.set("tProfile",teacherProfile);
});
This just dosent not work and results in a timeout.Is there anyway to create the userprofile on before save and associate it to the User Table ? Thanks
UPDATE
This is the working code.
Parse.Cloud.beforeSave(Parse.User,async (request)=>{
const user = request.object;
//if user id does not exists
//it is a new user
if (!user.id) {
//Making a new User Profile Object
const profileObject = Parse.Object.extend("TeacherProfile");
const teacherProfile = new profileObject();
teacherProfile.set("name", "harry");
await teacherProfile.save(null,{ useMasterKey: true });
//Putting teacher profile pointer in user
user.set("tProfile",teacherProfile);
}else{
console.log('old user');
}
});
After a bit more of experimentation I have come to the conclusion that before save is not at all advisable for User Profile Creation.
When signing up lets say the username or email already exists , then the signup does not happen but the profile is saved regardlesss.
So I would advice against it
Or you can use this code.
Parse.Cloud.afterSave(Parse.User, (request) => {
if(!request.original){
//Object saved for first time. This codes will work just first time. And will not work after object saved again.
const user = request.object;
const profileObject = Parse.Object.extend("TeacherProfile");
const teacherProfile = new profileObject();
teacherProfile.set("name", "harry");
await teacherProfile.save(null,{ useMasterKey: true });
//Putting teacher profile pointer in user
user.set("tProfile",teacherProfile);
user.save(null,{useMasterKey:true});
}
});

Input Value doesn't save, when pushing onto array gives undefined value

I am trying to update the user account details in firebase but I have noticed that the input value for one of my fields keeps coming up as undefined even when I console.log it. I am working in two files one is a loginjs file in which I am defining the user input.
signUpForm.addEventListener('click', function (e) {
e.preventDefault();
isSigningUp = true;
var email = signUpEmailInput.value;
var password = signUpPasswordInput.value;
var displayNameUser = displayNameInput.value;
var userPrivateKey = signUpPrivateKey.value;
var user = firebase.auth().currentUser;
var photoURL = "https://www.gravatar.com/avatar/" + md5(email);
if (signUpPasswordInput.value !== signUpPasswordConfirmInput.value) {
setSignUpError('Passwords do not match!');
} else if (!displayNameUser) {
setSignUpError("Display Name is required!");
} else if (!userPrivateKey) {
setSignUpError('You need to set a Private Key!');
} else {
auth.createUserWithEmailAndPassword(email, password)
.then(function (user) {
user.updateProfile({
displayName: displayNameUser,
photoURL: photoURL,
privateKey: userPrivateKey
}).then(function () {
// Update successful.
window.location.href = 'chat.html';
}).catch(function (error) {
// An error happened.
window.alert("Some unexpected error happened!");
});
user.sendEmailVerification().then(function () {
// Email sent.
}).catch(function (error) {
// An error happened.
window.alert("Email was not able to send!");
});
})
.catch(function (error) {
// Display error messages
setSignUpError(error.message);
});
}});
The weird thing is that the user input for my displayname and photoURL are working just fine, but when it comes to my private key user input it registers the input when it goes to the chat page and I do a console.log(user.privatekey) It says it is undefined.
In my chatjs file, thats when I am pushing the all the user profile information. The chatjs file basically allows a user to send a message, the message and all the user profile information gets stored onto the firebase database.
messages.push({
displayName: displayName,
userId: userId,
pic: userPic,
text: myString.toString(),
privatekey: user.privatekey,
timestamp: new Date().getTime() // unix timestamp in milliseconds
})
.then(function () {
messageStuff.value = "";
})
.catch(function (error) {
windows.alert("Your message was not sent!");
messageStuff;
});
The thing again is that the privatekey does not get stored at all, which is what I am not understanding, since it is registering user input in the loginjs file but when I go to the chatjs file it keeps saying the value is undefiend. I have googled everywhere and I still haven't found a solution to it. Any help would be greatly appricated!
It's because the Firebase user object you receive from Firebase is not customizable. When you call the createUserWithEmailAndPassword(email, password) method, it returns a specifically defined user object back to you - check out the docs for the properties of this object.
The properties displayName and photoURL both work because they are already properties of the user returned. privateKey is not an existing property of the Firebase user object, and Firebase doesn't know how to handle an update call for a property that isn't defined. Check out this question & answer where Frank explains that Users in Firebase aren't customizable - you need to store any extra info separately.

Update document in Mongoose (update array property at specific index)

I'm currently practicing my node.js skills and I am making a top10 movie list on a user's profile page. The user picked ten movies and now I want to save these movies in a user document in my database. This however, isn't working the array is still empty (I was able to change the 'picked' property to true though). Can anyone tell me why? Here is the code on the server side:
router.get('/updateTop10', function (req, res, next) {
// the string that I want to add to the db array
var movieElement = req.query.movieElement;
// the specific index of the array (the position of the movie in the top10
var index = req.query.index;
// the user that is currently logged in
var user = req.user;
User.findOne({'username': user.username }, function (err, user) {
// I am able to switch picked to true
user.top10.picked = true;
// However my array is still empty after I run the code
user.top10.top10[index] = movieElement ;
user.save();
res.send("SUCCESS")
} )
});
This is the structure of the user document:
Refer to https://github.com/Automattic/mongoose/issues/2654
Do this
user.top10.top10.set(index, movieElement);

how to get current user on firebase

I would like to know how how to get the current user. I am making a function where the user is creating a group and would like to add the user making the group to it at the same time. I can make the group fine, that was simple enough. But I do not know how to get to the user object outside of the simple login object.
I'm sorry if there are several topics stating this already, but I have been looking for hours and have not been able to find anything that explains it. Any help would be appreciated.
The currently logged in user is returned from Simple Login's callback. This callback runs when your user authenticates, or if your user is already authenticated, it runs at the time of page load.
Take this code form the simple login docs:
var myRef = new Firebase("https://<your-firebase>.firebaseio.com");
var authClient = new FirebaseSimpleLogin(myRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log("User ID: " + user.uid + ", Provider: " + user.provider);
} else {
// user is logged out
}
});
The user object is exposed in the callback. It's only in scope during the execution of that callback, so if you want to use it outside, store it in a variable for reuse later like this:
var currentUser = {};
var myRef = new Firebase("https://<your-firebase>.firebaseio.com");
var authClient = new FirebaseSimpleLogin(myRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
currentUser = user;
} else {
// user is logged out
}
});
...
// Later on in your code (that runs some time after that login callback fires)
console.log("User ID: " + currentUser.uid + ", Provider: " + currentUser.provider);

Categories