diff --git a/.versions b/.versions index e72c513..f32a343 100644 --- a/.versions +++ b/.versions @@ -1,10 +1,10 @@ -accounts-base@1.2.15 +accounts-base@1.2.17 allow-deny@1.0.5 -babel-compiler@6.14.1 +babel-compiler@6.18.2 babel-runtime@1.0.1 base64@1.0.10 binary-heap@1.0.10 -blaze@2.1.8 +blaze@2.3.2 blaze-tools@1.0.9 boilerplate-generator@1.0.11 caching-compiler@1.1.9 @@ -12,35 +12,36 @@ callback-hook@1.0.10 check@1.2.5 coffeescript@1.0.17 ddp@1.2.5 -ddp-client@1.3.3 +ddp-client@1.3.4 ddp-common@1.2.8 ddp-rate-limiter@1.0.7 -ddp-server@1.3.13 +ddp-server@1.3.14 deps@1.0.12 diff-sequence@1.0.7 -ecmascript@0.6.3 +ecmascript@0.7.3 ecmascript-runtime@0.3.15 ejson@1.0.13 -email@1.1.18 +email@1.2.1 geojson-utils@1.0.10 html-tools@1.0.10 -htmljs@1.0.10 +htmljs@1.0.11 id-map@1.0.9 jquery@1.11.10 localstorage@1.0.12 logging@1.1.17 meteor@1.6.1 -minimongo@1.0.21 -modules@0.7.9 -modules-runtime@0.7.9 -mongo@1.1.16 +minimongo@1.0.23 +modules@0.8.2 +modules-runtime@0.7.10 +mongo@1.1.17 mongo-id@1.0.6 npm-mongo@2.2.24 observe-sequence@1.0.16 ordered-dict@1.0.9 promise@0.8.8 random@1.0.10 -rate-limit@1.0.7 +rate-limit@1.0.8 +react-meteor-data@0.2.11 reactive-dict@1.1.8 reactive-var@1.0.11 retry@1.0.9 @@ -50,10 +51,10 @@ session@1.1.7 softwarerero:accounts-t9n@1.3.3 spacebars@1.0.12 spacebars-compiler@1.0.12 -std:accounts-ui@1.2.20 +std:accounts-ui@1.2.22 tmeasday:check-npm-versions@0.3.0 -tracker@1.1.2 +tracker@1.1.3 ui@1.0.11 underscore@1.0.10 -webapp@1.3.14 +webapp@1.3.15 webapp-hashing@1.0.9 diff --git a/CHANGELOG.md b/CHANGELOG.md index 057432b..45ee572 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # ChangeLog +### v1.2.22 +15-Jun-2017 + +* Fixed issue with faulty formState proptype. +* Fixed issue with faulty object iteration. +* Removed lodash dependency. +* Removed tracker dependency. + +### v1.2.21 +26-May-2017 + +* Added functionality to include your own translation function. +* Replaced dependency on tracker-component with react-meteor-data. + ### v1.2.20 13-March-2017 diff --git a/README.md b/README.md index 12016d2..265982d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # React Accounts UI -Current version 1.2.20 +Current version 1.2.22 ## Features @@ -206,52 +206,6 @@ Meteor.startup( () => { }); ``` -As a bonus, here's a component that redirects to the signin route if you're not -logged in, using [`Tracker.Component`](https://www.npmjs.com/package/tracker-component). - -`npm i --save tracker-component` - -```javascript -import React from 'react'; -import Tracker from 'tracker-component'; -import { Meteor } from 'meteor/meteor'; -import { browserHistory } from 'react-router'; - -const AdminPage = () => ( -

Admin

