Reputation: 130
In my Angular Project, I'm trying to have a sign in function that signs the user in with google and then updates their user data dependent on whether or not they already have the approved
role.
When trying to make this work, neither of my console.log
statements fire so I'm not sure what I can be doing to get into those blocks.
async handleSignIn() {
const credential = await this.afAuth.signInWithPopup(new firebase.default.auth.GoogleAuthProvider());
return this.manageUserData(credential.user)
}
private async manageUserData(user: User) {
let data$; // Init Variable
let update$; // Init Variable
const userCollection = this.afs.collection<User[]>('users'); // Sets Path for Data
console.log(user.uid);
data$ = userCollection.doc<User>(user.uid).snapshotChanges().pipe( // Fetches Document
map(action => { // Maps To Be Able To Do Things
const payload = action.payload.data(); // Sets Payload Data as the main `payload` variable
console.log("In place", payload)
if(payload.roles.approved == true || false) {
console.log("main block")
update$ = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL
}
} else {
console.log("else block")
update$ = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
roles: {
approved: false
}
}
}
const userRef: AngularFirestoreDocument<User> = this.afs.doc(`users/${user.uid}`);
return userRef.set(update$, { merge: true });
})
)
}
The console.log(user.uid)
statement returns my firebase UID: rKlGL943eqfaPVWLh2EEUVBoOvg2
Edit: I also tried using the ternary operator here and organizing functions better:
function hasApprovedStatus() {
update$ = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL
}
userRef.set(update$, { merge: true });
}
function hasNoApprovedStatus() {
update$ = {
uid: user.uid,
email: user.email,
displayName: user.displayName,
photoURL: user.photoURL,
roles: {
approved: false
}
}
userRef.set(update$, { merge: true })
}
data$ = userCollection.doc<User>(user.uid).snapshotChanges().pipe( // Fetches Document
map(action => action.payload.data().roles.approved ? hasApprovedStatus() : hasNoApprovedStatus())
)
}
Upvotes: 2
Views: 276
Reputation: 2324
The problem could be that your async method manageUserData()
is not actually return
ing anything.
The method is creating an Observable from userCollection.doc<User>(user.uid).snapshotChanges()
and assigning it to local var data$
but it's not returning that from the method, and it's also not subscribing to it. So you appear to be creating an Observable that never gets subscribed to, and therefore your map
function will never be invoked.
What you need to do is return
the Observable you're creating (also, no need to assign it to a local var unless you're going to use that var for something):
return userCollection.doc<User>(user.uid).snapshotChanges().pipe(...
That will cause manageUserData()
to return a Promise (since it's async
) that resolves to an Observable.
Since handleSignIn()
returns the return value of manageUserData()
, that Promise will be passed along to whoever calls handleSignIn()
. That calling method would look something like this:
async signIn() {
const handle: Observable<any> = await this.handleSignIn();
handle.subscribe();
}
Since handleSignIn()
returns a Promise, we must await
that Promise's resolution and then subscribe to the resulting Observable (i.e., your snapshotChanges()
).
Unless you need this to be Promise-based, you might consider simply returning the Observable from manageUserData()
and handleSignIn()
instead of wrapping it in a Promise (by making those methods async
), then you wouldn't need the extra step of await
ing the Promise to resolve.
Here's a StackBlitz with a mocked version of this chain of method calls working correctly.
Update in response to Comments, 12/29/2020:
If you need to filter your stream so that the first line in your map
function, const payload = action.payload.data()
, will always retrieve a value from method call action.payload.data()
, you can add a filter operator into your pipe before the map
:
return userCollection.doc<User>(user.uid).snapshotChanges().pipe(
filter(action => Boolean(action.payload?.data?.())),
map(action => { ... )
)
A couple other observations:
map
function isconst payload = action.payload.data();
in which you're invoking the data()
method on the payload
property. Is data()
truly a method or is it just a property?
map
function isif(payload.roles.approved == true || false) { ...
That's a strangely constructed condition. What condition(s) are you testing for?
The || false
essentially does nothing because it's only evaluated if payload.roles.approved == true
is false
, so you'd just be checking if (false || false)
, which doesn't seem very useful.
If your intention was to check if payload.roles.approved
is either strictly true
or strictly false
, then you would do this (note that I'm using strict equality, ===
, rather than loose equality because that's preferred unless there's a good reason to use loose equality):
if(payload.roles.approved === true || payload.roles.approved === false) { ...
Upvotes: 1