Force computed variable to update (Firebase VueJS) - javascript

I have a navigation link that I want to update when I have signed a user into the app through firebase auth.
I'm managing the user login changes through .onAuthStateChanged in the created hook ()
data () {
return {
user: null,
additionaluserinfo: null,
isAdmin: false
}
},
created () {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
this.user = user
// call for additional user information from firebase
db.collection('users').where('user_id', '==', user.uid)
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(doc.data())
this.additionaluserinfo = doc.data()
this.$store.dispatch('setAdditionalUserInfo', doc.data())
})
})
.catch((error) => {
console.log('Error getting documents: ', error)
})
this.additionaluserinfo = this.$store.state.additionaluserinfo
if (this.$store.state.additionaluserinfo.role === 'admin' || this.$store.state.additionaluserinfo.role === 'superadmin') {
this.isAdmin = true
}
if (this.$store.state.additionaluserinfo.role === 'superadmin') {
this.isSuperAdmin = true
}
} else {
this.user = null
this.additionalUserInfo = null
}
})
I'm checking to see if the user has user rights on the onAuthSateChanged.
The issue that I have is that I have to refresh the page in order for my navigation bar to update (which displays a div based on v-if="isAdmin" / "isSuperAdmin". Is there a way to force this to update from within onAuthStateChanged?

Either move the block
this.additionaluserinfo = this.$store.state.additionaluserinfo
if (this.$store.state.additionaluserinfo.role === 'admin' || this.$store.state.additionaluserinfo.role === 'superadmin') {
this.isAdmin = true
}
if (this.$store.state.additionaluserinfo.role === 'superadmin') {
this.isSuperAdmin = true
}
inside this.$store.dispatch('setAdditionalUserInfo', doc.data()).then(() => { /* */ }) or else that code would be evaluated before data are dispatched into the store (data fetching is async)
Or, better, just remove it and then add:
computed: {
role: () => this.$store.state.additionaluserinfo ? this.$store.state.additionaluserinfo.role : '',
isAdmin: () => this.role === 'admin' || this.role === 'superadmin', // remove isAdmin from data()
isSuperAdmin: () => this.role === 'superadmin',
}

Related

How to redirect a user based on Firebase Authentication status?

I would like to redirect users when they sign in with Github or others based on whether they are a new user or a returning user. I'm having trouble accessing the isNewUser property referenced in this answer: How to differentiate signin and signup user in firebase using google auth?
I have a standard sign in function:
const signinWithGoogle = () => {
return auth.signInWithPopup(googleProvider)
.then((response) => {
handleUser(response.user)
})
}
This is the handleUser function:
const handleUser = (rawUser) => {
if (rawUser) {
const currentUser = formatUser(rawUser)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
if (currentUser.providerData[0].isNewUser===true) {
history.push("/onboarding")
} else {
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
And this is formatUser:
const formatUser = (user) => {
return {
uid: user.uid,
email: user.email,
name: user.displayName,
provider: user.providerData[0].providerId,
avatar: user.photoURL,
}
}
Can anyone see what I'm doing wrong, please?
Cheers, Matt
EDIT:
If we pass the response to the HandleUser function and console log response.additionalUserInfo.isNewUser we get 'true'. However, if we use that in our if statement, it seems to be ignored for some reason
const handleUser = (response) => {
if (response) {
console.log("response: ", response.additionalUserInfo.isNewUser)
const currentUser = formatUser(response.user)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
console.log('response', response)
console.log('additional info', response.additionalUserInfo)
const isNew = response.additionalUserInfo.isNewUser
console.log('isNewUser', isNewUser)
if (isNew) {
console.log('redirecting to /onboarding')
history.push("/onboarding")
} else {
console.log('redirecting to /')
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
EDIT 2: Here is the output from the console logs
That error is coming from the signInWithGithub function in the modal
async function signInGitHub() {
try {
await signinWithGitHub()
}
catch(err) {
console.log("Error: ",err.code)
}
finally {
closeModal();
}
}
It looks like you are passing a User to that function and not the raw response. The isNewUser is present on the additionalUserInfo property. Please try refactoring as shown below:
const handleUser = (rawUser) => {
if (rawUser) {
const currentUser = formatUser(rawUser.user)
createUser(currentUser.uid, currentUser)
setCurrentUser(currentUser)
if (currentUser.additionalUserInfo.isNewUser) {
history.push("/onboarding")
} else {
history.push("/")
}
return currentUser
}
else {
setCurrentUser(false)
return false
}
}
Also make sure you pass the raw response:
handleUser(response.user)

How I can make asynchronous queries in GraphQL?

I'm calling 1 query and mutation. Mutation works fine, but when I get response from my query I need to redirect user to another page, but In my case, the function is triggered before I get response. How can I prevent this?
const renderData = async () => {
const currentUserId = await data?.signInUserSession?.idToken
?.payload?.sub;
const isAdmin = await data?.signInUserSession?.idToken?.payload[
"custom:role"
];
localStorage.setItem("userId", currentUserId);
if (
currentUserId !== null &&
currentUserId !== undefined &&
currentUserId !== ""
) {
Auth.currentSession().then((data) => {
setData({
variables: {
updateUserInput: {
id: currentUserId,
firstName: data.getIdToken().payload.given_name,
lastName: data.getIdToken().payload.family_name,
},
},
});
});
isCodeValid({
variables: {
validateUserVerificationCodeInput: {
user: {
id: currentUserId,
},
},
},
});
if (isAdmin === "admin" && isUserCodeValid) {
history.push("/managements");
} else if (
isUserCodeValid !== undefined &&
isUserCodeValid === true
) {
history.push("/verification");
} else if (isUserCodeValid) {
history.push("/stripe");
}
}
};
isUserCodeValid - is a response from query
useMutation has onCompleted and refetchQueries options for such cases. It is hard to write an exact solution for your case since not all code is visible but an example like below can help, I believe:
const [addProduct, { data, loading, error }] = useMutation(
createProductMutation
);
const onFinish = async (fieldNames) => {
await addSpending({
variables: { ...others, ...fieldNames},
refetchQueries: [{ query: calledQuery }],
onCompleted: (data) => {
// your logic
},
});
if (!error) {
form.resetFields();
onFinishSave(true);
}
};

Firebase admin deleteUser function not working

I'm trying to delete all the users in my auth and database using firebase functions. Here's my code for that:
const admin = require("firebase-admin");
admin.initializeApp({
credential: admin.credential.applicationDefault(),
databaseURL: "----"
});
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '--';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
adminDeleteAllUsers();
}
}
});
//Admin control
function adminDeleteAllUsers() {
deleteAllUsers(' ');
return null;
}
function deleteAllUsers(nextPageToken: any) {
admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
})
.catch((error: any) => {
console.log('Error deleting user:', error);
});
db.collection('users').doc(userUid).delete();
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
})
.catch((error: any) => {
console.log('Error listing users:', error);
});
}
When the function get executed, no user is deleted. It's like the function never worked. Am I missing something?
Update:
I'm not sure if this is the way to do it, but it's still not working. I tried to handle promises correctly, but I'm not sure if what I'm doing is correct or not.
export const listenToAdminCommands = functions.firestore.document('collection/{docUid}')
.onWrite((change, context) =>
{
const pass: string = '---';
// const before = change.before.exists? change.before.data() : null;
const after = change.after.exists? change.after.data() : null;
if(after !== null && after !== undefined) {
const adminCommandType: string = after['type'];
const adminCommandPass: string = after['pass'];
if(adminCommandType === 'deleteAll' && adminCommandPass === pass) {
return adminDeleteAllUsers();
}
return;
}
return;
});
//Admin control
function adminDeleteAllUsers() {
return deleteAllUsers(' ');
}
function deleteAllUsers(nextPageToken: any) {
return admin.auth().listUsers(1000, nextPageToken)
.then((listUsersResult: any) => {
//go through each one and check last time signed in
listUsersResult.users.forEach((userRecord: any) => {
const user: any = userRecord.toJSON();
const userUid = user['uid'];
console.log('Deleting user for data delete uid = ', userUid);
return admin.auth().deleteUser(userUid)
.then(() => {
console.log('Successfully deleted user', userUid);
return db.collection('users').doc(userUid).delete();
})
.catch((error: any) => {
console.log('Error deleting user:', error);
return;
});
});
if (listUsersResult.pageToken) {
// List next batch of users.
listAllUsers(listUsersResult.pageToken);
}
return;
})
.catch((error: any) => {
console.log('Error listing users:', error);
return;
});
}

