vue js realtime chat app without refreshing / firebase - firebase

I'm creating a chat app,
If the user enter message and press send button, the app is working fine. The informations going to the database and im taking a datas.
When the user refresh the page, so there is no problem, in mounted() instance im taking the datas from database(firebase) and im showing on the app. If another user comes to the chat, also there is no problem, all messages are appearing.
The problem is that: If the new message is coming, another user can not see it without refresh or without send message button. When the another user send a message then the user see all messages.
I explain the problem with a gif, if you help me i will be glad.
<template>
<div>
<div class="container">
<div class="row">
<div v-if="isLogin" class="offset-3 col-md-6 msg-area">
<header>
<h1>Group Chat</h1>
<p class="sm">Welcome, {{this.username }} </p>
</header>
<div class="msg">
<p class="mssgs" v-for="(message,index) in messages" :key="index">{{ message.msg }} <br> <span> {{ message.name }} - {{ message.time }} </span> </p>
</div>
<div class="sendMsg">
<form #submit.prevent="sendFunc">
<div class="form-group d-flex">
<input type="text" class="form-control" placeholder="Enter message.." v-model="msgInput">
<button class="btn">Send </button>
</div>
</form>
</div>
</div>
<div class="offset-3 col-md-6 login" v-else>
<form #submit.prevent="joinFunc">
<div class="form-group d-flex">
<input type="text" class="form-control" placeholder="Enter username.." v-model="username">
<button class="btn">Join to Group </button>
</div>
</form>
</div>
</div>
</div>
</div>
</template>
<script>
import firebase from "./firebase";
import 'firebase/firestore';
export default {
data() {
return {
db : firebase.firestore(),
isLogin: false,
username: '',
messages : [
],
msgInput: '',
}
},
methods: {
joinFunc() {
this.isLogin = true;
},
sendFunc() {
let date = new Date();
let hour = date.getHours();
let minute = date.getMinutes();
let nowTime = hour + ':' + minute;
this.db.collection("messages")
.add({ message: this.msgInput, name: this.username, time: nowTime, date: date })
.then(() => {
this.db.collection("messages").orderBy("date", "asc")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.messages.push({
name: doc.data().name,
msg: doc.data().message,
time: doc.data().time
});
});
})
})
.catch((error) => {
console.error("Error writing document: ", error);
});
},
},
mounted: function() {
this.db.collection("messages").orderBy("date", "asc")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.messages.push({
name: doc.data().name,
msg: doc.data().message,
time: doc.data().time
});
});
})
}
}
</script>

