Allowing only one like per item. JS + Firebase Realtime Database - javascript

I am new to programming and I am trying to implement a picture gallery in which you can like/vote your favorite pictures. Each picture displays a counter of the number of likes/votes.
The caveat is that while you can vote for different pictures, you can only vote once for each one.
Any ideas on how to achieve this?
const dCounters = document.querySelectorAll('.CountLike');
[].forEach.call(dCounters, function(dCounter) {
const el = dCounter.querySelector('button');
const cId = dCounter.id;
const dDatabase = firebase.database().ref('Like Number Counter').child(cId);
// get firebase data
dDatabase.on('value', function(snap) {
let data = snap.val() || 0;
dCounter.querySelector('span').innerHTML = data;
});
// set firebase data
el.addEventListener('click', function() {
dDatabase.transaction(function(dCount) {
return (dCount || 0) + 1;
});
});
});
I have tried using local storage to check if user has voted already =
localStorage.getItem('iHaveVoted', 'yes')
but this doesnt get into account until browser is refreshed. Therefore, user is still allowed to increase the votes multiple times.

Related

Passing info througout different HTML pages with Javascript? Shopping cart problem [duplicate]

This question already has answers here:
Persist javascript variables across pages? [duplicate]
(6 answers)
Closed 29 days ago.
So i am working on this college work i have where i need to build a e-commerce store. and i have this javascript code on the single product page and it is working perfectly, for example, when i click on the add-to-cart button it is added to the shopping-cart container. And now my problem is and i have no idea on how to do it. Is it even possible to pass that info (what has been inserted into the shopping-cart) through out the dif html pages. Like if i add one product to the cart and go back to the homepage, it should still appear on the shopping-cart container.
const decrementButton = document.querySelector(".decrement-quantity");
const incrementButton = document.querySelector(".increment-quantity");
const quantityDisplay = document.querySelector(".quantity-display");
let quantity = 1;
const maxQuantity = 4;
let remainingStock = maxQuantity;
decrementButton.addEventListener("click", function() {
if(quantity > 1) {
quantity--;
quantityDisplay.textContent = quantity;
incrementButton.disabled = false;
// Check if the tooltip element is present and remove it
const tooltip = incrementButton.querySelector(".tooltip");
if (tooltip) {
incrementButton.removeChild(tooltip);
}
}
if (quantity === 0) {
addToCartBtn.disabled = true;
}
});
incrementButton.addEventListener("click", function() {
if(quantity < remainingStock) {
quantity++;
quantityDisplay.textContent = quantity;
incrementButton.disabled = false;
}
if(quantity === remainingStock) {
incrementButton.disabled = true;
const tooltip = document.createElement("div");
tooltip.setAttribute("data-tooltip", "");
tooltip.innerHTML = "Sorry, no more products available";
incrementButton.appendChild(tooltip);
}
});
const addToCartBtn = document.getElementById("add-to-cart-btn");
const cartCount = document.getElementById("cart-count");
const shoppingCartContainer = document.getElementById("shopping-cart");
const slide1 = document.querySelector("#slide-1");
const imageSrc = slide1.src;
let image = document.querySelector('.product-slider img');
let value = document.getElementById("value");
let currentCount = 0;
addToCartBtn.addEventListener("click", function() {
currentCount += quantity;
remainingStock -= quantity;
cartCount.innerHTML = `(${currentCount})`;
quantity = remainingStock;
quantityDisplay.textContent = remainingStock;
// Create a new div to represent the product
const productDiv = document.createElement("div");
productDiv.classList.add("product");
// Set the content of the div to the name, price, and image of the product
productDiv.innerHTML = `
<img src="${imageSrc}" alt="Aimé Leon Dore x 550 'Evergreen'">
<h4>"${image.alt}"</h4>
<p>${value.textContent}</p>
`;
shoppingCartContainer.appendChild(productDiv);
if(remainingStock === 0){
incrementButton.disabled = true;
decrementButton.disabled = true;
addToCartBtn.disabled = true;
const tooltip = document.createElement("div");
tooltip.setAttribute("data-tooltip", "");
tooltip.innerHTML = "Sorry, no more products available";
addToCartBtn.appendChild(tooltip);
}
});
const cartMenuItem = document.getElementById("cart");
const shoppingCart = document.getElementById("shopping-cart");
const closeShoppingCart = document.getElementById("close-shopping-cart");
cartMenuItem.addEventListener("click", function() {
if(shoppingCart.classList.contains("hidden")){
shoppingCart.classList.remove("hidden");
}else{
shoppingCart.classList.add("show");
}
});
closeShoppingCart.addEventListener("click", function() {
if(shoppingCart.classList.contains("show")){
shoppingCart.classList.remove("show");
}else{
shoppingCart.classList.add("hidden");
}
});
So the main question is, is it possible to pass that kind of info with javascript? mind that, that everything needs to be hardcode, my professor doesn't let us use external libraries or frameworks -.-'
Because you are only allowed to use frontend technologies, your possibilities are very limited. Without this limitation a backend would solve this problem. The backend creates a session for the user (a cookie is stored in the browser) and then assigns all HTTP requests to this user. More about this: What are sessions? How do they work?.
Therefore you need a way to store data with JavaScript and keep it peristent over multiple pages. Theoretically you can use cookies, but this is unnecessarily complicated and sometimes browsers forbid JavaScript to manipulate cookies. Therefore I don't recommend this solution.
It is better to use localStorage. This storage option is designed exactly for use-cases like this. The data remains persistent across multiple pages and even survives browser restarts. The usage is also very simple:
// Store data
localStorage.setItem('numberOfItems', '5');
// Read data
const number = localStorage.getItem('numberOfItems');
You can also use sessionStorage. This works exactly like localStorage, but the data is removed as soon as the page session ends. This session has nothing to do with the sessions above that the backend uses.
Cookies
If you want persistent data, but do not save it server side
(but in case of using a server, you also need at least one session/token cookie to link the data).
Local Storage
As mentioned in the comments.

