feat(about): Submit new 'about' settings
This commit is contained in:
@ -1,8 +1,12 @@
|
||||
import debug from 'debug';
|
||||
import { check } from 'express-validator/check';
|
||||
|
||||
import { ifNoUser401, createValidatorErrorHandler } from '../utils/middleware';
|
||||
import { themes } from '../../common/utils/themes.js';
|
||||
import { alertTypes } from '../../common/utils/flash.js';
|
||||
|
||||
const log = debug('fcc:boot:settings');
|
||||
|
||||
export default function settingsController(app) {
|
||||
const api = app.loopback.Router();
|
||||
|
||||
@ -47,6 +51,7 @@ export default function settingsController(app) {
|
||||
createValidatorErrorHandler(alertTypes.danger),
|
||||
updateMyTheme
|
||||
);
|
||||
api.put('/update-my-about', ifNoUser401, updateMyAbout);
|
||||
api.put('/update-my-username', ifNoUser401, updateMyUsername);
|
||||
|
||||
app.use('/internal', api);
|
||||
@ -59,6 +64,11 @@ const standardErrorMessage = {
|
||||
'Something went wrong updating your account. Please check and try again'
|
||||
};
|
||||
|
||||
const standardSuccessMessage = {
|
||||
type: 'success',
|
||||
message: 'We have updated your preferences'
|
||||
};
|
||||
|
||||
const toggleUserFlag = (flag, req, res, next) => {
|
||||
const { user } = req;
|
||||
const currentValue = user[flag];
|
||||
@ -180,6 +190,21 @@ function updateMyProjects(req, res, next) {
|
||||
.subscribe(message => res.json({ message }), next);
|
||||
}
|
||||
|
||||
function updateMyAbout(req, res, next) {
|
||||
const {
|
||||
user,
|
||||
body: { name, location, about, picture }
|
||||
} = req;
|
||||
log(name, location, picture, about)
|
||||
return user.updateAttributes({ name, location, about, picture }, err => {
|
||||
if (err) {
|
||||
res.status(500).json(standardErrorMessage);
|
||||
return next(err);
|
||||
}
|
||||
return res.status(200).json(standardSuccessMessage);
|
||||
});
|
||||
}
|
||||
|
||||
function createUpdateMyUsername(app) {
|
||||
const { User } = app.models;
|
||||
return async function updateMyUsername(req, res, next) {
|
||||
|
@ -1,25 +1,24 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
Nav,
|
||||
NavItem,
|
||||
FormGroup,
|
||||
ControlLabel,
|
||||
FormControl
|
||||
} from '@freecodecamp/react-bootstrap';
|
||||
|
||||
import { submitNewAbout } from '../../redux/settings';
|
||||
|
||||
import { FullWidthRow, Spacer } from '../helpers';
|
||||
import ThemeSettings from './Theme';
|
||||
import Camper from './Camper';
|
||||
import UsernameSettings from './Username';
|
||||
import BlockSaveButton from '../helpers/form/BlockSaveButton';
|
||||
import BlockSaveWrapper from '../helpers/form/BlockSaveWrapper';
|
||||
|
||||
const mapStateToProps = () => ({});
|
||||
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({}, dispatch);
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ submitNewAbout }, dispatch);
|
||||
|
||||
const propTypes = {
|
||||
about: PropTypes.string,
|
||||
@ -28,6 +27,7 @@ const propTypes = {
|
||||
name: PropTypes.string,
|
||||
picture: PropTypes.string,
|
||||
points: PropTypes.number,
|
||||
submitNewAbout: PropTypes.func.isRequired,
|
||||
username: PropTypes.string
|
||||
};
|
||||
|
||||
@ -36,33 +36,58 @@ class AboutSettings extends Component {
|
||||
super(props);
|
||||
|
||||
const { name = '', location = '', picture = '', about = '' } = props;
|
||||
|
||||
const values = {
|
||||
name,
|
||||
location,
|
||||
picture,
|
||||
about
|
||||
};
|
||||
this.state = {
|
||||
view: 'edit',
|
||||
formValues: {
|
||||
formValues: { ...values },
|
||||
originalValues: { ...values },
|
||||
formClicked: false
|
||||
};
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { name, location, picture, about } = this.props;
|
||||
const { formValues, formClicked } = this.state;
|
||||
if (
|
||||
formClicked &&
|
||||
name === formValues.name &&
|
||||
location === formValues.location &&
|
||||
picture === formValues.picture &&
|
||||
about === formValues.about
|
||||
) {
|
||||
/* eslint-disable-next-line react/no-did-update-set-state */
|
||||
return this.setState({
|
||||
originalValues: {
|
||||
name,
|
||||
location,
|
||||
picture,
|
||||
about
|
||||
},
|
||||
isFormPristine: true
|
||||
};
|
||||
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleTabSelect = this.handleTabSelect.bind(this);
|
||||
this.renderEdit = this.renderEdit.bind(this);
|
||||
this.renderPreview = this.renderPreview.bind(this);
|
||||
this.show = {
|
||||
edit: this.renderEdit,
|
||||
preview: this.renderPreview
|
||||
};
|
||||
formClicked: false
|
||||
});
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
isFormPristine = () => {
|
||||
const { formValues, originalValues } = this.state;
|
||||
return Object.keys(originalValues)
|
||||
.map(key => originalValues[key] === formValues[key])
|
||||
.every(bool => bool);
|
||||
};
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
const { formValues } = this.state;
|
||||
console.log(formValues)
|
||||
}
|
||||
const { submitNewAbout } = this.props;
|
||||
return this.setState({ formClicked: true }, () =>
|
||||
submitNewAbout(formValues)
|
||||
);
|
||||
};
|
||||
|
||||
handleNameChange = e => {
|
||||
const value = e.target.value.slice(0);
|
||||
@ -104,19 +129,18 @@ class AboutSettings extends Component {
|
||||
}));
|
||||
};
|
||||
|
||||
handleTabSelect(key) {
|
||||
return this.setState(state => ({
|
||||
...state,
|
||||
view: key
|
||||
}));
|
||||
}
|
||||
|
||||
renderEdit() {
|
||||
render() {
|
||||
const {
|
||||
formValues: { name, location, picture, about }
|
||||
} = this.state;
|
||||
const { currentTheme, username } = this.props;
|
||||
const toggleTheme = () => {};
|
||||
return (
|
||||
<Fragment>
|
||||
<div className='about-settings'>
|
||||
<UsernameSettings username={username} />
|
||||
<br />
|
||||
<FullWidthRow>
|
||||
<form id='camper-identity' onSubmit={this.handleSubmit}>
|
||||
<FormGroup controlId='about-name'>
|
||||
<ControlLabel>
|
||||
<strong>Name</strong>
|
||||
@ -158,52 +182,7 @@ class AboutSettings extends Component {
|
||||
value={about}
|
||||
/>
|
||||
</FormGroup>
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
renderPreview() {
|
||||
const { about, picture, points, username, name, location } = this.props;
|
||||
return (
|
||||
<Camper
|
||||
about={about}
|
||||
location={location}
|
||||
name={name}
|
||||
picture={picture}
|
||||
points={points}
|
||||
username={username}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { currentTheme, username } = this.props;
|
||||
const { view, isFormPristine } = this.state;
|
||||
|
||||
const toggleTheme = () => {};
|
||||
return (
|
||||
<div className='about-settings'>
|
||||
<UsernameSettings username={username} />
|
||||
<FullWidthRow>
|
||||
<Nav
|
||||
activeKey={view}
|
||||
bsStyle='tabs'
|
||||
className='edit-preview-tabs'
|
||||
onSelect={k => this.handleTabSelect(k)}
|
||||
>
|
||||
<NavItem eventKey='edit' title='Edit Bio'>
|
||||
Edit Bio
|
||||
</NavItem>
|
||||
<NavItem eventKey='preview' title='Preview Bio'>
|
||||
Preview Bio
|
||||
</NavItem>
|
||||
</Nav>
|
||||
</FullWidthRow>
|
||||
<br />
|
||||
<FullWidthRow>
|
||||
<form id='camper-identity' onSubmit={this.handleSubmit}>
|
||||
{this.show[view]()}
|
||||
<BlockSaveButton disabled={isFormPristine} />
|
||||
<BlockSaveButton disabled={this.isFormPristine()} />
|
||||
</form>
|
||||
</FullWidthRow>
|
||||
<Spacer />
|
||||
|
@ -1,9 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
function CamperSettings() {
|
||||
return (<h1>CamperSettings</h1>);
|
||||
}
|
||||
|
||||
CamperSettings.displayName = 'CamperSettings';
|
||||
|
||||
export default CamperSettings;
|
@ -160,6 +160,17 @@ export const reducer = handleActions(
|
||||
}
|
||||
}
|
||||
}
|
||||
: state,
|
||||
[settingsTypes.submitNewAboutComplete]: (state, {payload}) => payload ? {
|
||||
...state,
|
||||
user: {
|
||||
...state.user,
|
||||
[state.appUsername]: {
|
||||
...state.user[state.appUsername],
|
||||
...payload
|
||||
}
|
||||
}
|
||||
}
|
||||
: state
|
||||
},
|
||||
initialState
|
||||
|
@ -22,6 +22,7 @@ const initialState = {
|
||||
export const types = createTypes(
|
||||
[
|
||||
...createAsyncTypes('validateUsername'),
|
||||
...createAsyncTypes('submitNewAbout'),
|
||||
...createAsyncTypes('submitNewUsername')
|
||||
],
|
||||
ns
|
||||
@ -29,6 +30,13 @@ export const types = createTypes(
|
||||
|
||||
export const sagas = [...createSettingsSagas(types)];
|
||||
|
||||
export const submitNewAbout = createAction(types.submitNewAbout);
|
||||
export const submitNewAboutComplete = createAction(
|
||||
types.submitNewAboutComplete,
|
||||
({ type, payload }) => (type === 'success' ? payload : null)
|
||||
);
|
||||
export const submitNewAboutError = createAction(types.submitNewAboutError);
|
||||
|
||||
export const submitNewUsername = createAction(types.submitNewUsername);
|
||||
export const submitNewUsernameComplete = createAction(
|
||||
types.submitNewUsernameComplete,
|
||||
|
@ -4,13 +4,38 @@ import { call, put, takeLatest } from 'redux-saga/effects';
|
||||
import {
|
||||
validateUsernameComplete,
|
||||
validateUsernameError,
|
||||
submitNewAboutComplete,
|
||||
submitNewAboutError,
|
||||
submitNewUsernameComplete,
|
||||
submitNewUsernameError
|
||||
} from './';
|
||||
|
||||
import { getUsernameExists, putUpdateMyUsername } from '../../utils/ajax';
|
||||
import {
|
||||
getUsernameExists,
|
||||
putUpdateMyAbout,
|
||||
putUpdateMyUsername
|
||||
} from '../../utils/ajax';
|
||||
import { createFlashMessage } from '../../components/Flash/redux';
|
||||
|
||||
function* submitNewAboutSaga({ payload }) {
|
||||
try {
|
||||
const { data: response } = yield call(putUpdateMyAbout, payload);
|
||||
yield put(submitNewAboutComplete({ ...response, payload }));
|
||||
yield put(createFlashMessage(response));
|
||||
} catch (e) {
|
||||
yield put(submitNewAboutError(e));
|
||||
}
|
||||
}
|
||||
|
||||
function* submitNewUsernameSaga({ payload: username }) {
|
||||
try {
|
||||
const { data: response } = yield call(putUpdateMyUsername, username);
|
||||
yield put(submitNewUsernameComplete({ ...response, username }));
|
||||
yield put(createFlashMessage(response));
|
||||
} catch (e) {
|
||||
yield put(submitNewUsernameError(e));
|
||||
}
|
||||
}
|
||||
|
||||
function* validateUsernameSaga({ payload }) {
|
||||
try {
|
||||
yield delay(500);
|
||||
@ -23,19 +48,10 @@ function* validateUsernameSaga({ payload }) {
|
||||
}
|
||||
}
|
||||
|
||||
function* submitNEwUswernameSaga({ payload: username }) {
|
||||
try {
|
||||
const { data: response } = yield call(putUpdateMyUsername, username);
|
||||
yield put(submitNewUsernameComplete({...response, username}));
|
||||
yield put(createFlashMessage(response));
|
||||
} catch (e) {
|
||||
yield put(submitNewUsernameError(e));
|
||||
}
|
||||
}
|
||||
|
||||
export function createSettingsSagas(types) {
|
||||
return [
|
||||
takeLatest(types.validateUsername, validateUsernameSaga),
|
||||
takeLatest(types.submitNewUsername, submitNEwUswernameSaga)
|
||||
takeLatest(types.submitNewAbout, submitNewAboutSaga),
|
||||
takeLatest(types.submitNewUsername, submitNewUsernameSaga),
|
||||
takeLatest(types.validateUsername, validateUsernameSaga)
|
||||
];
|
||||
}
|
||||
|
@ -40,6 +40,10 @@ export function postReportUser(body) {
|
||||
|
||||
/** PUT **/
|
||||
|
||||
export function putUpdateMyAbout(values) {
|
||||
return put('/update-my-about', { ...values });
|
||||
}
|
||||
|
||||
export function putUpdateMyUsername(username) {
|
||||
return put('/update-my-username', { username });
|
||||
}
|
||||
|
Reference in New Issue
Block a user