Create separate preferences localStorage item; remove username

This commit is contained in:
Robbie Antenesse 2018-06-23 13:30:04 -06:00
parent c6abce8cc6
commit a3755d4ee7
7 changed files with 137 additions and 62 deletions

View File

@ -5,6 +5,9 @@ export const DEFAULT_USER_DATA = {
username: '',
publicName: '',
allowEmails: true,
};
export const DEFAULT_PREFERENCES = {
useIPAPronunciation: true,
itemsPerPage: 30,
};

View File

@ -10,7 +10,7 @@ import { Pagination } from './structure/Pagination';
import { WordForm } from './management/WordForm';
import { DictionaryDetails } from './display/DictionaryDetails';
import { WordsList } from './display/WordsList';
import { DEFAULT_USER_DATA } from '../Constants';
import { DEFAULT_PREFERENCES } from '../Constants';
export class MainDisplay extends Component {
constructor (props) {
@ -56,8 +56,8 @@ export class MainDisplay extends Component {
}
render () {
const userData = store.get('LexicongaUserData');
const itemsPerPage = userData && userData.hasOwnProperty('itemsPerPage') ? userData.itemsPerPage : DEFAULT_USER_DATA.itemsPerPage;
const preferences = store.get('LexicongaPreferences');
const itemsPerPage = preferences && preferences.hasOwnProperty('itemsPerPage') ? preferences.itemsPerPage : DEFAULT_PREFERENCES.itemsPerPage;
const {
dictionaryInfo,
isLoadingWords,

View File

@ -26,19 +26,19 @@ export class LoginForm extends Component {
loginPasswordError: '',
loginFormIsValid: true,
signupEmail: '',
signupUsername: '',
// signupUsername: '',
signupPublicName: '',
signupPassword: '',
signupConfirm: '',
signupAllowEmail: true,
signupEmailError: '',
signupUsernameError: '',
// signupUsernameError: '',
signupPasswordError: '',
signupConfirmError: '',
signupEmailChecking: false,
signupUsernameChecking: false,
// signupUsernameChecking: false,
signupEmailIsUnique: true,
signupUsernameIsUnique: true,
// signupUsernameIsUnique: true,
signupFormIsValid: true,
};
}
@ -56,15 +56,15 @@ export class LoginForm extends Component {
signupEmailError,
signupEmailChecking,
signupEmailIsUnique,
signupUsernameError,
signupUsernameChecking,
signupUsernameIsUnique,
// signupUsernameError,
// signupUsernameChecking,
// signupUsernameIsUnique,
signupPasswordError,
signupConfirmError,
} = this.state;
return !signupEmailChecking && !signupUsernameChecking
&& signupEmailIsUnique && signupUsernameIsUnique
&& signupEmailError === '' && signupUsernameError === ''
&& signupEmailIsUnique && signupEmailError === ''
// && signupUsernameIsUnique && signupUsernameError === ''
&& signupPasswordError === '' && signupConfirmError === '';
}
@ -101,12 +101,12 @@ export class LoginForm extends Component {
}
}
if (field === 'signupUsername') {
if (value !== '' && /[^a-zA-Z0-9]+/g.test(value)) {
isValid = false;
fieldErrors[errorFieldName] = 'Please use only letters and numbers';
}
}
// if (field === 'signupUsername') {
// if (value !== '' && /[^a-zA-Z0-9]+/g.test(value)) {
// isValid = false;
// fieldErrors[errorFieldName] = 'Please use only letters and numbers';
// }
// }
if (isValid) {
fieldErrors[errorFieldName] = '';
@ -118,17 +118,22 @@ export class LoginForm extends Component {
const {
signupEmailChecking,
signupEmailIsUnique,
signupUsernameChecking,
signupUsernameIsUnique,
// signupUsernameChecking,
// signupUsernameIsUnique,
} = this.state;
const fields = ['signupEmail', 'signupUsername', 'signupPassword', 'signupConfirm'];
const fields = [
'signupEmail',
// 'signupUsername',
'signupPassword',
'signupConfirm'
];
let errors = {};
fields.forEach(field => {
const fieldErrors = this.validateField(field, this.state[field]);
errors = Object.assign(errors, fieldErrors);
});
errors.signupFormIsValid = !signupEmailChecking && !signupUsernameChecking
&& signupEmailIsUnique && signupUsernameIsUnique
errors.signupFormIsValid = !signupEmailChecking && signupEmailIsUnique
// && !signupUsernameChecking && signupUsernameIsUnique
&& Object.keys(errors).every(field => errors[field] === '');
this.setState(errors, callback);
}
@ -163,21 +168,22 @@ export class LoginForm extends Component {
this.setState(fieldUpdate);
});
});
} else if (field === 'signupUsername') {
this.setState({ signupUsernameChecking: true }, () => {
request('check-username', { username: value }, (response) => {
const { data, error } = response;
fieldUpdate['signupUsernameChecking'] = false;
if (error) {
console.error(data);
} else {
fieldUpdate['signupUsernameIsUnique'] = !data;
}
}).then(() => {
this.setState(fieldUpdate);
});
});
}
// else if (field === 'signupUsername') {
// this.setState({ signupUsernameChecking: true }, () => {
// request('check-username', { username: value }, (response) => {
// const { data, error } = response;
// fieldUpdate['signupUsernameChecking'] = false;
// if (error) {
// console.error(data);
// } else {
// fieldUpdate['signupUsernameIsUnique'] = !data;
// }
// }).then(() => {
// this.setState(fieldUpdate);
// });
// });
// }
}
logIn () {
@ -194,13 +200,13 @@ export class LoginForm extends Component {
if (this.signupFormIsValid) {
const {
signupEmail,
signupUsername,
// signupUsername,
signupPublicName,
signupPassword,
signupAllowEmail
} = this.state;
this.props.signUp(signupEmail, signupPassword, {
username: signupUsername,
// username: signupUsername,
publicName: signupPublicName,
allowEmail: signupAllowEmail,
});
@ -234,7 +240,7 @@ export class LoginForm extends Component {
</h3>
<div className='field'>
<label className='label'>
Email/Username
Email
</label>
<div className='control'>
<input className={`input ${this.state.loginEmailError !== '' && 'is-danger'}`}
@ -320,7 +326,7 @@ export class LoginForm extends Component {
}
</div>
</div>
<div className='field'>
{/* <div className='field'>
<label className='label'>
Username
</label>
@ -342,7 +348,7 @@ export class LoginForm extends Component {
) : null
}
</div>
</div>
</div> */}
<div className='field'>
<label className='label'>
Public Name

View File

@ -8,13 +8,13 @@ export class MyAccount extends Component {
PropTypes.checkPropTypes({
email: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
// username: PropTypes.string.isRequired,
publicName: PropTypes.string.isRequired,
allowEmails: PropTypes.bool.isRequired,
userDictionaries: PropTypes.array.isRequired,
updateUserData: PropTypes.func,
changeDictionary: PropTypes.func,
}, props, 'prop', 'LoginForm');
}, props, 'prop', 'MyAccount');
this.state = {
email: this.props.email,
@ -32,11 +32,17 @@ export class MyAccount extends Component {
<div className='column'>
<h2 className='title'>Account Details</h2>
<div className='control'>
<strong>Email:</strong> <span>{this.state.email}</span>
</div>
<div className='control'>
<strong>Username:</strong> <span>{this.state.username}</span>
<div className='field'>
<label className='label'>
<span>Email:</span>
</label>
<div className='control'>
<input className='input' type='text' value={this.state.email}
onInput={(event) => { this.setState({ email: event.target.value }) }} />
<div className='help'>
<strong>Note:</strong> If you change your email address, you will need to use your new email address to log in.
</div>
</div>
</div>
<div className='field'>
<label className='label'>
@ -45,6 +51,14 @@ export class MyAccount extends Component {
<div className='control'>
<input className='input' type='text' value={this.state.publicName}
onInput={(event) => {this.setState({publicName: event.target.value})}} />
<div className='help'>
This is the name we greet you with. It's also the name displayed if you ever decide to share
any of your dictionaries.
</div>
<div className='help'>
<strong>Note:</strong> This is <em>not a username</em>, and is therefore not guaranteed to be unique.
Use something people will recognize you as to differentiate from other people who might use the same name!
</div>
</div>
</div>
<div className='field'>
@ -55,12 +69,21 @@ export class MyAccount extends Component {
<label className='label is-unselectable' htmlFor='allowEmails'>
Allow Emails
</label>
<div className='help'>
We'll make sure that you're the first to hear about any new features that get added or if any of our policies
change for any reason. We'll never spam you or sell your information, but you may need to mark emails from
lexicon.ga as not spam to receive them.
</div>
<div className='help'>
<strong>Note:</strong> Password reset emails will be sent regardless of your choice here.
</div>
</div>
</div>
</div>
<div className='column'>
<h2 className='title'>Account Actions</h2>
<div className='field'>
<label className='label is-unselectable'>
<span>Change Dictionary</span>
@ -75,6 +98,47 @@ export class MyAccount extends Component {
</div>
</div>
</div>
<div className='field'>
<label className='label is-unselectable'>
<span>Reset Your Password</span>
</label>
<div className='help'>
Click the button below to reload the page and show the Reset Password form. Filling out this
form will instantly change your password, and you will need to log in using the new password
from that point forward.
</div>
<div className='control'>
<a className='button'>Reset Password</a>
</div>
</div>
<div className='content is-small'>
<h4><strong>Request Your Data</strong></h4>
<p>
Per your <a href='https://www.eugdpr.org/' target='_blank'>GDPR</a> rights in Articles 1315 and 20,
we allow you to request any and all data we have stored about you. The only data we have about
you personally is your email address and your Public Name, if you decided to set one. All other
data (your Dictionary data) is visible and accessible via the Export button under your Dictionary's
Settings. Send an email to help@lexicon.ga to request your information.
</p>
</div>
<div className='content is-small'>
<h4><strong>Delete Your Account</strong></h4>
<p>
Per your <a href='https://www.eugdpr.org/' target='_blank'>GDPR</a> rights in Article 17, if you wish
for your account to be deleted, please contact us at help@lexicon.ga, and we will delete your account
and all associated dictionaries and words as quickly as possible. Note that you can delete dictionaries
yourself via your Dictionary's Settings.
</p>
<p>
Anything that is deleted from our system is permanently and irretrievably removed from our system and
cannot be restored, though search engines or internet archives may retain a cached version of your content
(there is nothing we can do about this, and you will need to seek out removal of that information by directly
contacting the services that are caching your data).
</p>
</div>
</div>
</div>

View File

@ -28,7 +28,6 @@ export class AccountManager extends Component {
username: userData && userData.hasOwnProperty('username') ? userData.username : DEFAULT_USER_DATA.username,
publicName: userData && userData.hasOwnProperty('publicName') ? userData.publicName : DEFAULT_USER_DATA.publicName,
allowEmails: userData && userData.hasOwnProperty('allowEmails') ? userData.allowEmails : DEFAULT_USER_DATA.allowEmails,
itemsPerPage: userData && userData.hasOwnProperty('itemsPerPage') ? userData.itemsPerPage : DEFAULT_USER_DATA.itemsPerPage,
},
userDictionaries: [],
};
@ -119,7 +118,7 @@ export class AccountManager extends Component {
<Modal buttonText='Account' title='My Account' onShow={ this.getDictionaryNames.bind(this) }>
<MyAccount
email={ userData.email }
username={ userData.username }
// username={ userData.username }
publicName={ userData.publicName }
allowEmails={ userData.allowEmails }
userDictionaries={ this.state.userDictionaries }

View File

@ -3,7 +3,7 @@ import { Component } from 'inferno';
import PropTypes from 'prop-types';
import store from 'store';
import { DEFAULT_USER_DATA } from '../../Constants';
import { DEFAULT_PREFERENCES } from '../../Constants';
const phondueUsage = require('../../../vendor/KeyboardFire/phondue/usage.html');
const digraphs = require('../../../vendor/KeyboardFire/phondue/digraphs.json');
@ -48,18 +48,21 @@ export class IPAField extends Component {
get useIPA() {
if (this.props.useIPASetting) {
const userData = store.get('LexicongaUserData');
return userData && userData.hasOwnProperty('useIPAPronunciation')
? userData.useIPAPronunciation : DEFAULT_USER_DATA.useIPAPronunciation;
const preferences = store.get('LexicongaPreferences');
return preferences && preferences.hasOwnProperty('useIPAPronunciation')
? preferences.useIPAPronunciation : DEFAULT_PREFERENCES.useIPAPronunciation;
}
return true;
}
toggleIPA () {
const userData = store.get('LexicongaUserData');
userData.useIPAPronunciation = !this.useIPA;
this.setState({ useIPA: userData.useIPAPronunciation }, () => {
store.set('LexicongaUserData', userData);
let preferences = store.get('LexicongaPreferences');
if (!preferences) {
preferences = Object.assign({}, DEFAULT_PREFERENCES);
}
preferences.useIPAPronunciation = !this.useIPA;
this.setState({ useIPA: preferences.useIPAPronunciation }, () => {
store.set('LexicongaPreferences', preferences);
});
}

View File

@ -6,7 +6,7 @@ import { Component, render } from 'inferno';
import store from 'store';
import removeDiacritics from '../vendor/StackOverflow/removeDiacritics';
import { DEFAULT_USER_DATA } from './Constants';
import { DEFAULT_PREFERENCES } from './Constants';
import { addHelpfulPrototypes, getWordsStats } from './Helpers';
addHelpfulPrototypes();
@ -95,8 +95,8 @@ class App extends Component {
updateDisplayedWords (callback = () => {}) {
dictionary.wordsPromise.then(words => {
const userData = store.get('LexicongaUserData');
const itemsPerPage = userData && userData.hasOwnProperty('itemsPerPage') ? userData.itemsPerPage : DEFAULT_USER_DATA.itemsPerPage;
const preferences = store.get('LexicongaPreferences');
const itemsPerPage = preferences && preferences.hasOwnProperty('itemsPerPage') ? preferences.itemsPerPage : DEFAULT_PREFERENCES.itemsPerPage;
const { searchConfig, partsOfSpeech, currentPage } = this.state;
const partsOfSpeechForFilter = [...partsOfSpeech, 'Uncategorized'];
const pageStart = currentPage * itemsPerPage;