-); - -export class Admin extends Tracker.Component { - constructor(props) { - super(props); - this.autorun(() => { - this.setState({ - isAuthenticated: Meteor.user() - }); - }); - } - - componentWillMount() { - // Check that the user is logged in before the component mounts - if (!this.state.isAuthenticated) { - browserHistory.push(null, '/signin'); - } - } - - componentDidUpdate() { - // Navigate to a sign in page if the user isn't authenticated when data changes - if (!this.state.isAuthenticated) { - browserHistory.push(null, '/signin'); - } - } - - render() { - return ; - } -} - -``` - You can learn more about the remaining components here in the tutorial on [React Router Basics](https://themeteorchef.com/snippets/react-router-basics/) by the Meteor Chef. @@ -399,6 +353,7 @@ To install the dependencies added in your package.json run: // main.jsx import React from 'react'; +import PropTypes from 'prop-types'; import { Accounts, STATES } from 'meteor/std:accounts-ui'; import PropTypes from 'prop-types' @@ -483,6 +438,11 @@ class NewLogin extends Accounts.ui.LoginForm { return super.fields(); } + translate(text) { + // Here you specify your own translation function, e.g. + return this.props.t(text); + } + signUp(options = {}) { const { firstname = null } = this.state; if (firstname !== null) { diff --git a/check-npm.js b/check-npm.js index eff4cfc..b71d90b 100644 --- a/check-npm.js +++ b/check-npm.js @@ -3,5 +3,4 @@ // checkNpmVersions({ // "react": ">=0.14.7 || ^15.0.0-rc.2", // "react-dom": ">=0.14.7 || ^15.0.0-rc.2", -// "tracker-component": "^1.3.13" // }); diff --git a/imports/accounts_ui.js b/imports/accounts_ui.js index 69d6ba1..9bf74d4 100644 --- a/imports/accounts_ui.js +++ b/imports/accounts_ui.js @@ -73,21 +73,21 @@ Accounts.ui.config = function(options) { 'emailPattern', ]; - _.each(_.keys(options), function (key) { - if (!_.contains(VALID_KEYS, key)) + Object.keys(options).forEach(function (key) { + if (!VALID_KEYS.includes(key)) throw new Error("Accounts.ui.config: Invalid key: " + key); }); // Deal with `passwordSignupFields` if (options.passwordSignupFields) { - if (_.contains([ + if ([ "USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "USERNAME_ONLY", "EMAIL_ONLY", "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], options.passwordSignupFields)) { + ].includes(options.passwordSignupFields)) { Accounts.ui._options.passwordSignupFields = options.passwordSignupFields; } else { @@ -97,7 +97,8 @@ Accounts.ui.config = function(options) { // Deal with `requestPermissions` if (options.requestPermissions) { - _.each(options.requestPermissions, function (scope, service) { + Object.keys(options.requestPermissions).forEach(service => { + const score = options.requestPermissions[service]; if (Accounts.ui._options.requestPermissions[service]) { throw new Error("Accounts.ui.config: Can't set `requestPermissions` more than once for " + service); } @@ -112,7 +113,8 @@ Accounts.ui.config = function(options) { // Deal with `requestOfflineToken` if (options.requestOfflineToken) { - _.each(options.requestOfflineToken, function (value, service) { + Object.keys(options.requestOfflineToken).forEach(service => { + const value = options.requestOfflineToken[service]; if (service !== 'google') throw new Error("Accounts.ui.config: `requestOfflineToken` only supported for Google login at the moment."); @@ -127,7 +129,8 @@ Accounts.ui.config = function(options) { // Deal with `forceApprovalPrompt` if (options.forceApprovalPrompt) { - _.each(options.forceApprovalPrompt, function (value, service) { + Object.keys(options.forceApprovalPrompt).forEach(service => { + const value = options.forceApprovalPrompt[service]; if (service !== 'google') throw new Error("Accounts.ui.config: `forceApprovalPrompt` only supported for Google login at the moment."); diff --git a/imports/api/server/loginWithoutPassword.js b/imports/api/server/loginWithoutPassword.js index c86e2c1..73c2b20 100644 --- a/imports/api/server/loginWithoutPassword.js +++ b/imports/api/server/loginWithoutPassword.js @@ -58,12 +58,11 @@ Accounts.sendLoginEmail = function (userId, address) { throw new Error("Can't find user"); // pick the first unverified address if we weren't passed an address. if (!address) { - var email = _.find(user.emails || [], - function (e) { return !e.verified; }); + var email = (user.emails || []).find(({ verified }) => !verified); address = (email || {}).address; } // make sure we have a valid address - if (!address || !_.contains(_.pluck(user.emails || [], 'address'), address)) + if (!address || !(user.emails || []).map(({ address }) => address).includes(address)) throw new Error("No such email address for user."); diff --git a/imports/helpers.js b/imports/helpers.js index 53b6842..60db363 100644 --- a/imports/helpers.js +++ b/imports/helpers.js @@ -18,7 +18,7 @@ export function getLoginServices() { // backwards-compatibility. services.sort(); - return _.map(services, function(name){ + return services.map(function(name){ return {name: name}; }); }; @@ -64,10 +64,10 @@ export function validateEmail(email, showMessage, clearMessage) { if (Accounts.ui._options.emailPattern.test(email)) { return true; } else if (!email || email.length === 0) { - showMessage(T9n.get("error.emailRequired"), 'warning', false, 'email'); + showMessage("error.emailRequired", 'warning', false, 'email'); return false; } else { - showMessage(T9n.get("error.accounts.Invalid email"), 'warning', false, 'email'); + showMessage("error.accounts.Invalid email", 'warning', false, 'email'); return false; } } @@ -76,7 +76,8 @@ export function validatePassword(password = '', showMessage, clearMessage){ if (password.length >= Accounts.ui._options.minimumPasswordLength) { return true; } else { - const errMsg = T9n.get("error.minChar").replace(/7/, Accounts.ui._options.minimumPasswordLength); + // const errMsg = T9n.get("error.minChar").replace(/7/, Accounts.ui._options.minimumPasswordLength); + const errMsg = "error.minChar" showMessage(errMsg, 'warning', false, 'password'); return false; } @@ -87,7 +88,7 @@ export function validateUsername(username, showMessage, clearMessage, formState) return true; } else { const fieldName = (passwordSignupFields() === 'USERNAME_ONLY' || formState === STATES.SIGN_UP) ? 'username' : 'usernameOrEmail'; - showMessage(T9n.get("error.usernameRequired"), 'warning', false, fieldName); + showMessage("error.usernameRequired", 'warning', false, fieldName); return false; } } diff --git a/imports/login_session.js b/imports/login_session.js index 26d38c1..618b0a7 100644 --- a/imports/login_session.js +++ b/imports/login_session.js @@ -30,7 +30,7 @@ const VALID_KEYS = [ ]; export const validateKey = function (key) { - if (!_.contains(VALID_KEYS, key)) + if (!VALID_KEYS.includes(key)) throw new Error("Invalid key in loginButtonsSession: " + key); }; @@ -43,7 +43,7 @@ export const KEY_PREFIX = "Meteor.loginButtons."; Accounts._loginButtonsSession = { set: function(key, value) { validateKey(key); - if (_.contains(['errorMessage', 'infoMessage'], key)) + if (['errorMessage', 'infoMessage'].includes(key)) throw new Error("Don't set errorMessage or infoMessage directly. Instead, use errorMessage() or infoMessage()."); this._set(key, value); @@ -68,7 +68,7 @@ if (Meteor.isClient){ // Accounts.onPageLoadLogin(function (attemptInfo) { // Ignore if we have a left over login attempt for a service that is no longer registered. - if (_.contains(_.pluck(getLoginServices(), "name"), attemptInfo.type)) + if (getLoginServices().map(({ name }) => name).includes(attemptInfo.type)) loginResultCallback(attemptInfo.type, attemptInfo.error); }); diff --git a/imports/ui/components/Button.jsx b/imports/ui/components/Button.jsx index 99e1e7e..05f6559 100644 --- a/imports/ui/components/Button.jsx +++ b/imports/ui/components/Button.jsx @@ -1,15 +1,11 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Accounts } from 'meteor/accounts-base'; -import PropTypes from 'prop-types' let Link; try { Link = require('react-router').Link; } catch(e) {} export class Button extends React.Component { - propTypes: { - onClick: PropTypes.func - } - render () { const { label, @@ -34,4 +30,8 @@ export class Button extends React.Component { } } +Button.propTypes = { + onClick: PropTypes.func +}; + Accounts.ui.Button = Button; diff --git a/imports/ui/components/Field.jsx b/imports/ui/components/Field.jsx index 317dff5..4636d68 100644 --- a/imports/ui/components/Field.jsx +++ b/imports/ui/components/Field.jsx @@ -1,12 +1,8 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { Accounts } from 'meteor/accounts-base'; -import PropTypes from 'prop-types' export class Field extends React.Component { - propTypes: { - onChange: PropTypes.func - } - constructor(props) { super(props); this.state = { @@ -74,4 +70,8 @@ export class Field extends React.Component { } } +Field.propTypes = { + onChange: PropTypes.func +}; + Accounts.ui.Field = Field; diff --git a/imports/ui/components/Form.jsx b/imports/ui/components/Form.jsx index fcaafb4..833209f 100644 --- a/imports/ui/components/Form.jsx +++ b/imports/ui/components/Form.jsx @@ -1,7 +1,7 @@ import React from 'react'; +import PropTypes from 'prop-types'; import ReactDOM from 'react-dom'; import { Accounts } from 'meteor/accounts-base'; -import PropTypes from 'prop-types' import './Fields.jsx'; import './Buttons.jsx'; @@ -11,14 +11,6 @@ import './SocialButtons.jsx'; import './FormMessages.jsx'; export class Form extends React.Component { - propTypes: { - oauthServices: PropTypes.object, - fields: PropTypes.object.isRequired, - buttons: PropTypes.object.isRequired, - error: PropTypes.string, - ready: PropTypes.bool - }; - componentDidMount() { let form = this.form; if (form) { @@ -36,6 +28,7 @@ export class Form extends React.Component { buttons, error, messages, + translate, ready = true, className } = this.props; @@ -48,7 +41,7 @@ export class Form extends React.Component { > - + @@ -56,4 +49,13 @@ export class Form extends React.Component { } } +Form.propTypes = { + oauthServices: PropTypes.object, + fields: PropTypes.object.isRequired, + buttons: PropTypes.object.isRequired, + translate: PropTypes.func.isRequired, + error: PropTypes.string, + ready: PropTypes.bool +}; + Accounts.ui.Form = Form; diff --git a/imports/ui/components/FormMessage.jsx b/imports/ui/components/FormMessage.jsx index b5fcb4e..112bfee 100644 --- a/imports/ui/components/FormMessage.jsx +++ b/imports/ui/components/FormMessage.jsx @@ -1,6 +1,10 @@ import React from 'react'; import { Accounts } from 'meteor/accounts-base'; +function isObject(obj) { + return obj === Object(obj); +} + export class FormMessage extends React.Component { render () { let { message, type, className = "message", style = {}, deprecated } = this.props; @@ -9,7 +13,7 @@ export class FormMessage extends React.Component { // Found backwords compatibility issue. console.warn('You are overriding Accounts.ui.Form and using FormMessage, the use of FormMessage in Form has been depreacted in v1.2.11, update your implementation to use FormMessages: https://github.com/studiointeract/accounts-ui/#deprecations'); } - message = _.isObject(message) ? message.message : message; // If message is object, then try to get message from it + message = isObject(message) ? message.message : message; // If message is object, then try to get message from it return message ? (
{ message }
diff --git a/imports/ui/components/LoginForm.jsx b/imports/ui/components/LoginForm.jsx index cc51186..7593083 100644 --- a/imports/ui/components/LoginForm.jsx +++ b/imports/ui/components/LoginForm.jsx @@ -1,6 +1,6 @@ -import React from 'react'; +import React, { PropTypes, Component } from 'react'; import ReactDOM from 'react-dom'; -import Tracker from 'tracker-component'; +import { createContainer } from 'meteor/react-meteor-data'; import { Accounts } from 'meteor/accounts-base'; import { T9n } from 'meteor/softwarerero:accounts-t9n'; import { KEY_PREFIX } from '../../login_session.js'; @@ -18,7 +18,15 @@ import { capitalize } from '../../helpers.js'; -export class LoginForm extends Tracker.Component { +function indexBy(array, key) { + const result = {}; + array.forEach(function(obj) { + result[obj[key]] = obj; + }); + return result; +} + +class LoginForm extends Component { constructor(props) { super(props); let { @@ -45,6 +53,7 @@ export class LoginForm extends Tracker.Component { onPreSignUpHook: props.onPreSignUpHook || Accounts.ui._options.onPreSignUpHook, onPostSignUpHook: props.onPostSignUpHook || Accounts.ui._options.onPostSignUpHook, }; + this.translate = this.translate.bind(this); } componentDidMount() { @@ -71,22 +80,11 @@ export class LoginForm extends Tracker.Component { Session.set(KEY_PREFIX + 'state', null); break; } - + // Add default field values once the form did mount on the client this.setState(prevState => ({ ...this.getDefaultFieldValues(), })); - - // Listen for the user to login/logout. - this.autorun(() => { - - // Add the services list to the user. - this.subscribe('servicesList'); - this.setState({ - user: Accounts.user() - }); - - }); } componentWillReceiveProps(nextProps, nextContext) { @@ -99,13 +97,20 @@ export class LoginForm extends Tracker.Component { } componentDidUpdate(prevProps, prevState) { - if (!prevState.user !== !this.state.user) { + if (!prevProps.user !== !this.props.user) { this.setState({ - formState: this.state.user ? STATES.PROFILE : STATES.SIGN_IN + formState: this.props.user ? STATES.PROFILE : STATES.SIGN_IN }); } } + translate(text) { + // if (this.props.t) { + // return this.props.t(text); + // } + return T9n.get(text); + } + validateField(field, value) { const { formState } = this.state; switch(field) { @@ -131,8 +136,8 @@ export class LoginForm extends Tracker.Component { getUsernameOrEmailField() { return { id: 'usernameOrEmail', - hint: T9n.get('enterUsernameOrEmail'), - label: T9n.get('usernameOrEmail'), + hint: this.translate('enterUsernameOrEmail'), + label: this.translate('usernameOrEmail'), required: true, defaultValue: this.state.username || "", onChange: this.handleChange.bind(this, 'usernameOrEmail'), @@ -143,8 +148,8 @@ export class LoginForm extends Tracker.Component { getUsernameField() { return { id: 'username', - hint: T9n.get('enterUsername'), - label: T9n.get('username'), + hint: this.translate('enterUsername'), + label: this.translate('username'), required: true, defaultValue: this.state.username || "", onChange: this.handleChange.bind(this, 'username'), @@ -155,8 +160,8 @@ export class LoginForm extends Tracker.Component { getEmailField() { return { id: 'email', - hint: T9n.get('enterEmail'), - label: T9n.get('email'), + hint: this.translate('enterEmail'), + label: this.translate('email'), type: 'email', required: true, defaultValue: this.state.email || "", @@ -168,8 +173,8 @@ export class LoginForm extends Tracker.Component { getPasswordField() { return { id: 'password', - hint: T9n.get('enterPassword'), - label: T9n.get('password'), + hint: this.translate('enterPassword'), + label: this.translate('password'), type: 'password', required: true, defaultValue: this.state.password || "", @@ -181,8 +186,8 @@ export class LoginForm extends Tracker.Component { getSetPasswordField() { return { id: 'newPassword', - hint: T9n.get('enterPassword'), - label: T9n.get('choosePassword'), + hint: this.translate('enterPassword'), + label: this.translate('choosePassword'), type: 'password', required: true, onChange: this.handleChange.bind(this, 'newPassword') @@ -192,8 +197,8 @@ export class LoginForm extends Tracker.Component { getNewPasswordField() { return { id: 'newPassword', - hint: T9n.get('enterNewPassword'), - label: T9n.get('newPassword'), + hint: this.translate('enterNewPassword'), + label: this.translate('newPassword'), type: 'password', required: true, onChange: this.handleChange.bind(this, 'newPassword'), @@ -225,11 +230,11 @@ export class LoginForm extends Tracker.Component { } if (hasPasswordService() && formState == STATES.SIGN_IN) { - if (_.contains([ + if ([ "USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getUsernameOrEmailField()); } @@ -237,48 +242,48 @@ export class LoginForm extends Tracker.Component { loginFields.push(this.getUsernameField()); } - if (_.contains([ + if ([ "EMAIL_ONLY", "EMAIL_ONLY_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getEmailField()); } - if (!_.contains([ + if (![ "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getPasswordField()); } } if (hasPasswordService() && formState == STATES.SIGN_UP) { - if (_.contains([ + if ([ "USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "USERNAME_ONLY", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getUsernameField()); } - if (_.contains([ + if ([ "USERNAME_AND_EMAIL", "EMAIL_ONLY", "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getEmailField()); } - if (_.contains(["USERNAME_AND_OPTIONAL_EMAIL"], passwordSignupFields())) { + if (["USERNAME_AND_OPTIONAL_EMAIL"].includes(passwordSignupFields())) { loginFields.push(Object.assign(this.getEmailField(), {required: false})); } - if (!_.contains([ + if (![ "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { loginFields.push(this.getPasswordField()); } } @@ -297,8 +302,7 @@ export class LoginForm extends Tracker.Component { if (this.showEnrollAccountForm()) { loginFields.push(this.getSetPasswordField()); } - - return _.indexBy(loginFields, 'id'); + return indexBy(loginFields, 'id'); } buttons() { @@ -309,13 +313,14 @@ export class LoginForm extends Tracker.Component { changePasswordPath = Accounts.ui._options.changePasswordPath, profilePath = Accounts.ui._options.profilePath, } = this.props; - const { formState, waiting, user } = this.state; + const { user } = this.props; + const { formState, waiting } = this.state; let loginButtons = []; if (user && formState == STATES.PROFILE) { loginButtons.push({ id: 'signOut', - label: T9n.get('signOut'), + label: this.translate('signOut'), disabled: waiting, onClick: this.signOut.bind(this) }); @@ -324,7 +329,7 @@ export class LoginForm extends Tracker.Component { if (this.showCreateAccountLink()) { loginButtons.push({ id: 'switchToSignUp', - label: T9n.get('signUp'), + label: this.translate('signUp'), type: 'link', href: signUpPath, onClick: this.switchToSignUp.bind(this) @@ -334,7 +339,7 @@ export class LoginForm extends Tracker.Component { if (formState == STATES.SIGN_UP || formState == STATES.PASSWORD_RESET) { loginButtons.push({ id: 'switchToSignIn', - label: T9n.get('signIn'), + label: this.translate('signIn'), type: 'link', href: loginPath, onClick: this.switchToSignIn.bind(this) @@ -344,22 +349,22 @@ export class LoginForm extends Tracker.Component { if (this.showForgotPasswordLink()) { loginButtons.push({ id: 'switchToPasswordReset', - label: T9n.get('forgotPassword'), + label: this.translate('forgotPassword'), type: 'link', href: resetPasswordPath, onClick: this.switchToPasswordReset.bind(this) }); } - if (user && !_.contains([ + if (user && ![ "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields()) + ].includes(passwordSignupFields()) && formState == STATES.PROFILE && (user.services && 'password' in user.services)) { loginButtons.push({ id: 'switchToChangePassword', - label: T9n.get('changePassword'), + label: this.translate('changePassword'), type: 'link', href: changePasswordPath, onClick: this.switchToChangePassword.bind(this) @@ -369,7 +374,7 @@ export class LoginForm extends Tracker.Component { if (formState == STATES.SIGN_UP) { loginButtons.push({ id: 'signUp', - label: T9n.get('signUp'), + label: this.translate('signUp'), type: hasPasswordService() ? 'submit' : 'link', className: 'active', disabled: waiting, @@ -380,7 +385,7 @@ export class LoginForm extends Tracker.Component { if (this.showSignInLink()) { loginButtons.push({ id: 'signIn', - label: T9n.get('signIn'), + label: this.translate('signIn'), type: hasPasswordService() ? 'submit' : 'link', className: 'active', disabled: waiting, @@ -391,7 +396,7 @@ export class LoginForm extends Tracker.Component { if (formState == STATES.PASSWORD_RESET) { loginButtons.push({ id: 'emailResetLink', - label: T9n.get('resetYourPassword'), + label: this.translate('resetYourPassword'), type: 'submit', disabled: waiting, onClick: this.passwordReset.bind(this) @@ -401,7 +406,7 @@ export class LoginForm extends Tracker.Component { if (this.showPasswordChangeForm() || this.showEnrollAccountForm()) { loginButtons.push({ id: 'changePassword', - label: (this.showPasswordChangeForm() ? T9n.get('changePassword') : T9n.get('setPassword')), + label: (this.showPasswordChangeForm() ? this.translate('changePassword') : this.translate('setPassword')), type: 'submit', disabled: waiting, onClick: this.passwordChange.bind(this) @@ -410,7 +415,7 @@ export class LoginForm extends Tracker.Component { if (Accounts.user()) { loginButtons.push({ id: 'switchToSignOut', - label: T9n.get('cancel'), + label: this.translate('cancel'), type: 'link', href: profilePath, onClick: this.switchToSignOut.bind(this) @@ -418,7 +423,7 @@ export class LoginForm extends Tracker.Component { } else { loginButtons.push({ id: 'cancelResetPassword', - label: T9n.get('cancel'), + label: this.translate('cancel'), type: 'link', onClick: this.cancelResetPassword.bind(this), }); @@ -435,7 +440,7 @@ export class LoginForm extends Tracker.Component { b.type != undefined); }); - return _.indexBy(loginButtons, 'id'); + return indexBy(loginButtons, 'id'); } showSignInLink(){ @@ -457,11 +462,9 @@ export class LoginForm extends Tracker.Component { } showForgotPasswordLink() { - return !this.state.user + return !this.props.user && this.state.formState == STATES.SIGN_IN - && _.contains( - ["USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "EMAIL_ONLY"], - passwordSignupFields()); + && ["USERNAME_AND_EMAIL", "USERNAME_AND_OPTIONAL_EMAIL", "EMAIL_ONLY"].includes(passwordSignupFields()); } /** @@ -587,7 +590,7 @@ export class LoginForm extends Tracker.Component { error = true; } else { - if (_.contains([ "USERNAME_AND_EMAIL_NO_PASSWORD" ], passwordSignupFields())) { + if (["USERNAME_AND_EMAIL_NO_PASSWORD"].includes(passwordSignupFields())) { this.loginWithoutPassword(); return; } else { @@ -610,7 +613,7 @@ export class LoginForm extends Tracker.Component { error = true; } else { - if (_.contains([ "EMAIL_ONLY_NO_PASSWORD" ], passwordSignupFields())) { + if (["EMAIL_ONLY_NO_PASSWORD"].includes(passwordSignupFields())) { this.loginWithoutPassword(); error = true; } else { @@ -618,7 +621,7 @@ export class LoginForm extends Tracker.Component { } } } - if (!_.contains([ "EMAIL_ONLY_NO_PASSWORD" ], passwordSignupFields()) + if (!["EMAIL_ONLY_NO_PASSWORD"].includes(passwordSignupFields()) && !this.validateField('password', password)) { error = true; } @@ -627,7 +630,7 @@ export class LoginForm extends Tracker.Component { Meteor.loginWithPassword(loginSelector, password, (error, result) => { onSubmitHook(error,formState); if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); } else { loginResultCallback(() => this.state.onSignedInHook()); @@ -658,11 +661,12 @@ export class LoginForm extends Tracker.Component { }); } } - return _.indexBy(oauthButtons, 'id'); + return indexBy(oauthButtons, 'id'); } oauthSignIn(serviceName) { - const { formState, waiting, user, onSubmitHook } = this.state; + const { user } = this.props; + const { formState, waiting, onSubmitHook } = this.state; //Thanks Josh Owens for this one. function capitalService() { return serviceName.charAt(0).toUpperCase() + serviceName.slice(1); @@ -683,10 +687,11 @@ export class LoginForm extends Tracker.Component { options.forceApprovalPrompt = Accounts.ui._options.forceApprovalPrompt[serviceName]; this.clearMessages(); + const self = this loginWithService(options, (error) => { onSubmitHook(error,formState); if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error")); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error"); } else { this.setState({ formState: STATES.PROFILE }); this.clearDefaultFieldValues(); @@ -720,10 +725,10 @@ export class LoginForm extends Tracker.Component { options.username = username; } } else { - if (_.contains([ + if ([ "USERNAME_AND_EMAIL", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields()) && !this.validateField('username', username) ) { + ].includes(passwordSignupFields()) && !this.validateField('username', username) ) { if (this.state.formState == STATES.SIGN_UP) { this.state.onSubmitHook("error.accounts.usernameRequired", this.state.formState); } @@ -737,10 +742,10 @@ export class LoginForm extends Tracker.Component { options.email = email; } - if (_.contains([ + if ([ "EMAIL_ONLY_NO_PASSWORD", "USERNAME_AND_EMAIL_NO_PASSWORD" - ], passwordSignupFields())) { + ].includes(passwordSignupFields())) { // Generate a random password. options.password = Meteor.uuid(); } else if (!this.validateField('password', password)) { @@ -753,12 +758,12 @@ export class LoginForm extends Tracker.Component { const SignUp = function(_options) { Accounts.createUser(_options, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); - if (T9n.get(`error.accounts.${error.reason}`)) { + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); + if (this.translate(`error.accounts.${error.reason}`)) { onSubmitHook(`error.accounts.${error.reason}`, formState); } else { - onSubmitHook("Unknown error", formState); + onSubmitHook("unknown_error", formState); } } else { @@ -804,10 +809,10 @@ export class LoginForm extends Tracker.Component { Accounts.loginWithoutPassword({ email: email }, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); } else { - this.showMessage(T9n.get("info.emailSent"), 'success', 5000); + this.showMessage(this.translate("info.emailSent"), 'success', 5000); this.clearDefaultFieldValues(); } onSubmitHook(error, formState); @@ -818,10 +823,10 @@ export class LoginForm extends Tracker.Component { Accounts.loginWithoutPassword({ email: usernameOrEmail, username: usernameOrEmail }, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); } else { - this.showMessage(T9n.get("info.emailSent"), 'success', 5000); + this.showMessage(this.translate("info.emailSent"), 'success', 5000); this.clearDefaultFieldValues(); } onSubmitHook(error, formState); @@ -829,11 +834,11 @@ export class LoginForm extends Tracker.Component { }); } else { let errMsg = null; - if (_.contains([ "USERNAME_AND_EMAIL_NO_PASSWORD" ], passwordSignupFields())) { - errMsg = T9n.get("error.accounts.Invalid email or username"); + if (["USERNAME_AND_EMAIL_NO_PASSWORD"].includes(passwordSignupFields())) { + errMsg = this.translate("error.accounts.invalid_email"); } else { - errMsg = T9n.get("error.accounts.Invalid email"); + errMsg = this.translate("error.accounts.invalid_email"); } this.showMessage(errMsg,'warning'); onSubmitHook(errMsg, formState); @@ -858,10 +863,10 @@ export class LoginForm extends Tracker.Component { Accounts.forgotPassword({ email: email }, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); } else { - this.showMessage(T9n.get("info.emailSent"), 'success', 5000); + this.showMessage(this.translate("info.emailSent"), 'success', 5000); this.clearDefaultFieldValues(); } onSubmitHook(error, formState); @@ -891,11 +896,11 @@ export class LoginForm extends Tracker.Component { if (token) { Accounts.resetPassword(token, newPassword, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); onSubmitHook(error, formState); } else { - this.showMessage(T9n.get('info.passwordChanged'), 'success', 5000); + this.showMessage(this.translate('info.passwordChanged'), 'success', 5000); onSubmitHook(null, formState); this.setState({ formState: STATES.PROFILE }); Accounts._loginButtonsSession.set('resetPasswordToken', null); @@ -907,11 +912,11 @@ export class LoginForm extends Tracker.Component { else { Accounts.changePassword(password, newPassword, (error) => { if (error) { - this.showMessage(T9n.get(`error.accounts.${error.reason}`) || T9n.get("Unknown error"), 'error'); + this.showMessage(`error.accounts.${error.reason}` || "unknown_error", 'error'); onSubmitHook(error, formState); } else { - this.showMessage(T9n.get('info.passwordChanged'), 'success', 5000); + this.showMessage('info.passwordChanged', 'success', 5000); onSubmitHook(null, formState); this.setState({ formState: STATES.PROFILE }); this.clearDefaultFieldValues(); @@ -921,7 +926,7 @@ export class LoginForm extends Tracker.Component { } showMessage(message, type, clearTimeout, field){ - message = message.trim(); + message = this.translate(message).trim(); if (message) { this.setState(({ messages = [] }) => { messages.push({ @@ -994,9 +999,34 @@ export class LoginForm extends Tracker.Component { buttons={this.buttons()} {...this.state} message={message} + translate={text => this.translate(text)} /> ); } } +LoginForm.propTypes = { + formState: PropTypes.symbol, + loginPath: PropTypes.string, + signUpPath: PropTypes.string, + resetPasswordPath: PropTypes.string, + profilePath: PropTypes.string, + changePasswordPath: PropTypes.string, +}; +LoginForm.defaultProps = { + formState: null, + loginPath: null, + signUpPath: null, + resetPasswordPath: null, + profilePath: null, + changePasswordPath: null, +}; Accounts.ui.LoginForm = LoginForm; + +export default createContainer(() => { + // Listen for the user to login/logout and the services list to the user. + Meteor.subscribe('servicesList'); + return ({ + user: Accounts.user(), + }); +}, LoginForm); diff --git a/imports/ui/components/PasswordOrService.jsx b/imports/ui/components/PasswordOrService.jsx index a1127f0..6d983a7 100644 --- a/imports/ui/components/PasswordOrService.jsx +++ b/imports/ui/components/PasswordOrService.jsx @@ -5,10 +5,6 @@ import { T9n } from 'meteor/softwarerero:accounts-t9n'; import { hasPasswordService } from '../../helpers.js'; export class PasswordOrService extends React.Component { - propTypes: { - oauthServices: PropTypes.object - } - constructor(props) { super(props); this.state = { @@ -19,6 +15,13 @@ export class PasswordOrService extends React.Component { }; } + translate(text) { + if (this.props.translate) { + return this.props.translate(text); + } + return T9n.get(text); + } + render () { let { className = "password-or-service", style = {} } = this.props; let { hasPasswordService, services } = this.state; @@ -30,7 +33,7 @@ export class PasswordOrService extends React.Component { if (hasPasswordService && services.length > 0) { return (
- { `${T9n.get('orUse')} ${ labels.join(' / ') }` } + { `${this.translate('orUse')} ${ labels.join(' / ') }` }
); } @@ -38,4 +41,8 @@ export class PasswordOrService extends React.Component { } } +PasswordOrService.propTypes = { + oauthServices: PropTypes.object +}; + Accounts.ui.PasswordOrService = PasswordOrService; diff --git a/main_client.js b/main_client.js index 68919b9..e73715e 100644 --- a/main_client.js +++ b/main_client.js @@ -3,8 +3,10 @@ import './imports/accounts_ui.js'; import './imports/login_session.js'; import { STATES } from './imports/helpers.js'; import './imports/api/client/loginWithoutPassword.js'; +import LoginForm from './imports/ui/components/LoginForm.jsx'; -import './imports/ui/components/LoginForm.jsx'; - -export { Accounts, STATES }; -export default Accounts; +export { + LoginForm as default, + Accounts, + STATES, +}; diff --git a/main_server.js b/main_server.js index 6dfc35e..10d0d78 100644 --- a/main_server.js +++ b/main_server.js @@ -4,8 +4,10 @@ import './imports/login_session.js'; import { redirect, STATES } from './imports/helpers.js'; import './imports/api/server/loginWithoutPassword.js'; import './imports/api/server/servicesListPublication.js'; +import LoginForm from './imports/ui/components/LoginForm.jsx'; -import './imports/ui/components/LoginForm.jsx'; - -export { Accounts, redirect, STATES }; -export default Accounts; +export { + LoginForm as default, + Accounts, + STATES, +}; diff --git a/package.js b/package.js index cbab22e..d60393c 100644 --- a/package.js +++ b/package.js @@ -1,6 +1,6 @@ Package.describe({ name: 'std:accounts-ui', - version: '1.2.20', + version: '1.2.22', summary: 'Accounts UI for React in Meteor 1.3+', git: 'https://github.com/studiointeract/accounts-ui', documentation: 'README.md' @@ -9,13 +9,12 @@ Package.describe({ Package.onUse(function(api) { api.versionsFrom('1.3'); api.use('ecmascript'); - api.use('tracker'); - api.use('underscore'); api.use('accounts-base'); api.use('check'); api.use('random'); api.use('email'); api.use('session'); + api.use('react-meteor-data@0.2.11'); api.use('softwarerero:accounts-t9n'); api.use('tmeasday:check-npm-versions@0.3.0'); diff --git a/package.json b/package.json index 8ef9dd9..563f427 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,8 @@ "prop-types": "^15.5.8" }, "peerDependencies": { - "react": ">=0.14.7 || ^15.0.0", - "react-dom": ">=0.14.7 || ^15.0.0", - "tracker-component": "^1.3.16" + "react": "^15.0.0", + "react-dom": "^15.0.0", + "react-addons-pure-render-mixin": "^15.0.0" } }