Authenticate with Firebase with a Phone Number (JS) requires a mandatory reCAPTCHA verifier, it takes the ID of the container. For the ID of the container, I am generating a random one -
firebase_recaptcha_container: "recaptcha-container",
firebase_recaptcha_reset: function() {
if (typeof appVerifier != "undefined") {
appVerifier.reset()
appVerifier.clear()
}
let id = loadJS.firebase_recaptcha_container
let newID = loadJS.randomString(10)
$("#"+id).contents().remove()
$("#"+id).prop("id", newID)
loadJS.firebase_recaptcha_container = newID
return newID
}
then requesting for the RecaptchaVerifier and upon receiving I set this as a global variable window.appVerifier .
firebase_recaptcha: function(name_r="default") {
let promiseD = new firebase.auth.RecaptchaVerifier(name_r, {
'size': 'invisible',
'callback': function(response) {
resolve(response)
},
'expired-callback': function(r) {
console.log("expired", r)
},
'isolated' : false
});
return promiseD
},
_____________________
let container_recaptcha = $utils.firebase_recaptcha_reset()
window.appVerifier = await $utils.firebase_recaptcha(container_recaptcha)
It works totally fine for the very first time. But its a honest mistake for users not to use correct phone number. So for next time, I am doing the same thing again and getting error while generating the RecaptchaVerifier -
reCAPTCHA has already been rendered in this element
Which sadly does not make sense as the new element is totally different and also clear, reset methods were called following the documentation. I am neither using any other reCaptcha on this page. Refreshing the page might be a possible solution but that I really hate. Any insight would be helpful.
Thanks!
Finally found the solution, looks like it was a stupid mistake!
Invoking firebase.auth.RecaptchaVerifier adds new recaptcha scripts, every time! Hence all needed to be done is, calling it once, it does the rest on its own.
This won't get fixed just by implementing recaptchaVerifier.clear() method.
In the callback where you are passing this appVerifier, you'll have to implement the above clear method and add that "recaptcha-container" using ref
The below would be the element in render method:
<div ref={recaptchaWrapperRef}>
<div id="recaptcha-container"></div>
</div>
GenerateCaptcha function:
const generateRecaptcha = () => {
appVerifier = new RecaptchaVerifier(
"recaptcha-container",
{
size: "invisible",
},
authentication
);
Inside submit Callback:
if (appVerifier && recaptchaWrapperRef.current) {
appVerifier.clear();
recaptchaWrapperRef.current.innerHTML = `<div id="recaptcha-container"></div>`;
}
// Initialize new reCaptcha verifier
generateRecaptcha();
Related
I need to add current system time into child data field.
I'm using TypeScript, but this might still give you and idea how you could do it.
My code uses the event.timestamp property to get date and time:
export const onWrite = functions.database.ref('/{databaseName}/{tableName}/{key}').onCreate((event) => {
let ref = event.data.ref;
let isCreate = event.eventType == 'providers/google.firebase.database/eventTypes/ref.create';
ref.once('value').then(e => {
// Cloud functions are sometimes executed with a delay and the record might not exist anymore.
if (e.val() === null) {
return;
}
if (isCreate) {
return ref.update({
'createdDateTime': event.timestamp
});
}
});
});
The created events for clients won't include this added data yet, only a later change event does.
I'm haven't investigated yet if this can be fixed (perhaps by making use of transaction).
I saw your image description and understood u want to add system time into firebase.
If you want to do you can do that by , like below
var fb_db=firebase.database().ref('treeName');
var key=fb_db.push().key;
var updatenode={}
updatenode[key+"\"]= new Date();
fb_db.update(updatenode).then(function(){
alert("Success")
})
I've been looking at the documentation for Synchronized Arrays https://www.firebase.com/docs/web/libraries/angular/api.html#angularfire-extending-the-services and https://www.firebase.com/docs/web/libraries/angular/guide/extending-services.html#section-firebasearray
I'm using Firebase version 2.2.7 and AngularFire version 1.1.2
Using the code below, I'm having trouble recognizing $$removed events.
.factory("ExtendedCourseList", ["$firebaseArray", function($firebaseArray) {
// create a new service based on $firebaseArray
var ExtendedCourseList= $firebaseArray.$extend({
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
console.log("new course");
return course;
},
$$removed: function(snap){
console.log("removed");
return true;
}
});
return function(listRef) {
return new ExtendedCourseList(listRef);
}
}])
.factory("CompanyRefObj", function(CompanyRef) {
//CompanyRef is a constant containing the url string
var ref = new Firebase(CompanyRef);
return ref;
})
.factory('CourseList', function (localstorage,$rootScope,ExtendedCourseList,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = ExtendedCourseList(CompanyRefObj.child(companyID).child("courses"));
)
If I run this code, only the $$added events will be triggered. To simulate the remove events I use the web-interface at Firebase to display data, where I press the remove button and accept the data being deleted permanently.
Additionally, if I delete the $$removed function, the extended service still won't synchronize when a record is deleted.
If I modify my code to use the $firebaseArray instead of extending the service (as seen above) both add and remove events will be recognized.
.factory('CourseList', function (localstorage,$rootScope,$firebaseArray,CompanyRefObj) {
var companyID = localstorage.get("company");
$rootScope.courseList = $firebaseArray(CompanyRefObj.child(companyID).child("courses"));
)
Finally, are there any bad practices I've missed that can cause some of the extended functions to not work?
Solved
$$added: function(dataSnapshot, prevChild){
var course = dataSnapshot.val();
var course_key = dataSnapshot.key();
//Modified below
course.$id = course_key;
//End of modification
console.log("new course");
return course;
}
After posting about the issue at firebase/angularfire github I received an answer that solved my issue. When $$added got overridden by the code provided, the $firebaseArray also lost its internal record $id.
Adding this line of code: course.$id = course_key; before returning the course, made AngularFire recognize when the record was removed from the server.
hope my first question here is not a stupid one.
Lets say we want to build a chat application with meteor, with logedin and anonymous users. The chat should be filled like that:
var post = {
userId: user._id, // is empty if anonymous user
submitted: new Date().getTime(),
text: chat_message
});
var postId = Posts.insert(post);
The publication could looks like this to make sure that the userId is not transfered
Meteor.publish('getTheChat', function() {
return Post.find({}, {fields: {userId: false});
});
But is there a way to add a field in the returned collection dynamically?
The userId should not be published but a status like "Your_Post","another_logedin_user" or "an_anonymous_user". By having that, I could include some css, so the chat looks a little bit more like WhatsApp or iMessage.
The logic inside the publish method could be something like
if (userId == this.userId) {
status = "Your_Post";
} else if (userId != null) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
You see, the publication should include different values when called from different users. Is there a way in Meteor.publish?
Thanks for any insight or suggestions
Thank you both for your ideas! But as I had to find out (just for my inward peace) how it is possible inside the publish method server sided, I came, with the help of David's link, to this solution -maybe it will help someone later:
Meteor.publish('getTheChat', function(postId) {
var currentUserId = this.userId;
var ownerUserId = Posts.findOne({'_id':postId}).userId;
var findOptions = {}; // in my final coding these differ for 'chat room owners' and 'normal users'
var transform = function(post) {
if (currentUserId && post.userId == currentUserId) {
post.userId = "posted by you";
} else if (post.userId == null) {
post.userId = "anonym posted";
} else if (post.userId == ownerUserId) {
post.userId = "posted by owner";
} else {
post.userID = "posted by another loged in";
return post;
};
var self = this;
var handle = Posts.find(findOptions).observe({
added: function (document) {
self.added('posts', document._id, transform(document));
},
changed: function (newDocument, oldDocument) {
self.changed('posts', document._id, transform(newDocument));
},
removed: function (oldDocument) {
self.removed('posts', oldDocument._id);
}
});
self.ready();
self.onStop(function(){
handle.stop();
});
By having this I am finally able to overwrite values dynamically.
It looks like you need to add a transform on your Posts collection. You can do this in your publish function (as seen here), but server-side transforms are computationally inefficient and tricky to write. Though they are necessary in cases where only the server could perform the action - e.g. signed URLs. In your case, I'd recommend a standard collection transform which is a filter applied after the documents are fetched.
Unfortunately, this kind of transform would require the userId on the client. I've never seen a case where simply publishing a id could cause a security issue. If you believe this is the case with your app, I'm very interested to know why. If you can overcome this restriction, keep reading...
You can read about transforms in the documentation on collections, and you can see an example on evented mind. Personally I like to use the collection-helpers package for this.
If you try collection-helpers, your transform could look like:
Posts.helpers({
status: function() {
if (this.userId === Meteor.userId()) {
return 'Your_Post';
} else if (this.userId != null) {
return 'another_logedin_user';
} else {
return 'an_anonymous_user';
}
}
});
And then you could use it in your template like:
{{#each posts}}
<p>{{text}} - <span class='status'>{{status}}</span></p>
{{/each}}
Of course, you can also use template helpers to achieve the same result but transforms are more easily reusable across your application.
Sadly, this has been a huge issue for me too, and I am sorry to say, it is not technically possible to just add a field on the publisher's query and use it conveniently in your view. BUT, I have a solution that may work for you. It will also give you an idea of how complex it can become as soon as you want to keep some reactive data private in Meteor.
Server side:
First, create two different publishers: one for the current user's posts, one for all the others:
Meteor.publish('getTheOthersChat', function() {
return Post.find({$ne:{userId: this.userId}}, {fields: {userId: false});
});
Meteor.publish('getTheOwnChat', function() {
return Post.find({userId: this.userId});
});
Client/router side:
Then, subscribe to both of these: what this will do is include the post's userId only when it is the own user's id. If not, it'll be undefined.
Then, we still need to identify the case "anonymously posted" vs "posted by user". For this, you can add another field during the post creation, for example is_anonymous, which you then set to true or false depending on the case if the user is logged in or not. The check would then be:
if (userId) {
status = "Your_Post";
} else if (is_anonymous === false) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
This solution should work. I know, it is sad to have to come to this kind of means. It makes Meteor look clunky and impractical for tasks that should be dead easy. Such a shame for such a cool framework!
I'm using the meteor-paginated-subscription package in my app. On the server, my publication looks like this:
Meteor.publish("posts", function(limit) {
return Posts.find({}, {
limit: limit
});
});
And on the client:
this.subscriptionHandle = Meteor.subscribeWithPagination("posts", 10);
Template.post_list.events = {
'click #load_more': function(event, template) {
template.subscriptionHandle.loadNextPage();
}
};
This works well, but I'd like to hide the #load_more button if all the data is loaded on the client, using a helper like this:
Template.post_list.allPostsLoaded = function () {
allPostsLoaded = Posts.find().count() <= this.subscriptionHandle.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
};
The problem is that Posts.find().count() is returning the number of documents loaded on the client, not the number available on the server.
I've looked through the Telescope project, which also uses the meteor-paginated-subscription package, and I see code that does what I want to do:
allPostsLoaded: function(){
allPostsLoaded = this.fetch().length < this.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
}
But I'm not sure if it's actually working. Porting their code into mine does not work.
Finally, it does look like Mongo supports what I want to do. The docs say that, by default, cursor.count() ignores the effects of limit.
Seems like all the pieces are there, but I'm having trouble putting them together.
None of the answers do what you really want becase none provide solution that is reactive.
This package does exactly what you want and also reactive.
publish-counts
I think you can see the demo: counts-by-room in meteor doc
It can help you publish the counts of your posts at server and get it at client
You can simply write this:
// server: publish the current size of your post collection
Meteor.publish("counts-by-room", function () {
var self = this;
var count = 0;
var initializing = true;
var handle = Posts.find().observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", 'postCounts', {count: count});
},
removed: function (id) {
count--;
self.changed("counts", postCounts, {count: count});
}
});
initializing = false;
self.added("counts", 'postCounts', {count: count});
self.ready();
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Mongo.Collection("counts");
// client: subscribe to the count for posts
Tracker.autorun(function () {
Meteor.subscribe("postCounts");
});
// client: simply use findOne, you can get the count object
Counts.findOne()
The idea of sub.loaded() is to help you with exactly this problem.
Posts.count() isn't going to return the right thing because, as you've guessed, on the client, Meteor has no way of knowing the real number of posts that live on the server. But what the client knows is how many posts it's tried to load. That's what that .loaded() tells you, and is why the line this.fetch().length < this.loaded() will tell you if there are more posts on the server or not.
What I would do is write a Meteor server side method that retrieves the count like so:
Meteor.methods({
getPostsCount: function () {
return Posts.find().count();
}
});
Then call it on the client, in observe to make it reactive:
function updatePostCount() {
Meteor.call('getPostsCount', function (err, count) {
Session.set('postCount', count);
});
}
Posts.find().observe({
added: updatePostCount,
removed: updatePostCount
});
Although this question is old, I thought I would provide an answer that ended up working for me. I did not create the solution, I found the basis for it here (so credit where credit is due): Discover Meteor
Anyway, in my case I was trying to get "size" of the database from client side, so I can determine when to hide the "load more" -button. I was using template level subscriptions. Oh and for this solution to work, you need to add reactive-var -package. Here is my (in short):
/*on the server we define the method which returns
the number of posts in total in the database*/
if(Meteor.isServer){
Meteor.methods({
postsTotal: function() {
return PostsCollection.find().count();
}
});
}
/*In the client side we first create the reactive variable*/
if(Meteor.isClient){
Template.Posts.onCreated(function() {
var self = this;
self.totalPosts = new ReactiveVar();
});
/*then in my case, when the user clicks the load more -button,
we call the postsTotal-method and set the returned value as
the value of the totalPosts-reactive variable*/
Template.Posts.events({
'click .load-more': function (event, instance){
Meteor.call('postsTotal', function(error, result){
instance.totalPosts.set(result);
});
}
});
}
Hope this helps someone (I recommend checking the link first). For template level subscriptions, I used this as my guide Discover Meteor - template level subscriptions. This was my first stacked-post and I am just learning Meteor, so please have mercy...:D
Ouch this post is old, anyway maybe it will help someone.
I had exactly the same issue. I managed to solve it with 2 simple lines...
Remember the :
handle = Meteor.subscribeWithPagination('posts', 10);
Well I used in client handle.loaded() and Posts.find().count(). Because when they are different it means that all the posts are loaded. So here is my code :
"click #nextPosts":function(event){
event.preventDefault();
handle.loadNextPage();
if(handle.loaded()!=Posts.find().count()){
$("#nextPosts").fadeOut();
}
}
I had the same problem, and using the publish-counts package didn't work with the subs-manager package. I created a package that can set a reactive server-to-client session, and keep the document count in this session. You can find an example here:
https://github.com/auweb/server-session/#getting-document-count-on-the-client-before-limit-is-applied
I'm doing something like this:
On cliente
Template.postCount.posts = function() {
return Posts.find();
};
Then you create a template:
<template name="postCount">
{{posts.count}}
</template>
Then, whatever you want to show the counter: {{> postCount}}
Much easier than any solution i have seen.
Most of my .on events use closures:
ref.on( 'child_added',
function( snapshot ) {
userCallback( snapshot.val() );
} );
which means, it is not possible to deactivate these monitors with .off(), since off needs the original callback pointer as well as the eventType. How can I do this sort of thing?
My app (jQuery Mobile) is page-based. When the user hits a certain page, I want to activate monitors. When the user leaves that page, I want to de-activate, so when they re-enter, I don't end up with multiple monitors. I think I want to call .off() with only eventType and have all callbacks removed.
Right now the only option is to store a reference to the callback. We actually make this a little easier by having .on() return it back to you. So you can do:
var childCallback = ref.on('child_added', function(snapshot) { /* whatever */ });
// later...
ref.off('child_added', childCallback);
But we've received several pieces of feedback like yours, saying it is sometimes cumbersome to keep track of your callback references. So we have a planned API change so that you can call .off() with only an event type or with no arguments at all, and we'll just remove whatever callbacks are registered. But we're focused on other features right now, so this change is probably 1+ months out.
Thanks for the feedback!
In the interim, you can reproduce that sort of behavior with a simple manager pattern. For instance:
function ObserverManager( firebaseRef, page ) {
this.firebaseRef = firebaseRef;
this.listeners = {child_added: [], child_removed: [], value: [], child_updated: [], child_changed: []};
this.page = page;
}
FirebaseObservable.prototype.on(event, callback) {
this.listeners[event].push(
this.firebaseRef.on(event, function(snapshot) {
callback(snapshot.val());
})
);
}
FirebaseObservable.prototype.off(event) {
var list = this.listeners[event], i = list.length;
while(i--) { // 50% more efficient than for(i..; list.length; ...) in IE due to scoping
firebaseRef.off(event, list[i]);
}
}