You're using get() to read the data from Firestore. As the documentation I linked explains, that reads the value from the database once, and does nothing more.
If you want to continue listening for updates to the database, you'll want to use a realtime listener. By using onSnapshot() your code will get called with a querySnapshot of the current state of the database right away, and will then also be called whenever the database changes. This is the perfect way to then update your UI.
So instead of
...
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
Do the following:
...
.onSnapshot((querySnapshot) => {
querySnapshot.forEach((doc) => {

Related

woocommerce graphql registerUser, sometime work something dont

i am sending data to make woocommerce graphql reigster a user, but sometimes it work some time it dont, speicalily once i succesful register a user, then right ahead to register again it wont register again. after i wait a while or refresh the page, it can be register again, is woocommerce graphql have somekind of delay system to prevent register spam, so i know is not my code problem. here is my code
const [username, setUsername] = useState('')
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [registerUser, { data, error, isLoading }] = client.useMutation((mutation, args: FormData) => {
const result = mutation.registerUser({
input: args,
})
console.log('REsult', result)
return result.user
})
const handleSubmit = async (event) => {
event.preventDefault()
await registerUser({
args: {
username,
email,
password,
},
})
.catch((error) => {
console.log('error:', error)
})
.then((response) => {
console.log('response:', response)
})
}
useEffect(() => {
console.log(data)
setUsername('')
setEmail('')
setPassword('')
}, [data])
here is my html:
return (
<div className="ps-page--default">
<form className="ps-form--auth" id="register__tab">
<div className="ps-tabs">
<div id="tab-2">
<div className="form-group form-group--space">
<input
className="form-control"
name="username"
id="register__tab--username"
type="text"
placeholder="What should we call you ?"
value={username}
onChange={(e) => setUsername(e.target.value)}
/>
</div>
<div className="form-group form-group--space">
<input
className="form-control"
name="email"
id="register__tab--email"
type="email"
placeholder="Enter your email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div className="form-group form-group--space">
<input
className="form-control"
name="password"
id="register__tab--password"
type="text"
placeholder="Create a password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
<div className="ps-form__desc">
<p>Your personal data will be used to support your experience throughout this website, to manage access to your account, and for other purposes described in our privacy policy.
</p>
</div>
<div className="form-group submit">
<button type="submit" className="ps-btn ps-btn--fullwidth ps-btn--black" disabled={isLoading} onClick={handleSubmit}>
{isLoading ? 'Signing up...' : 'Sign up'}
</button>
</div>
</div>
</div>
</form>
</div>
)
do anyone know what do i miss ? or is just part of woocommerce graphql setting ?
the only error show on my console.log are this
[gqty] Warning! No data requested.
resolved2 # resolvers.mjs?c4ac:167
others are all my successful return

Fetch and display lists of an user

I have a profile page that displays the user info. The page shows the user name / email and a button to create a list.
I can also edit the name and email correctly, and it reflects in the firebase instantaneously. Ok. I get the user data and I can edit it.
What I'm trying to do now is to show the lists that the user has created.
Look, this user has created one list, and what is returned to me is that he doesn't have lists.
I'll try to shorten the code as much as possible:
<script>
imports.....
import { db } from '../../firebase.config.js'
let listings = []
let auth = getAuth()
// fetch the user's listings
const fetchUserListings = async () => {
const listingsRef = collection(db, 'listings')
const q = query(
listingsRef,
where('userRef', '==', auth.currentUser.uid),
orderBy('timestamp', 'desc')
)
const querySnap = await getDocs(q)
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
}
fetchUserListings()
</script>
<!-- display the user's listings -->
<div>
{#if listings.length > 0}
<p class="listingText">My lists</p>
{#each listings as listing}
<ListingItem listing={listing.data} id={listing.id} />
{/each}
{:else}
<p class="noListings">You have no lists</p>
{/if}
</div>
My ListItem component:
<script>
export let listing
export let id
export let handleDelete
import DeleteIcon from '../../static/assets/svg/deleteIcon.svg'
</script>
<li class="categoryListing">
<a href={`/category/${listing.type}/${id}`} class="categoryListingLink">
<img src={listing.imgUrls[0]} alt={listing.name} class="categoryListingImg" />
<div class="categoryListingDetails">
<p class="categoryListingLocation">
{listing.location}
</p>
<p class="CategoryListingName">
{listing.name}
</p>
<p class="categoryListingPrice">
${listing.offer ? listing.discountedPrice : listing.regularPrice}
{listing.type === 'rent' ? '/ por mês' : ''}
</p>
<div class="categoryListingInfoDiv">
<img src="/assets/svg/bedIcon.svg" alt="cama" />
<p class="categoryListingInfoText">
{listing.bedrooms > 1 ? `${listing.bedrooms} camas` : `${listing.bedrooms} cama`}
</p>
<img src="/assets/svg/bathtubIcon.svg" alt="banheiro" />
<p class="categoryListingInfoText">
{listing.bathrooms > 1
? `${listing.bathrooms} banheiros`
: `${listing.bathrooms} banheiro`}
</p>
</div>
</div>
</a>
{#if handleDelete}
<DeleteIcon
class="removeIcon"
fill="rgb(231, 76, 60)"
onClick={() => {
handleDelete(listing.id, listing.name)
}}
/>
{/if}
</li>
Just when you think you've reached the simplest part, it's still tough.
Update:
I think that the problem is in firebase. The "docs" are empty:
Now I am in serious trouble!
querySnap.forEach((doc) => {
return listings.push({
id: doc.id,
data: doc.data()
})
})
I see two things here. The less important: The .forEach() method returns undefined, so the return is redundant. The more important: the .push() alone won't automatically trigger updates. Have a look at this section in the Docs
Did you try logging listings? I assume the data is there, it's just not displayed, so I propose to change this part to
querySnap.forEach((doc) => {
listings = [...listings, {
id: doc.id,
data: doc.data()
}]
})
or
querySnap.forEach((doc) => {
listings.push({
id: doc.id,
data: doc.data()
})
listings = listings
})

Firebase Auth - ConfirmPasswordReset, how to grab oobcode from URL to pass thorugh function?

I'm trying to implement a reset password Page on my website using firebase auth. I got the send email to reset password page working. Now from what I understand you get an email with a link that you need to click and on this email there will be a code that is needed to reset the password. Now I'm at a loss on how to grab said code from the url and already display it for the user on the field. Is it possible to have the code come in the body of the email and have the user input the code? If not, how do I grab the code from the url and input it for the user so the user can only input the password? My website is using vue and this is what I have so far
<template>
<div class="container">
<h3>reset pw page</h3>
<div class="row">
<form #submit.prevent="ResetPw()" class="col s12">
<div class="row">
<div class="input-field col s12">
<input type="password" id="password" v-model="password" />
<label>Password</label>
</div>
</div>
<div class="row">
<div class="input-field col s12">
<input type="text" id="code" v-model="code" />
<label>Code</label>
</div>
</div>
<button type="submit" class="btn">Submit</button>
</form>
</div>
</div>
</template>
<script>
import firebase from "firebase/app";
export default {
data() {
return {
password: "",
code: ""
};
},
methods: {
ResetPw() {
firebase
.auth()
.confirmPasswordReset(this.code, this.password)
.then(() => {
console.log(`Password Changed!`);
})
.catch(err => console.log(err));
}
}
};
</script>
I think I got everything done, I just need to understand how to grab the oobcode from the link https://my-project.firebaseapp.com/__/auth/action?mode=&oobCode=
If you are using react-router, it does not parse the query any more, but you can access it via location.search
const params = new URLSearchParams(this.props.location.search);
const code = params.get('oobCode')
const email = await firebase.auth().verifyPasswordResetCode(code)
Alternatively, instead of using this.props.location.search, you can do new URLSearchParams(window.location.pathname)
Not sure how to grab the oobCode from the body of the email but to grab the code from the URL once the page loads, you can refer to this question: how-can-i-get-query-string-values-in-javascript. In your form, create a hidden input for the code with an empty string value. Once window loads, code will be grabbed from URL and then you can pass the code and password into the function
<body>
<form>
<input type="text" id='newPass' name='newPass' placeholder='New password'>
<input type="hidden" id='code' name='code' value="">
<button type='submit'>Submit</button>
</form>
</body>
<script>
$(window).load(function () {
function getParameterByName(name, url) {
if (!url) url = window.location.href;
name = name.replace(/[\[\]]/g, '\\$&');
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url);
if (!results) return null;
if (!results[2]) return '';
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
var code = getParameterByName('oobCode')
document.getElementById('code').value = code;
</script>
Hope this helps!

Calling Meteor methods in React components

Currently I'm working on a project based on Meteor as back end and React as front end. I really enjoyed simplicity untill I removed insecure package and have to deal with Meteor methods. Right now I need to perform a basic insert operation and I'm just stucked!
I have a form as component (in case eventually I'd like to use this form not only for inserting items but for editing those items as well) and here's my code for this form:
AddItemForm = React.createClass({
propTypes: {
submitAction: React.PropTypes.func.isRequired
},
getDefaultProps() {
return {
submitButtonLabel: "Add Item"
};
},
render() {
return (
<div className="row">
<form onSubmit={this.submitAction} className="col s12">
<div className="row">
<div className="input-field col s6">
<input
id="name"
placeholder="What"
type="text"
/>
</div>
<div className="input-field col s6">
<input
placeholder="Amount"
id="amount"
type="text"
/>
</div>
</div>
<div className="row">
<div className="input-field col s12">
<textarea
placeholder="Description"
id="description"
className="materialize-textarea">
</textarea>
</div>
</div>
<div className="row center">
<button className="btn waves-effect waves-light" type="submit">{this.props.submitButtonLabel}</button>
</div>
</form>
</div>
);
}
});
This chunk of code is used as a form component, I have a prop submitAction which I use in let's say add view:
AddItem = React.createClass({
handleSubmit(event) {
event.preventDefault();
const
name = $('#name').val(),
amount = $('#amount').val(),
description = $('#description').val();
Items.insert(
{
name: name,
range: range,
description: description,
createdAt: new Date(),
ownerId: Meteor.userId()
},
function(error) {
if (error) {
console.log("error");
} else {
FlowRouter.go('items');
};
}
);
},
render() {
return (
<div className="row">
<h1 className="center">Add Item</h1>
<AddItemForm
submitButtonLabel="Add Event"
submitAction={this.handleSubmit}
/>
</div>
);
}
});
As you can see I directly grab values by IDs then perform insert operation which works absolutely correct, I can even get this data displayed.
So now I have to remove insecure package and rebuild the whole operation stack using methods, where I actually stucked.
As I understand all I should do is to grab same data and after that perform Meteor.call, but I don't know how to pass this data correctly into current method call. I tried considering this data right in the method's body which doesn't work (I used the same const set as in AddItem view). Correct me if I'm wrong, but I don't think this method knows something about where I took the data (or may be I don't really get Meteor's method workflow), so by this moment I ended up with this code as my insert method:
Meteor.methods({
addItem() {
Items.insert({
name: name,
amount: amount,
description: description,
createdAt: new Date(),
ownerId: Meteor.userId()
});
}
});
and this is how I changed my handleSubmit function:
handleSubmit(event) {
event.preventDefault();
const
name = $('#name').val(),
amount = $('#amount').val(),
description = $('#description').val();
Meteor.call('addItem');
},
Also I tried declaring method like this:
'addItem': function() {
Items.insert({
// same code
});
}
but it also didn't work for me.
Again, as I understand the problem isn't about data itself, as I wrote before it works just right with insecure package, the problem is how the heck should I get this data on the server first and right after that pass this to the client using methods (also console gives no even warnings and right after I submit the form, the page reloads)?
I've already seen some tutorials and articles in the web and didn't find desicion, hope to get help here.
You can add your data as parameters in your Meteor call function. You can also add a callback function to check on the success of the call.
handleSubmit(event) {
event.preventDefault();
const
name = $('#name').val(),
amount = $('#amount').val(),
description = $('#description').val();
Meteor.call('addItem', name, amount, description, function(err, res) {
if (err){
console.log(JSON.stringify(err,null,2))
}else{
console.log(res, "success!")
}
});
},
In your Meteor methods:
Meteor.methods({
addItem(name, amount, description) {
var Added = Items.insert({
name: name,
amount: amount,
description: description,
createdAt: new Date(),
ownerId: Meteor.userId()
});
return Added
}
});

How to display Meteor.loginWithPassword callbak error message on the same page

I have created a custom login page and used the Meteor.loginWithPassword(user, password, [callback]) function to login to the app.
Following is the login template:
<template name ="Login">
<form class="login-form form-horizontal">
<div class="control-group">
<input class="email" type="text" placeholder="Email">
</div>
<div class="control-group m-inputwrapper">
<input class="password" type="password" placeholder="Password">
</div>
<div class="control-group">
<button type="submit" class="submit t-btn-login" >Login</button>
</div>
</form>
<div class="alert-container">
<div class="alert-placeholder"></div>
</div>
</template>
Template.Login.events({
'submit .login-form': function(e, t) {
e.preventDefault();
// retrieve the input field values
var email = t.find('.email').value,
password = t.find('.password').value;
Meteor.loginWithPassword(email, password, function(err) {
if (err) {
$(".alert-placeholder").html('<div></div><div class="alert"><span><i class="icon-sign"></i>'+err.message+'</span></div>')
}
});
return false;
}
});
While i debugging i can see the error message displayed and added to the dom. but it will get refresh and message will disappear.
Is meteor re render the page after Meteor.loginWithPassword() ? How can i overcome this?
When using meteor, if you find yourself manually injecting html elements with jQuery, you are probably doing it wrong. I don't know the blaze internals well enough to give you an exact answer to why your elements are not being preserved, but here is a more meteor-like solution:
In your alert container, conditionally render an error message:
<div class="alert-container">
{{#if errorMessage}}
<div class="alert">
<span><i class="icon-sign"></i>{{errorMessage}}</span>
</div>
{{/if}}
</div>
In your login callback, Set the errorMessage session variable if err exists:
Meteor.loginWithPassword(email, password, function(err) {
if (err) {
Session.set('errorMessage', err.message);
}
});
Finally, add a template helper to access the errorMessage session variable:
Template.Login.helpers({
errorMessage: function() {
return Session.get('errorMessage');
}
});
You can use Bert for showing error message in each page. I use it in login page like this :
Meteor.loginWithPassword(emailVar, passwordVar, function(error) {
if (error) {
Bert.alert(error.reason, 'danger', 'growl-top-right');
} else {
Router.go('/dashboard');
}
});

Resources