Checking firestore real time changes

I am working with firestore real time in a map project, and it requires updating a users current location at x distance interval.
However, the real time listener keeps refetching my own updates, thereby increasing my reads.
I assume firestore real time updates the cache locally before sending to the server, is it possible to ignore fetching changes that are made by that user?
class Booking extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
errorMessage: '',
};
this.unsubscribe = null;
}
componentDidMount() {
this.getRealTimeData();
}
componentWillUnmount() {
this.unsubscribe = null;
}
getRealTimeData = () => {
this.fetchCompletedGigs();
}
fetchCompletedGigs = () => {
const { userID } = this.props;
this.props.bookingData([]);
this.setState({ isLoading: true, errorMessage: '' });
this.unsubscribe = Firebase.shared.fetchBooking('Bookings')
.where('userID', '==', userID)
.orderBy('d.CreatedAt', 'desc')
.limit(20)
.onSnapshot((querySnapshot) => {
if (querySnapshot.empty) {
this.setState({
isLoading: false,
errorMessage: "You currently don't have anybooking",
});
this.props.bookingData([]);
}
querySnapshot.docChanges().forEach(change => {
const doc = change.doc;
const item = doc.data();
item.docId = doc.id;
const list = [...this.props.bookings, item];
this.setState({ isLoading: false, errorMessage: '' });
if (change.type === 'added') {
const filterList = _.uniqBy(list, 'docId');
this.props.bookingData(filterList);
} else if (change.type === 'removed') {
const newData = list.filter(task => task.docId !== doc.id);
const filterList = _.uniqBy(newData, 'docId');
return this.props.bookingData(filterList);
} else if (change.type === 'modified') {
const newData = list.filter(task => task.docId !== doc.id);
const newList = [...newData, item];
const filterList = _.uniqBy(newList, 'docId');
return this.props.bookingData(filterList);
}
}, err => {
this.props.bookingData([]);
console.warn(err);
this.setState({
isLoading: false,
errorMessage: 'Error occurred while fetching your booking',
});
});
}, err => {
this.props.bookingData([]);
console.warn(err);
this.setState({
isLoading: false,
errorMessage: 'Error occurred while fetching your booking.',
});
});
}
You can't prevent the onSnapshot listener from firing for local events. But you can detect those local events inside the callback, and ignore them there:
Firebase.shared.fetchBooking('Bookings')
.where('userID', '==', userID)
.orderBy('d.CreatedAt', 'desc')
.limit(20)
.onSnapshot((querySnapshot) => {
...
querySnapshot.docChanges().forEach(change => {
if (change.doc.metadata.hasPendingWrites) {
... handle the local event differently
}
else {
... handle normally
}
});
...
});
Also see the Firebase documentation on detecting local changes.