How do I check if an IndexedDB database already exists?

I'm working on restaurant website (client-side project) and facing this problem, I want to make an admin page will show me all the orders placed by the customers and the way I choose that to save the order details in local storage then save it in this indexedDB then display the (the orders) at the admin page so I made this code and it work all good I guess to save the order and all customer details
document.getElementById('submittheorder').onclick = function() {
let i = 0;
const versionDB = 1;
let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
var open = indexedDB.open("CustomersOrders", versionDB);
open.onupgradeneeded = function() {
let db = open.result;
let store = db.createObjectStore("OrdersTable", {
keyPath: "id"
});
let index = store.createIndex("CIndex", ["FullName", "Order", "House", "Road", "Block"]);
};
open.onsuccess = function() {
let db = open.result;
let tx = db.transaction("OrdersTable", "readwrite");
let store = tx.objectStore("OrdersTable");
let index = store.index("CIndex");
store.put({
FullName: (sessionStorage.getItem("Cfullname")),
Order: (sessionStorage.getItem("order")),
House: (sessionStorage.getItem("CHouse")),
Road: (sessionStorage.getItem("CRoad")),
Block: (sessionStorage.getItem("CBlock"))
});
tx.oncomplete = function() {
db.close();
location.href = "Thanks.html";
};
}
}
Now the problem is I want to retrieve all the orders and the details for each object to the admin page
the second problem is that i want to check if the database already exist then insert new object not make a new database and save only one object, in a nutshell i want only one database to make and next times save the orders at that database.
Thank you :)
You can place this logic in the function that handles an upgrade event. There are essentially two ways. You can check if object stores and indices exist, using for example db.objectStoreNames.contains(), or you can compare versions by accessing the version properties from the database object or the event object.
For example, you would want to only create an object store if it did not already exist. If it does not already exists, then you know this is when your database is created.

Issues updating firestore document by committing a batch after an async function

