Why does AngularFire .set() waits forever for a response and doesn't proceed?

Viewed 47

I'm trying to create my first app in Angular using AngularFire to help with Firebase operations and after logging in with Twitter I'm trying to add the user as a document in my users collection. Here's the full function:

processLoggedUser(): Promise<number> {
    return this.afAuth.getRedirectResult().then(async result => {
        if (result.user) {
            const uid = result.user.uid;
            const credential = result.credential as firebase.default.auth.OAuthCredential;
            console.log('Twitter answer of AuthLogin: ', result)
            this.uid = uid
            let code = 0 // 0 = default code -> error
            if (result.additionalUserInfo.isNewUser) {
                console.log("New user")
                this.user = {
                    uid: this.uid,
                    email: result.user.email,
                    displayName: result.user.displayName,
                    photoURL: result.user.photoURL.replace('_normal', ''),
                    miniPhotoURL: result.user.photoURL,
                    accessToken: credential.accessToken,
                    secret: credential.secret,
                    twitterId: result.additionalUserInfo.profile["id"],
                    username: result.additionalUserInfo.username,
                    bio: result.additionalUserInfo.profile["description"],
                    verified: result.additionalUserInfo.profile["verified"],
                    wallet: 0,
                    staff: false,
                    banned: false,
                    hidden: false
                };
                console.log(this.user)
                await this.afs.collection('users').doc(this.uid).set(this.user || {nome: "oops"}).then(res => {
                    console.log("User created", res)
                    code = 2 // Number code for new user
                }).catch(err => {
                    console.log("Error creating user", err)
                    code = 0 // Number code for error
                })
                return 2
            } else {
                await this.getUser(uid).then(res => res.subscribe(res => {
                    if (res == undefined) {
                        this.signOut()
                        code = 4 // Number code for user not found
                    } else {
                        this.user = res
                        console.log("Existing user", this.user)
                        code = 1 // Number code for existing user
                    }
                }))
            }
            return code
        }else {
            return 3
        }
    }).catch((error) => {
        console.warn(error)
        return 0 // Number code for error
    })
}

What really bothers me is this part:

await this.afs.collection('users').doc(this.uid).set(this.user).then(res => {
    console.log("User created", res)
    code = 2 // Number code for new user
}).catch(err => {
    console.log("Error creating user", err)
    code = 0 // Number code for error
})

It never gets to .then() or .catch(), it just gets stuck waiting forever for a response. But, if i put this exact same code, let's say, in the constructor or anywhere else, it works perfectly and actually creates the document in the Firestore Database.

Can anyone suggest me a way to make this work?

1 Answers

Found one, https://github.com/angular/angularfire/issues/2585

So basically they are telling you need to subscribe to ".authState or .user from AngularFireAuth", (note, authState triggers only on login/logout) i'd try something like below but i have no way to test, so sorry if anything:

Also, added some note about your subscribe function below, you can convert them to promise in the way i did also.

import { first, tap } from "rxjs/operators";

processLoggedUser(): Promise<number> {
  return this.afAuth
    .getRedirectResult()
    .then(async (result) => {
      if (result.user) {
        const authUser = await this.afAuth.user
          .pipe(
            // Logs
            tap((user) => {
              console.log("User changed: ", user);
            }),
            // to unsibscribe after first event emitted
            first()
          )
          .toPromise(); // toPromise is tricky, a chance it will not fire for you, i have no way to test.

        const uid = result.user.uid;
        const credential = result.credential as firebase.default.auth.OAuthCredential;
        console.log("Twitter answer of AuthLogin: ", result);
        this.uid = uid;
        let code = 0; // 0 = default code -> error
        if (result.additionalUserInfo.isNewUser) {
          console.log("New user");
          this.user = {
            uid: this.uid,
            email: result.user.email,
            displayName: result.user.displayName,
            photoURL: result.user.photoURL.replace("_normal", ""),
            miniPhotoURL: result.user.photoURL,
            accessToken: credential.accessToken,
            secret: credential.secret,
            twitterId: result.additionalUserInfo.profile["id"],
            username: result.additionalUserInfo.username,
            bio: result.additionalUserInfo.profile["description"],
            verified: result.additionalUserInfo.profile["verified"],
            wallet: 0,
            staff: false,
            banned: false,
            hidden: false
          };
          console.log(this.user);
          await this.afs
            .collection("users")
            .doc(this.uid)
            .set(this.user || { nome: "oops" })
            .then((res) => {
              console.log("User created", res);
              code = 2; // Number code for new user
            })
            .catch((err) => {
              console.log("Error creating user", err);
              code = 0; // Number code for error
            });
          return 2;
        } else {
          await this.getUser(uid).then((res) =>
            // Note: subscribe is not a promise, not awaitable, codes will not be set
            res.subscribe((res) => {
              if (res == undefined) {
                this.signOut();
                code = 4; // Number code for user not found
              } else {
                this.user = res;
                console.log("Existing user", this.user);
                code = 1; // Number code for existing user
              }
            })
          );
        }
        return code;
      } else {
        return 3;
      }
    })
    .catch((error) => {
      console.warn(error);
      return 0; // Number code for error
    });
}
Related