Firebase push() method shuffle the list data - javascript

Have created a chat application and we use firebase for realtime communication.
Sometimes i noticed that push() method shuffle the list data. We can see in the below image :
If we see in above image i am just trying to communicate with someone i said hello in reply user said hey, again i said i need some help then user said That's what I'm here for. What can I assist you with? in the reply but if we see in the image users reply is appearing first.
It happens intermittently that's why i didn't figure out this problem. So please someone help me out what shall i doing wrong.
var pushMessageToFB = function(){
var chatMsgRef = db.child("chatMessages").child("gr1").child("ch_usr1_usr2");
var message = {
type: "chat",
content: data.messageText,
timestamp: Date.now(),
by: user.id
};
chatMsgRef.push(message, function(err){
if (err){
console.log('error occurred while pushing message to fb : err ' + JSON.stringify(err));
}
});};
var loadChatMessages = function(){
var chatMsgRef = db.child('chatMessages').child("gr1").child("ch_usr1_usr2");
$scope.chatMessages = chatMsgRef.orderByKey().limitToLast(50);
};

Don't use Date for remote data. It will be slightly offset on every machine. Use ServerValue.TIMESTAMP which Firebase will convert to the server's Unix epoch time when the data is written. This ensures consistency of order across clients.
You didn't provide any code for how you're reading or displaying the chat messages, but since you're trying to show them in chronological order I assume your query uses orderBy("timestamp"). If you use ServerValue.TIMESTAMP when you write the message it will be guaranteed to sort in the order that it was written to the database.

Related

How to retrieve a specific data from Firebase

I am designing a website where when the user comes and enters his email id, a corresponding value gets printed on the website.
I have the below data on my real-time firebase database.
How do I write a javascript function in my JS file so that when a user enters his/her email-id, the value of the total gets printed on the website?
I have tried something like this:
function getdata(){
var email=document.getElementById("email").value;
firebase.database().ref('0/'+email).once('value').then(function(snapshort){
var total=snapshort.val().total;
document.getElementById("total").innerHTML=total;
})}
I think you're trying to perform a query based on the email property of the node.
In that case, it'd be:
firebase.database().ref()
.orderByChild("email")
.equalTo(email)
.once('value').then((results) => {
results.forEach((snapshot) => {
let total = snapshot.val().total;
document.getElementById("total").innerHTML=total;
});
});
I recommend reading the Firebase Realtime Database documentation end-to-end, as it should answer many of the questions you may have about the API. A few hours spent there now, will save you many hours down the line.

Prevent the delay time to fetch the data from firebase (Please check details for clarity)

