1
votes

Users in my Meteor app can create accounts 'manually' or with the accounts-facebook package.

If they create an account manually then in the database their email is stored like this:

emails: [address: '[email protected]', verified: false]

But if they use the Facebook login then its stored like this:

services: {
  facebook: {
    email: "[email protected]"
  }
}

I have an user account page where I need to display the users email and allow them to change it. How can I deal with the varying structure?

I made this React component to display the users email. When I just had the default Meteor user profiles it worked, but now Ive added Facebook login it errors as props.user.emails doenst exist.

        <div className="form-primary__row">
          <label>Email:</label>
          {props.user.emails.map((item, i) => {
            return (
              <input defaultValue={item.address} key={i} name="email" />
            );
          })}
        </div>

This is my method for a user to update their email. It also worked when I just had Meteors accounts but won't work with Facebook.

Meteor.methods({
  'user.updateEmail'({ email }) {
    Meteor.users.update(
      { _id: Meteor.userId() },
      {
        $set: {
          'emails.0.address': email,
          'emails.0.verified': false,
        },
      },
    );
  },
});
1
How about storing manual emails also in a similar structure format? Eg. services.manual.email Assuming that there can only exist either manual emails OR fb emails, you can program your logic to return like either of the two, based on whichever exists. I believe this can be normalized on the client code and sent back as a standard object to the client so that the client need not bother about the structure of the object it received. - blueren
Can you normalize in a publish function? If I normalize in a client component eg my accounts page then I may need to do the same on another page, which violates the DRY principal. - Evanss
Ok, I think you can make use of Accounts.onCreateUser(). When a new user is created (irrespective of manual or via fb), pull the respective email and populate the emails array. That way, you don't have to touch your publications or your client side rendering - blueren

1 Answers

1
votes

One approach is to use Accounts.onCreated()

The function should return the user document (either the one passed in or a newly-created object) with whatever modifications are desired. The returned document is inserted directly into the Meteor.users collection.

Accounts.onCreateUser(function (options, user) {
    // if the account is created using the manual approach,
    // simply return the user object that will be inserted into
    // the Users collection.
    if (!user.services.facebook) {
        return user;
    }

    // if user is created using fb's API,
    // manually set the emails array, then return the user object
    // which will be inserted into the Users collection.
    user.username = user.services.facebook.name;
    user.emails = [{address: user.services.facebook.email}];

    return user;
});

The above ensures that the emails array always contains the email, whichever the login method the user chooses to use.