Search code examples
javascriptnode.jsmeteoruser-accounts

Meteor Accounts Profile


I want my users to have a profile with a name and an avatar. I am not able to get this working properly. (in fact, I am inheriting some code). I need someone to tell me how to modify the following code so that I can get my user profile name to be the same as the Username from the signup page.

To be very clear, when a user is created, there is a option to fill in a profile which is a sub document of user. In my profile, I want a name and image. Upon creating a user, we store things like a password and email and usernane. I also want to store the profile, and record the name in the profile as equal to the username. This seems impossible, as you can see from the terrible code where we strip the profile name from the email. Please help me set the profile name upon user creation.

Here is the override of onCreateUser:

import { Accounts } from 'meteor/accounts-base';

Accounts.onCreateUser((options, user) => {
  console.log('\nsign up attempt:', new Date());


  // Handle password signup
  if (user.services.password) {
    const email = user.emails[0].address;
    const name = email.split('@')[0];
    console.log(
      '\nservice --> password',
      '\nname:', name,
      '\nemail:', email,
    );

    // Extend user's profile by adding default name and avatar
    const profile = {
      name,
      avatar: 'user.png',
    };

    return { roles: [], ...user, profile };
  }

  // Handle facebook signup
  if (user.services.facebook) {
    const { id, name, gender, email } = user.services.facebook;
    console.log(
      '\nservice --> facebook',
      '\nname:', name,
      '\nid:', id,
      '\ngender:', gender,
      '\nemail:', email,
    );

    // Extend user's profile by adding facebook data
    const profile = {
      name,
      gender,
      avatar: `http://graph.facebook.com/${id}/picture/`,
    };

    return { roles: [], ...user, profile };
  }

  // Throw in case of a different service
  throw new Error(401, 'Sign up attempt with service different than facebook or password');
});

It is really bad. I don't want to take the user name from the email. Instead, I want to use the username from the login page. Here is some create user code:

import gql from 'graphql-tag';
import Auth from '/app/api/auth';
import { storeLoginToken } from './store';

async function createUser({ username, email, password, profile }, apollo) {
  const result = await apollo.mutate({
    mutation: gql`
      mutation createUser ($username: String, $email: String, $password: HashedPassword!, $profile: CreateUserProfileInput) {
        createUser (username: $username, email: $email, password: $password, profile: $profile) {
          id
          token
          tokenExpires
        }
      }
    `,
    variables: {
      username,
      email,
      password: Auth.hashPassword(password),
      profile,
    },
  });

  const { id, token, tokenExpires } = result.data.createUser;
  await storeLoginToken(id, token, new Date(tokenExpires));
  return id;
}

export default createUser;

Here is some graphql mutation with this weird thing "CreateUserProfileInput" which I don't understand:

import gql from 'graphql-tag';

const types = gql`
  extend input CreateUserInput {
    profile: CreateUserProfileInput!
  }

  input CreateUserProfileInput {
    name: String!
  }


  type Mutation {
    # Create a new user.
    createUser (username: String, email: String, password: HashedPassword, plainPassword: String, profile: CreateUserProfileInput): LoginMethodResponse

Here is my collection snippet with the user profile:

  'profile.name': {
    type: String,
    max: 150,
    optional: true,
  },

  'profile.gender': {
    type: String,
    max: 50,
    optional: true,
  },

  'profile.avatar': {
    type: String,
    max: 150,
    optional: true,
  },

Here is my signup page:

import React from 'react';
import { Link } from 'react-router-dom';
import { compose, setDisplayName } from 'recompose';
import { FormattedMessage as T, injectIntl } from 'react-intl';
import { withRouteProps, withFormProps, withServiceProps, withSEO } from '/app/ui/hocs';
import AuthPageLayout from '/app/ui/layouts/auth-page';
import { PasswordAuthViews, FBAuthBtn } from '/app/ui/components/smart/auth';
import Feedback from '/app/ui/components/dumb/feedback';

const SignupPage = ({
  intl: { formatMessage: t },
  disabled,
  errorMsg,
  successMsg,
  handleBefore,
  handleClientError,
  handleServerError,
  service,
  setService,
  loginUrl,
}) => (
  <AuthPageLayout
    title={t({ id: 'signup' })}
    subtitle={t({ id: 'signupSubTitle' })}
    link={<Link to={loginUrl()}><T id="login" /></Link>}
  >
    <PasswordAuthViews
      view="signup"
      btnLabel={t({ id: 'signup' })}
      disabled={disabled}
      onBeforeHook={() => {
        // Keep track of the auth service being used
        setService('password');
        handleBefore();
      }}
      onClientErrorHook={handleClientError}
      onServerErrorHook={handleServerError}
    />
    {service === 'password' && (
      <Feedback
        loading={disabled}
        errorMsg={errorMsg}
        successMsg={successMsg}
      />
    )}
    {/* <div className="center">
      <T id="signupOrText" />
    </div>
    <FBAuthBtn
      btnLabel={t({ id: 'signupFBButton' })}
      disabled={disabled}
      onBeforeHook={() => {
        // Keep track of the auth service being used
        setService('facebook');
        handleBefore();
      }}
      onServerErrorHook={handleServerError}
    />
    {service === 'facebook' && (
      <Feedback
        loading={disabled}
        errorMsg={errorMsg}
        successMsg={successMsg}
      />
    )} */}
  </AuthPageLayout>
);

export default compose(
  injectIntl,
  withRouteProps,
  withFormProps,
  withServiceProps,
  withSEO({ title: 'signup' }),
  setDisplayName('SignupPage'),
)(SignupPage);

Solution

  • If all you want is to have the profile.name property to be the same as the username, you can extract the username property from user in onCreateUser hook and then pass it to the profile.

    Accounts.onCreateUser((options, user) => {
      console.log('\nsign up attempt:', new Date());
    
    
      // Handle password signup
      if (user.services.password) {
        const email = user.emails[0].address;
        const name = user.username; 
        console.log(
          '\nservice --> password',
          '\nname:', name,
          '\nemail:', email,
        );
    
        // Extend user's profile by adding default name and avatar
        const profile = {
          name,
          avatar: 'user.png',
        };
    
        return { roles: [], ...user, profile };
      }
        ...
    });