I'm writing a cloud function that uses request-promise and cheerio to scrape a website and then check that information against a user document.
I am not entirely familiar with Javascript and Cloud Functions.
I've come so far that I managed to extract the information I need and navigate to the user's document and compare the data. Now the last piece of this function is to give the user points for each matching data point, so I need to update a map inside the user document.
This function has to loop through all users and change their document if the data point matches. I'm not sure the way I've written the code is the most optimal in terms of performance and billing if the userbase gets huge... Any pointers to how I could minimize the impact on the task would be of great help, as im new with JS.
So this is the code:
exports.getV75Results = functions.pubsub.schedule('every 2 minutes').onRun(async (context) => {
let linkMap = new Map();
const url = `https://www.example.com`
const options = {
uri: url,
headers: { 'User-Agent': 'test' },
transform: (body) => cheerio.load(body)
}
await rp(options)
.then(($) => {
for(let i = 1; i <= 7; i++)
{
//Find player from game
const lopp1 = $(`#mainContentHolder > div > div.mainContentStyleTrot > div > div.panel-body > table:nth-child(1) > tbody > tr:nth-child(${i}) > td:nth-child(2) > span`).text()
const lopp1StrR1 = lopp1.replace("(", "");
const lopp1StrR2 = lopp1StrR1.replace(")", "");
const lopp1StrR3 = lopp1StrR2.replace(" ", "");
linkMap.set(i, lopp1StrR3.toUpperCase());
}
console.log(linkMap);
return linkMap;
}).then(async () => {
//Start lookup users
let usersRef = db.collection('fantasyfotball').doc('users');
usersRef.listCollections().then(collections => {
collections.forEach( collection => {
var user = collection.doc(collection.id);
let batch = new admin.firestore().batch();
user.get().then(function(doc) {
let json = doc.data();
//Look in users collection if players document exist
Object.keys(json).forEach((name) => {
if(name != null) {
//Document with users active fotball players
if(name == 'players') {
let i = 0;
Object.values(json[name]).forEach((value) => {
i++;
if(value.localeCompare(linkMap.get(i)) == 0) {
//Loop through user keys and find owned players if user has the correct player
Object.keys(json).forEach((map) => {
if(map != null)
{
//Document with a map of player owned fotball players, each respective player has a key = 'fotball player' and value = '[price, points]'
if(map == 'ownedplayers')
{
Object.entries(json[map]).forEach((players) => {
if(players[0].localeCompare(value) == 0) {
console.log(players[1][1]);
//Add points to respective player field
//PROBABLY NOT HOW TO CHANGE A DOCUMENT FILED, THIS DOESNT WORK..
players[1][1]++;
}
});
//EACH TIME THIS RUNS IT SAYS: "Cannot modify a WriteBatch that has been committed"
batch.update(user, {'ownedplayers': json[map]});
}
}
});
}
});
}
} else {
console.log('user does not have a playermode document.');
}
});
});
return batch.commit().then(function () {
console.log("Succesfully commited changes.");
return null;
});
});
});
}).catch((err) => {
return err;
});
});
The issues i get in the console are "Cannot modify a WriteBatch that has been committed." and I fail to modify and add points to the player field inside the users document.
This is the console:
This is the firestore document structure:
I'm completely stuck on this.. Feels like I've tried all different approaches, but I think i dont fully understand cloud functions and javascript, so i would gladly recive feedback and help on how to make this work.
Cheers,
Finally.... i managed to update the document successfully. I put the commit outside another ".then()". Thought I tried that, but yay I guess :P
}).then(() => {
return batch.commit().then(function () {
console.log("Succesfully commited changes.");
return null;
});
The problem now is that it commits every loop. I think the most optimal here would be to batch update ALL users before committing?
And again, is there a more optimal way to do this, in terms of minimizing the operation and impact? I'm afraid I go too deep with for loops instead of directly navigating to the document, but haven't found an easier way to do that.
Any thoughts?

Handling firebase real time changes in progressive web app

I'm fetching the top news from hacker news API which is using firebase. I'm planning to build a progressive web app hence, I'm planning to cache the results into the localstorage.
The current code is here, which fetch and render the top stories. The code is here:
var ref = new Firebase("http://hacker-news.firebaseio.com/v0/");
var itemRef = ref.child('item');
var topStories = [];
var storyCallback = function(snapshot) {
var story = snapshot.val();
var html = '';
if(story.score) {
html = '<i>'+story.score+'</i> '+story.title+''
}
document.getElementById(topStories.indexOf(story.id)).innerHTML = html;
}
ref.child('topstories').once('value', function(snapshot) {
topStories = snapshot.val();
for(var i = 0; i < topStories.length; i++) {
var element = document.createElement("P");
element.id = i;
document.getElementById('items').appendChild(element);
itemRef.child(topStories[i]).on('value', storyCallback);
}
});
ref.child('topstories').on('child_changed', function(snapshot, prevChildName) {
var ref = snapshot.ref()
var index = ref.name();
var oldItemId = topStories[index];
itemRef.child(oldItemId).off();
var newItemId = snapshot.val();
topStories[index] = newItemId
itemRef.child(newItemId).on('value', storyCallback);
});
If I add each stories into the localstorage (by tweaking the above code), how can I skip fetching it from firebase the next time (if it already presents and doesn't changed)?
Note that doesn't change part is important because I can easily get from local storage using the key, how ever it should be in sync with firebase as well. So wondering whether firebase has some way to handle this
If I'm not missing any point you can simply check whether they already exists in local storage or not
if(localStorage.getItem('key')){
//display here
}else{
//fetch
//save for next time
localStorage.setItem('key', JSON.stringify('yourdata'));
//and display here
}
Also you can generalize your function for fetching, display or rendering so you can call at multiple place.

Java/Firebase Script Executing Multiple Times

I am having an interesting issue. The general idea of what I am doing is pulling data from a Firebase database, and populating a table based on that data. Everything runs perfectly during initial population--cells and rows are populated as they should be, but the weird issue is that the scripts seem to execute again randomly. I've logged the incoming data to the console, and can see it print twice after some amount of time.
This second execution does not happen if I am to navigate between pages, or reload the page--in either of those cases everything works as it should. The problem SEEMS to happen when I log back into my computer after locking it??? Does anybody have ANY idea what could be going on here? Relevant portion of script below:
const table = document.getElementById('myTable');
firebase.auth().onAuthStateChanged(firebaseUser => {
if (firebaseUser) {
let user = firebase.auth().currentUser;
let uid = user.uid;
const dbRef = firebase.database().ref().child("data/" + uid);
dbRef.once('value', snap => {
var dataCount = snap.child("secondData").numChildren();
var datalist = snap.child("secondData").val();
var dataArray = Object.keys(datalist).map(function(k) {
return datalist[k]
});
pullAllInfo(dataCount, dataArray);
});
}
});
function pullAllInfo(count, array) {
let k = 0;
let dataArray = [];
for (i = 0; i < count; i++) {
let specificRef = firebase.database().ref().child("secondData/" + array[i]);
specificRef.once('value', snap => {
var optionsTag = array[k];
k++;
var dataId = snap.child("id").val();
var dataName = snap.child("name").val();
var dataCount = snap.child("data").numChildren();
dataArray.push(dataId, dataName, dataCount, optionsTag);
if (k == count) {
buildTable(dataArray);
console.log(dataArray);
}
});
}
}
As you can see from the code above I AM calling .once() for each reference, which would prevent data duplication from the typical .on() call. Just cant seem to figure this one out. ALSO I have an iMac, just for anyone curious about my potential computer unlock diagnosis.
Thanks all!
Most likely, the auth state is changing and setting off your function. Try throwing a log under firebase.auth().onAuthStateChanged like this:
firebase.auth().onAuthStateChanged(firebaseUser => {
console.log( 'auth state changed', firebaseUser );
if (firebaseUser) {
My guess is that you'll see that the AuthState is changing when you log out/log in from your computer.
I solved this issue by creating another global boolean called preLoaded. At the beginning, it is set to false and, once the data is loaded and passed off to build the table, it is set to true. It now looks like this:
if(k == count && preloaded == false){
preloaded = true;
console.log(dataArray);
buildTable(dataArray);
}
All set!

Categories

Resources