Note:
The asynchronous nature of firebase has been discussed thousands of times here, but my low reputation number does not allow for a comment on an existing question. That's why I have asked this question.
I am a noob, so please help me understand the implementation in an easy to understand manner.
Steps to implement:
User enters a value in the HTML input box
Search the input value in the firebase db (showMessage() gets called)
Display an appropriate result based on the search result in step 2
Problem faced:
The message displayed in step 3 takes almost an average of 1.75 seconds to display. This experience is not user-friendly. I want to display the message as soon as possible i.e. want to reduce the fetch time.
Probable root causes:
Either my way of fetching the data from firebase dB is incorrect (I still don't understand how to keep a promise :()
Or The mechanism of search and display is not right
var full_name;
function showMessage(){
extractData();
}
function extractData(){
test(function(returnValue) {
custom_message = searchMessage(returnValue);
var container = document.querySelector('#placeholder');
var para = document.createElement('p');
var custom_message = "Happy happy, buds!";
para.innerHTML = custom_message;
para.className = "message";
container.appendChild(para);
});
}
function test(callback) {
var ref = firebase.database().ref();
ref.on('value', function(snapshot) {
var data = snapshot.val();
callback(data);
}, function (error) {
console.log("Error: " + error.code);
});
}
function searchMessage(data){
for(var i = 0; i < data.length; i++)
{
name_f_data = data[i].firstName.concat(" ", data[i].lastName);
if(full_name.toLowerCase() == name_f_data.toLowerCase())
{
console.log(name_f_data.toLowerCase());
console.log(full_name.toLowerCase());
return data[i].message;
}
}
}
The time a read operation takes depends on:
The latency of your connection to Firebase's servers
The amount of data you are reading
The bandwidth of your connection
The time it takes Firebase to process the request
In most cases, the time Firebase takes is only a very small portion of the total time, and most of your time actually goes to the data transfer, which depends purely on the bandwidth and amount of data. If this is the first time you're reading data from Firebase in the page, the latency also matters more, as Firebase has to establish a connection, which takes a few roundtrips.
Your current code is downloading all data from the database, and then searching in the JavaScript code for a child node that matches a certain value. The best way to reduce the time that takes (apart from upgrading to a fast connection) is to transfer less data, which you can do by using Firebase's query mechanism to do the filtering on the server.
You can get pretty close with:
var ref = firebase.database().ref();
var query = ref.orderByChild().startAt(firstName).endAt(firstName+"~");
query.once('value', function(snapshot) {
var data = snapshot.val();
callback(data);
This will significantly reduce the amount of data transferred. A few notes though:
The query returns just the people that have the first name you're looking for. It does not yet filter on the last name, so you'll still need to filter that in the client-side code.
To further optimize this, store the full name (which you now compose in the client-side code) in the database so that you can query on that and reduce data transfer even more.
Firebase queries are case sensitive, so the query only returns data where the case matches exactly. If you want to query case-indifferent, consider storing a toLowerCase() value in the database.
Be sure to define an index on firstName, as otherwise the Firebase database will still send all data to the client, and the SDK will perform the filtering client-side.

Webhooks in Bitcoin Transactions - How to Apply Business Logics?

I want to start accepting Bitcoin on my website.
In order to do that, I wrote the following piece of code, but I truly struggle to understand how I can implement proper business logic after that the transaction is completed.
Here is the code:
<html>
<head>
<title>Pay with Bitcoin</title>
<script>
//Gets the URL of the Webpage and gets the price value of this transaction in USD.
//For simplicity Here the Value is passed in the URL.
//However in production you wanna use POST instead of GET.
const myUrl = window.location.href;
const url = new URL(myUrl);
const usdPrice = url.searchParams.get("price");
//This is the function where all the magin happens
const showQR = () => {
//URL of the api which will provide us with current BTC exchange rate
const apiUrl = "https://blockchain.info/ticker";
const hr = new XMLHttpRequest();
hr.open('GET', apiUrl, true);
hr.onreadystatechange = function(){
//Make sure the API sent a valid response
if(hr.readyState == 4){
let ticker = JSON.parse(hr.responseText);
//Get last BTC/USD exchange value from the API , then convert Price from USD to BTC
let BTCprice = ticker.USD.last;
let btcToPay = usdPrice / BTCprice;
//Make sure you have just 8 decimal points in your BTC price!!
btcToPay = btcToPay.toFixed(8);
//Use google API (or other...) to create the QR code. Pass on your btc public address and
//the amount (btc price) dynamically created. Message and label parameters can be dynamic too.
let qrurl = "https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl=bitcoin:1BAnkZn1qW42uRTyG2sCRN9F5kgtfb5Bci?amount="+btcToPay+"%26label=CarRental%26message=BookingID123456";
//Populate the 'btc' DIV with QR code and other info...
document.getElementById('btc').innerHTML = "<img src=" +qrurl+"><br> <span class = 'greenMoney'>" + usdPrice + " usd / " + btcToPay + " BTC </span>";
}
}
hr.send();
};
</script>
</head>
<body onload = "showQR()">
<h1>Pay with BitCoin</h1>
<div id = "btc">
</div>
</body>
</html>
This code does the following:
Gets current USD/BTC exchange rate using the blockchain API.
takes the price in USD for the URL and converts it into BTC
generates a QR code using google API.
Embeds the price, label and message into the QR code
Renders the QR code in a DIV
I ve also set up a web hook service which will be listening to new transactions happening in the specified wallet address. Then a callback to my server is made, by mean of a POST request.
The problem is: the label and message parameters passed to the QR code will not be written in the blockchain.
They are just a handy reference for the customer to remind him what that specific transaction paid for.
As a result the callback to my server is practically useless.
In fact, the callback doesn't return any Booking Id or any other piece of information which could help me to understand who paid for what. Needless to say, in this scenario no business logic is possible: I can't update the order status on my DB, I can't send a confirmation email to the right customer.
How can I embed relevant information (e.g. Booking ID) into the BTC payment, ideally through the QR code?
If this is possible, how can I retrieve this information later on when my server receives the callback informing me that a new payment was made to my BTC wallet?
In short, you can't.
When accepting payments, you are supposed to give each invoice a new BTC address. This way, when you receive notification of an incoming transaction, you can check the receiving address to see which invoice is being paid, and compare the received amount against the expected amount.
Note
Technically, you could embed stuff like a order ID into an OP_RETURN. However, most wallets don't support transactions like that, and any users who want to pay you from an exchange account would be unable to comply.
#Raghav Sood thank you for your input which routed me to the right direction.
Using NodeJS/Express/MongoDB in the backend, I managed to implement a solution which I would like to share here.
Before starting, I wanna make a big disclaimer: this solution is not the only one, it is not the best one, it is not the fastest and probably it is not the most elegant.
Anyway, this solution has the advantage of not relying on packaged third parties solutions. This is in line with the spirit of the whole "no intermediation" philosophy of the bitcoin community. Most imortantly, your XPub always stay in your server and is NOT shared with any external service, which is probably the wisest approach.
Having said that, here is how one can show dynamic unique BTC addresses to customers:
First of all , I put in place a counter which keeps track of how many btc addresses were created for customers from a my HD wallet.
This is important to make sure than you never present the same address twice to customers, which is good for privacy of all parties and also for the sake of implementing business logic in your app.
In order to do this, I store a "counter value" into my DB. Everytime someone visits the BTC payment page, this value is retrived from mongo using a "dealCount" function and is assigned to a "serialPay" variable, which is equal to the value gotten from Mongo + 1. In the backend, the code would be something like this:
`function dealCount(){`
return new Promise(function(resolve, reject){
Deal.find({_id: "ID_OF_OBJ_WHERE_YOU_STORE_COUNTER"}, function(err, data){
if(err){
console.log(err);
}
resolve(data[0].serialDeal + 1);
})
})
};
The new value obtained (which later on will be saved again into Mongo in order to keep track of addresses created) is used to generate the new BTC public address for the customer at hand. If you keep reading you will see how.
To create new public addresses dynamically, one needs the xPub Key of his or her HD Wallet. If one is coding in NodeJS there are a couple of libraries (which can be imported into the server) that will enable this operation rather easily: bitcoinjs-lib and/or bitcore-lib. Personally I opted for Bitcore-lib, because there are less dependencies to deal with and I found the supporting material easier to digest.
Codewise, address generation goes as follows:
const bitcore = require('bitcore-lib');
app.post("/pay.html", urlencodedParser, function(req, res){
let serialPay = dealCount();
serialPay.then(function(serialPay){
const pub = new bitcore.HDPublicKey('INSERT_HERE_YOUR_XPUB_KEY');
let derivedHdPk = pub.derive('m/0/'+serialPay);
let derivedPk = derivedHdPk.publicKey;
let myDynAddress = new bitcore.Address(derivedPk);
res.render('pay', {myDynAddress: myDynAddress});
});
});
Then, using EJS as a templating engine, I could easily make the receiving bitcoin address dynamic in the front-end (/pay.ejs):
let myDynAddress = "<%=myDynAddress%>";
let qrurl = "https://chart.googleapis.com/chart?chs=250x250&cht=qr&chl=bitcoin:"+myDynAddress+"?amount="+btcToPay+"%26label=CarRental";
This will generate the QR Code Dynamically. In the original question, one can see how to render that into the webpage. In the meantime one should also put in place a function to store the updated "serialPay" counter back to the DB.
At this point one should only start monitoring incoming (non-confirmed) payments to the dynamic BTC address generated. A simple way to do it, is using the blockchain.info websocket API. When the payment arrives, things go forward as suggested by #Raghav Sood: one checks the incoming transaction making sure the customer paid the right amount to the right address.
Now you know who paid for what and all sorts of business logics can be triggered.

Meteor: Best practice for modifying document data with user data

Thanks for looking at my question. It should be easy for anyone who has used Meteor in production, I am still at the learning stage.
So my meteor setup is I have a bunch of documents with ownedBy _id's reflecting which user owns each document (https://github.com/rgstephens/base/tree/extendDoc is the full github, note that it is the extendDoc branch and not the master branch).
I now want to modify my API such that I can display the real name of each owner of the document. On the server side I can access this with Meteor.users.findOne({ownedBy}) but on the client side I have discovered that I cannot do this due to Meteor security protocols (a user doesnt have access to another user's data).
So I have two options:
somehow modify the result of what I am publishing to include the user's real name on the server side
somehow push the full user data to the clientside and do the mapping of the _id to the real names on the clientside
what is the best practice here? I have tried both and here are my results so far:
I have failed here. This is very 'Node' thinking I know. I can access user data on clientside but Meteor insists that my publications must return cursors and not JSON objects. How do I transform JSON objects into cursors or otherwise circumvent this publish restriction? Google is strangely silent on this topic.
Meteor.publish('documents.listAll', function docPub() {
let documents = Documents.find({}).fetch();
documents = documents.map((x) => {
const userobject = Meteor.users.findOne({ _id: x.ownedBy });
const x2 = x;
if (userobject) {
x2.userobject = userobject.profile;
}
return x2;
});
return documents; //this causes error due to not being a cursor
}
I have succeeded here but I suspect at the cost of a massive security hole. I simply modified my publish to be an array of cursors, as below:
Meteor.publish('documents.listAll', function docPub() {
return [Documents.find({}),
Meteor.users.find({}),
];
});
I would really like to do 1 because I sense there is a big security hole in 2, but please advise on how I should do it? thanks very much.
yes, you are right to not want to publish full user objects to the client. but you can certainly publish a subset of the full user object, using the "fields" on the options, which is the 2nd argument of find(). on my project, i created a "public profile" area on each user; that makes it easy to know what things about a user we can publish to other users.
there are several ways to approach getting this data to the client. you've already found one: returning multiple cursors from a publish.
in the example below, i'm returning all the documents, and a subset of all the user object who own those documents. this example assumes that the user's name, and whatever other info you decide is "public," is in a field called publicInfo that's part of the Meteor.user object:
Meteor.publish('documents.listAll', function() {
let documentCursor = Documents.find({});
let ownerIds = documentCursor.map(function(d) {
return d.ownedBy;
});
let uniqueOwnerIds = _.uniq(ownerIds);
let profileCursor = Meteor.users.find(
{
_id: {$in: uniqueOwnerIds}
},
{
fields: {publicInfo: 1}
});
return [documentCursor, profileCursor];
});
In the MeteorChef slack channel, #distalx responded thusly:
Hi, you are using fetch and fetch return all matching documents as an Array.
I think if you just use find - w/o fetch it will do it.
Meteor.publish('documents.listAll', function docPub() {
let cursor = Documents.find({});
let DocsWithUserObject = cursor.filter((doc) => {
const userobject = Meteor.users.findOne({ _id: doc.ownedBy });
if (userobject) {
doc.userobject = userobject.profile;
return doc
}
});
return DocsWithUserObject;
}
I am going to try this.

firebase: get records with dates greater than now

I'm using Firebase for an admin panel which has multiple users. Whenever a new question is added, everyone in the panel gets alerted immediately.
It's working fine, but the way i implemented it doesn't feel right. Here's what i'm doing:
dataRef.auth(token, function(error, result) {
if (error) {
console.log("Login Failed!", error);
} else {
var started = new Date();
dataRef.on('child_added', function(snapshot) {
var message = snapshot.val();
var t = message.date.split(/[- :]/);
var lastChildDate = new Date(t[0], t[1]-1, t[2], t[3], t[4], t[5]);
if(lastChildDate > started) {
// alert the user
}
});
}
});
And this is the data structure. It only has one field:
{date: "2013-06-14 16:45:10"}
This brings all the records in that base. A child_added event gets fired for all records. I then check if it's created after the page is loaded, and alert the user accordingly. But the clients only need to be alerted for children that are added from now on. How can i get rid of that unnecessary load? I've checked the documentation, but couldn't find an answer.
There's a few ways to solve this issue.
1) Encode the timestamp at which the event occurred in the priority of the new child, and then use "startAt" to prune older elements.
Make sure your encoding of time sorts lexigraphically in time order.
For example:
ref.startAt("2013-06-14 10:11:13").on(...);
When generating the timestamped items, you'll want to use the Firebase server time so everyone is working off of the same clock. See serverTimeOffset:
https://www.firebase.com/docs/managing-presence.html
2) Have a different event queue for every user, and have users' clients remove elements from the queue once they've seen them.
This solution is best in cases where each user might receive different notifications, such as in a social network.

Categories