How to make different logged in users in firebase go to different pages

So I have it working that when a user is logged in it goes to a TabsPage but if not logged in go to log in page. The problem is I have two users a customer and an event organiser. So I want to do something like if (user.useType === "customer") go to TabsPage and if (user.useType === "eventorganiser") then go EventTabsPage else if not a user go to LoginPage.
I am trying user firebase snapshot but not working at all as always returns true. Am I close? I have userType in database that is either userType: "customer" or userType: "eventorganiser"
import { Reference } from '#firebase/database-types';
#Component({
templateUrl: 'app.html',
})
export class MyApp {
rootPage: any;
public type : Reference
constructor(platform: Platform,
statusBar: StatusBar,
splashScreen: SplashScreen) {
firebase.initializeApp(firebaseConfig);
this.type = firebase.database().ref("userProfile");
this.type.once("value")
.then(function (snapshot) {
this.type = snapshot.val().userType
});
const unsubscribe: Unsubscribe = firebase
.auth()
.onAuthStateChanged(user => {
if (user && this.type.toString() === "customer") {
this.rootPage = 'TabsPage'
unsubscribe();
}
else if (user && this.type.toString() === "eventorganiser") {
this.rootPage = 'EventTabsPage';
unsubscribe();
}
else {
this.rootPage = 'LoginPage';
unsubscribe();
}
});
database is like below
{
"userProfile" : {
"3RQOUe9cqcRX2IMy0S290S9trSM2" : {
"birthDate" : "2018-02-20",
"email" : "acs#acs.com",
"firstName" : "asda",
"lastName" : "dsad",
"userType" : "customer"
},
"CHtJTfDmCgfaMY91ve4oepvIkuc2" : {
"birthDate" : "2018-02-20",
"email" : "liam#liam.com",
"firstName" : "lk",
"lastName" : "",
"userType" : "eventorganiser"
},
my working code for all users is below
const unsubscribe: Unsubscribe = firebase
.auth()
.onAuthStateChanged(user => {
if (user) {
this.rootPage = 'TabsPage';
unsubscribe();
} else {
this.rootPage = 'LoginPage';
unsubscribe();
}
})
It is possible that by the time your apps calls:
firebase
.auth()
.onAuthStateChanged(user => {
if (user && this.type.toString() === "customer") {
this.rootPage = 'TabsPage'
unsubscribe();
}
The:
this.type = firebase.database().ref("userProfile");
this.type.once("value")
.then(function (snapshot) {
this.type = snapshot.val().userType
});
Is not ready, because it's an async call and it's not waiting for the value to return before calling your onAuthStateChanged() function.
Also, make sure please that's the correct reference, because /userType is a generic node.
Here's how I've done this in the past:
const unsubscribe: Unsubscribe = firebase
.auth()
.onAuthStateChanged(user => {
if (user) {
firebase.database().ref("userProfile").once("value").then(function (snapshot) {
if (snapshot.val().userType === 'customer') {
this.rootPage = 'TabsPage';
} else {
this.rootPage = 'EventTabsPage'
}
});
} else {
this.rootPage = 'LoginPage';
unsubscribe();
}
});
Following that flow, it first gets the user, then the user profile, and then checks the values.

Categories

Resources