feat(report-user) Gatsby /user/:username/report-user
This commit is contained in:
@ -3,24 +3,19 @@ import debugFactory from 'debug';
|
||||
import { curry, pick } from 'lodash';
|
||||
import { Observable } from 'rx';
|
||||
|
||||
import { homeLocation } from '../../../config/env';
|
||||
import {
|
||||
getProgress,
|
||||
normaliseUserFields,
|
||||
userPropsForSession
|
||||
} from '../utils/publicUserProps';
|
||||
import { fixCompletedChallengeItem } from '../../common/utils';
|
||||
import {
|
||||
ifNoUser401,
|
||||
ifNoUserRedirectTo,
|
||||
ifNotVerifiedRedirectToUpdateEmail
|
||||
} from '../utils/middleware';
|
||||
import { ifNoUser401, ifNoUserRedirectTo } from '../utils/middleware';
|
||||
|
||||
const log = debugFactory('fcc:boot:user');
|
||||
const sendNonUserToHome = ifNoUserRedirectTo('/');
|
||||
const sendNonUserToHomeWithMessage = curry(ifNoUserRedirectTo, 2)('/');
|
||||
const sendNonUserToHome = ifNoUserRedirectTo(homeLocation);
|
||||
|
||||
module.exports = function bootUser(app) {
|
||||
const router = app.loopback.Router();
|
||||
const api = app.loopback.Router();
|
||||
|
||||
api.get('/account', sendNonUserToHome, getAccount);
|
||||
@ -29,21 +24,8 @@ module.exports = function bootUser(app) {
|
||||
|
||||
api.post('/account/delete', ifNoUser401, createPostDeleteAccount(app));
|
||||
api.post('/account/reset-progress', ifNoUser401, postResetProgress);
|
||||
api.post(
|
||||
'/user/:username/report-user/',
|
||||
ifNoUser401,
|
||||
createPostReportUserProfile(app)
|
||||
);
|
||||
api.post('/user/report-user/', ifNoUser401, createPostReportUserProfile(app));
|
||||
|
||||
router.get(
|
||||
'/user/:username/report-user/',
|
||||
sendNonUserToHomeWithMessage('You must be signed in to report a user'),
|
||||
ifNotVerifiedRedirectToUpdateEmail,
|
||||
getReportUserProfile
|
||||
);
|
||||
|
||||
app.use(router);
|
||||
app.use('/external', api);
|
||||
app.use('/internal', api);
|
||||
};
|
||||
|
||||
@ -86,14 +68,6 @@ function readSessionUser(req, res, next) {
|
||||
).subscribe(user => res.json(user), next);
|
||||
}
|
||||
|
||||
function getReportUserProfile(req, res) {
|
||||
const username = req.params.username.toLowerCase();
|
||||
return res.render('account/report-profile', {
|
||||
title: 'Report User',
|
||||
username
|
||||
});
|
||||
}
|
||||
|
||||
function getAccount(req, res) {
|
||||
const { username } = req.user;
|
||||
return res.redirect('/' + username);
|
||||
@ -217,17 +191,19 @@ function createPostReportUserProfile(app) {
|
||||
const { Email } = app.models;
|
||||
return function postReportUserProfile(req, res, next) {
|
||||
const { user } = req;
|
||||
const { username } = req.params;
|
||||
const { username } = req.body;
|
||||
const report = req.sanitize('reportDescription').trimTags();
|
||||
|
||||
if (!username || !report || report === '') {
|
||||
req.flash(
|
||||
'danger',
|
||||
'Oops, something is not right please re-check your submission.'
|
||||
);
|
||||
return next();
|
||||
}
|
||||
log(username);
|
||||
log(report);
|
||||
|
||||
if (!username || !report || report === '') {
|
||||
return res.json({
|
||||
type: 'danger',
|
||||
message:
|
||||
'Oops, something is not right please re-check your submission.'
|
||||
});
|
||||
}
|
||||
return Email.send$(
|
||||
{
|
||||
type: 'email',
|
||||
@ -250,15 +226,14 @@ function createPostReportUserProfile(app) {
|
||||
},
|
||||
err => {
|
||||
if (err) {
|
||||
err.redirectTo = '/' + username;
|
||||
err.redirectTo = `${homeLocation}/${username}`;
|
||||
return next(err);
|
||||
}
|
||||
|
||||
req.flash(
|
||||
'info',
|
||||
`A report was sent to the team with ${user.email} in copy.`
|
||||
);
|
||||
return res.redirect('/');
|
||||
return res.json({
|
||||
typer: 'info',
|
||||
message: `A report was sent to the team with ${user.email} in copy.`
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
@ -1,13 +1,32 @@
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { Link, navigate } from 'gatsby';
|
||||
import { connect } from 'react-redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { isSignedInSelector, userFetchStateSelector } from '../redux';
|
||||
import {
|
||||
Panel,
|
||||
FormControl,
|
||||
FormGroup,
|
||||
ControlLabel,
|
||||
Button,
|
||||
Col
|
||||
} from '@freecodecamp/react-bootstrap';
|
||||
|
||||
import {
|
||||
isSignedInSelector,
|
||||
userFetchStateSelector,
|
||||
userSelector,
|
||||
reportUser
|
||||
} from '../redux';
|
||||
import Layout from '../components/Layout';
|
||||
import { Spacer } from '../components/helpers';
|
||||
import { Spacer, Loader, FullWidthRow } from '../components/helpers';
|
||||
import { runInThisContext } from 'vm';
|
||||
|
||||
const propTypes = {
|
||||
email: PropTypes.string,
|
||||
isSignedIn: PropTypes.bool,
|
||||
reportUser: PropTypes.func.isRequired,
|
||||
userFetchState: PropTypes.shape({
|
||||
pending: PropTypes.bool,
|
||||
comnplete: PropTypes.bool,
|
||||
@ -19,19 +38,142 @@ const propTypes = {
|
||||
const mapStateToProps = createSelector(
|
||||
isSignedInSelector,
|
||||
userFetchStateSelector,
|
||||
(isSignedIn, userFetchState) => ({ isSignedIn, userFetchState })
|
||||
userSelector,
|
||||
(isSignedIn, userFetchState, { email }) => ({
|
||||
isSignedIn,
|
||||
userFetchState,
|
||||
email
|
||||
})
|
||||
);
|
||||
|
||||
function ShowUser() {
|
||||
return (
|
||||
<Layout>
|
||||
<Spacer />
|
||||
<h1>ShowUser</h1>
|
||||
</Layout>
|
||||
);
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ reportUser }, dispatch);
|
||||
|
||||
class ShowUser extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.timer = null;
|
||||
this.state = {
|
||||
textarea: ''
|
||||
};
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const textarea = e.target.value.slice();
|
||||
return this.setState({
|
||||
textarea
|
||||
});
|
||||
}
|
||||
|
||||
handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
const { textarea: reportDescription } = this.state;
|
||||
const { username, reportUser } = this.props;
|
||||
return reportUser({ username, reportDescription });
|
||||
}
|
||||
setNavigationTimer() {
|
||||
if (!this.timer) {
|
||||
this.timer = setTimeout(() => navigate('/signin'), 5000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { username, isSignedIn, userFetchState, email } = this.props;
|
||||
const { pending, complete, errored } = userFetchState;
|
||||
if (pending && !complete) {
|
||||
return (
|
||||
<Layout>
|
||||
<div className='loader-wrapper'>
|
||||
<Loader />
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
if ((complete || errored) && !isSignedIn) {
|
||||
this.setNavigationTimer();
|
||||
return (
|
||||
<Layout>
|
||||
<FullWidthRow>
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Panel bsStyle='info'>
|
||||
<Panel.Heading>
|
||||
<Panel.Title componentClass='h3'>
|
||||
You need to be signed in to report a user
|
||||
</Panel.Title>
|
||||
</Panel.Heading>
|
||||
<Panel.Body className='text-center'>
|
||||
<Spacer />
|
||||
<p>
|
||||
You will be redirected to sign in to freeCodeCamp.org
|
||||
automatically in 5 seconds
|
||||
</p>
|
||||
<p>
|
||||
<Link to='/signin'>
|
||||
Or you can here if you do not want to wait
|
||||
</Link>
|
||||
</p>
|
||||
<Spacer />
|
||||
</Panel.Body>
|
||||
</Panel>
|
||||
</FullWidthRow>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
||||
const { textarea } = this.state;
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<FullWidthRow>
|
||||
<Spacer />
|
||||
<Spacer />
|
||||
<Col md={8} mdOffset={2}>
|
||||
<h2>
|
||||
Do you want to report {username}
|
||||
's profile for abuse?
|
||||
</h2>
|
||||
<p>
|
||||
We will notify the community moderators' team, and a send copy of
|
||||
this report to your email:{' '}
|
||||
<span className='green-text'>{email}</span>.
|
||||
</p>
|
||||
<p>We may get back to you for more information, if required.</p>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<FormGroup controlId='report-user-textarea'>
|
||||
<ControlLabel>Additional Information</ControlLabel>
|
||||
<FormControl
|
||||
componentClass='textarea'
|
||||
onChange={this.handleChange}
|
||||
placeholder=''
|
||||
value={textarea}
|
||||
/>
|
||||
</FormGroup>
|
||||
<Button block={true} bsStyle='primary' type='submit'>
|
||||
Sumbit the report
|
||||
</Button>
|
||||
</form>
|
||||
</Col>
|
||||
</FullWidthRow>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ShowUser.displayName = 'ShowUser';
|
||||
ShowUser.propTypes = propTypes;
|
||||
|
||||
export default connect(mapStateToProps)(ShowUser);
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(ShowUser);
|
||||
|
@ -19,3 +19,7 @@ h6 {
|
||||
.text-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.green-text {
|
||||
color: #006400;
|
||||
}
|
@ -4,6 +4,7 @@ import { createTypes, createAsyncTypes } from '../utils/createTypes';
|
||||
import { createFetchUserSaga } from './fetch-user-saga';
|
||||
import { createAcceptTermsSaga } from './accept-terms-saga';
|
||||
import { createAppMountSaga } from './app-mount-saga';
|
||||
import { createReportUserSaga } from './report-user-saga';
|
||||
import { createShowCertSaga } from './show-cert-saga';
|
||||
import { createUpdateMyEmailSaga } from './update-email-saga';
|
||||
|
||||
@ -34,7 +35,8 @@ const types = createTypes(
|
||||
...createAsyncTypes('fetchUser'),
|
||||
...createAsyncTypes('acceptTerms'),
|
||||
...createAsyncTypes('showCert'),
|
||||
...createAsyncTypes('updateMyEmail')
|
||||
...createAsyncTypes('updateMyEmail'),
|
||||
...createAsyncTypes('reportUser')
|
||||
],
|
||||
ns
|
||||
);
|
||||
@ -44,7 +46,8 @@ export const sagas = [
|
||||
...createAppMountSaga(types),
|
||||
...createFetchUserSaga(types),
|
||||
...createUpdateMyEmailSaga(types),
|
||||
...createShowCertSaga(types)
|
||||
...createShowCertSaga(types),
|
||||
...createReportUserSaga(types)
|
||||
];
|
||||
|
||||
export const appMount = createAction(types.appMount);
|
||||
@ -57,6 +60,10 @@ export const fetchUser = createAction(types.fetchUser);
|
||||
export const fetchUserComplete = createAction(types.fetchUserComplete);
|
||||
export const fetchUserError = createAction(types.fetchUserError);
|
||||
|
||||
export const reportUser = createAction(types.reportUser);
|
||||
export const reportUserComplete = createAction(types.reportUserComplete);
|
||||
export const reportUserError = createAction(types.reportUserError);
|
||||
|
||||
export const showCert = createAction(types.showCert);
|
||||
export const showCertComplete = createAction(types.showCertComplete);
|
||||
export const showCertError = createAction(types.showCertError);
|
||||
|
29
client/src/redux/report-user-saga.js
Normal file
29
client/src/redux/report-user-saga.js
Normal file
@ -0,0 +1,29 @@
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
import { navigate } from 'gatsby';
|
||||
|
||||
import { reportUserComplete, reportUserError } from './';
|
||||
import { createFlashMessage } from '../components/Flash/redux';
|
||||
|
||||
import { postReportUser } from '../utils/ajax';
|
||||
|
||||
function* reportUserSaga({ payload }) {
|
||||
try {
|
||||
const { data: response } = yield call(postReportUser, payload);
|
||||
|
||||
yield put(reportUserComplete());
|
||||
yield put(createFlashMessage(response));
|
||||
} catch (e) {
|
||||
yield put(reportUserError(e));
|
||||
}
|
||||
}
|
||||
|
||||
function* acceptCompleteSaga() {
|
||||
yield call(navigate, '/');
|
||||
}
|
||||
|
||||
export function createReportUserSaga(types) {
|
||||
return [
|
||||
takeEvery(types.reportUser, reportUserSaga),
|
||||
takeEvery(types.reportUserComplete, acceptCompleteSaga)
|
||||
];
|
||||
}
|
@ -6,9 +6,9 @@ function get(path) {
|
||||
return axios.get(`${base}${path}`);
|
||||
}
|
||||
|
||||
// function post(path, body) {
|
||||
// return axios.post(`${base}${path}`, body);
|
||||
// }
|
||||
function post(path, body) {
|
||||
return axios.post(`${base}${path}`, body);
|
||||
}
|
||||
|
||||
function put(path, body) {
|
||||
return axios.put(`${base}${path}`, body);
|
||||
@ -30,6 +30,10 @@ export function getShowCert(username, cert) {
|
||||
|
||||
/** POST **/
|
||||
|
||||
export function postReportUser(body) {
|
||||
return post('/user/report-user', body);
|
||||
}
|
||||
|
||||
/** PUT **/
|
||||
|
||||
export function putUserAcceptsTerms(quincyEmails) {
|
||||
|
Reference in New Issue
Block a user