Skip to content

Commit 64a590b

Browse files
authored
Merge pull request #89 from ozair-dev/dev
Fix(#87): Add Some Basic Validations For Better UX
2 parents 4c15c83 + 4e6cd79 commit 64a590b

File tree

14 files changed

+392
-289
lines changed

14 files changed

+392
-289
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"@testing-library/react": "^12.1.2",
1313
"@testing-library/user-event": "^13.5.0",
1414
"axios": "^0.24.0",
15+
"bad-words": "^3.0.4",
1516
"feather-icons": "^4.28.0",
1617
"moment": "^2.29.4",
1718
"prop-types": "^15.8.0",

src/components/LayoutWrapper/RightSideBar/SideBarWidget/SideBarWidget.component.jsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import React, {Fragment} from 'react';
2-
import {Link} from 'react-router-dom';
32

43
import { SideBarWidgetData } from "./SideBarWidgetData";
54

@@ -43,11 +42,11 @@ const WidgetItem = ({ icon, title, link }) => (
4342
{icon}
4443
</div>
4544
<div className="flex--item wmn0 ow-break-word">
46-
<Link
47-
to={link}
45+
<a
46+
href={link}
4847
className="js-gps-track"
4948
data-ga={`[&quot;community bulletin board&quot;,&quot;The Overflow Blog&quot;,&quot;${link}&quot;,null,null]`}
50-
data-gps-track="communitybulletin.click({ priority: 1, position: 0 })">{title}</Link>
49+
data-gps-track="communitybulletin.click({ priority: 1, position: 0 })">{title}</a>
5150
</div>
5251
</li>
5352
)

src/components/PostItem/PostItem.component.jsx

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
import React from 'react';
2-
import {connect} from 'react-redux';
3-
import PropTypes from 'prop-types';
4-
import {Link} from 'react-router-dom';
1+
import React from "react";
2+
import { connect } from "react-redux";
3+
import PropTypes from "prop-types";
4+
import { Link } from "react-router-dom";
55

6-
import htmlSubstring from '../../services/htmlSubstring'
7-
import injectEllipsis from '../../services/injectEllipsis'
6+
import censorBadWords from "../../services/censorBadWords";
87

9-
import UserCard from '../UserCard/UserCard.component';
10-
import TagBadge from '../TagBadge/TagBadge.component';
8+
import htmlSubstring from "../../services/htmlSubstring";
9+
import injectEllipsis from "../../services/injectEllipsis";
1110

12-
import './PostItem.styles.scss';
11+
import UserCard from "../UserCard/UserCard.component";
12+
import TagBadge from "../TagBadge/TagBadge.component";
13+
14+
import "./PostItem.styles.scss";
1315

1416
const PostItem = ({
1517
post: {
@@ -27,54 +29,59 @@ const PostItem = ({
2729
},
2830
}) => {
2931
const answerVoteUp = (
30-
<div className='vote answer'>
31-
<span className='vote-count'>{answer_count}</span>
32-
<div className='count-text'>answers</div>
32+
<div className="vote answer">
33+
<span className="vote-count">{answer_count}</span>
34+
<div className="count-text">answers</div>
3335
</div>
3436
);
3537

3638
const answerVoteDown = (
37-
<div className='vote'>
38-
<span className='vote-count'>{answer_count}</span>
39-
<div className='count-text'>answers</div>
39+
<div className="vote">
40+
<span className="vote-count">{answer_count}</span>
41+
<div className="count-text">answers</div>
4042
</div>
4143
);
4244

4345
return (
44-
<div className='posts'>
45-
<div className='stats-container fc-black-500'>
46-
<div className='stats'>
47-
<div className='vote'>
48-
<span className='vote-count'>{comment_count}</span>
49-
<div className='count-text'>comments</div>
46+
<div className="posts">
47+
<div className="stats-container fc-black-500">
48+
<div className="stats">
49+
<div className="vote">
50+
<span className="vote-count">{comment_count}</span>
51+
<div className="count-text">comments</div>
5052
</div>
5153
{answer_count > 0 ? answerVoteUp : answerVoteDown}
52-
<div className='vote'>
53-
<span className='vote-count'>{tags.length}</span>
54-
<div className='count-text'>tags</div>
54+
<div className="vote">
55+
<span className="vote-count">{tags.length}</span>
56+
<div className="count-text">tags</div>
5557
</div>
56-
<div className='vote'>
57-
<div className='count-text'>{views} views</div>
58+
<div className="vote">
59+
<div className="count-text">{views} views</div>
5860
</div>
5961
</div>
6062
</div>
61-
<div className='summary'>
63+
<div className="summary">
6264
<h3>
63-
<Link to={`/questions/${id}`}>{title}</Link>
65+
<Link to={`/questions/${id}`}>{censorBadWords(title)}</Link>
6466
</h3>
65-
<div className='brief' dangerouslySetInnerHTML={{__html: injectEllipsis(htmlSubstring(body, 200))}}></div>
67+
<div
68+
className="brief"
69+
dangerouslySetInnerHTML={{
70+
__html: injectEllipsis(censorBadWords(htmlSubstring(body, 200))),
71+
}}
72+
></div>
6673
<div className="profile-tags">
67-
{tags.map((tag, index) => (
68-
<TagBadge key={index} tag_name={tag.tagname} size={'s-tag'} />
69-
))}
74+
{tags.map((tag, index) => (
75+
<TagBadge key={index} tag_name={tag.tagname} size={"s-tag"} />
76+
))}
7077
</div>
7178
<UserCard
7279
created_at={created_at}
7380
user_id={user_id}
7481
gravatar={gravatar}
7582
username={username}
76-
float={'right'}
77-
backgroundColor={'transparent'}
83+
float={"right"}
84+
backgroundColor={"transparent"}
7885
/>
7986
</div>
8087
</div>

src/pages/AllUsersPage/AllUsersPage.component.jsx

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,80 @@
1-
import React, {Fragment, useEffect, useState} from 'react';
2-
import {connect} from 'react-redux';
3-
import PropTypes from 'prop-types';
4-
import {getUsers} from '../../redux/users/users.actions';
5-
import handleSorting from '../../services/handleSorting';
1+
import React, { Fragment, useEffect, useState } from "react";
2+
import { connect } from "react-redux";
3+
import PropTypes from "prop-types";
4+
import { getUsers } from "../../redux/users/users.actions";
5+
import handleSorting from "../../services/handleSorting";
66

7-
import UserPanel from './UserPanel/UserPanel.component';
8-
import Spinner from '../../components/Spinner/Spinner.component';
9-
import SearchBox from '../../components/SearchBox/SearchBox.component';
10-
import ButtonGroup from '../../components/ButtonGroup/ButtonGroup.component';
7+
import UserPanel from "./UserPanel/UserPanel.component";
8+
import Spinner from "../../components/Spinner/Spinner.component";
9+
import SearchBox from "../../components/SearchBox/SearchBox.component";
10+
import ButtonGroup from "../../components/ButtonGroup/ButtonGroup.component";
1111
import Pagination from "../../components/Pagination/Pagination.component";
1212

13-
import './AllUsersPage.styles.scss';
13+
import "./AllUsersPage.styles.scss";
1414

15-
const itemsPerPage = 16;
15+
const itemsPerPage = 18;
1616

17-
const AllUsersPage = ({getUsers, user: {users, loading}}) => {
17+
const AllUsersPage = ({ getUsers, user: { users, loading } }) => {
1818
useEffect(() => {
1919
getUsers();
2020
}, [getUsers]);
2121

2222
const [page, setPage] = useState(1);
23-
const [fetchSearch, setSearch] = useState('');
24-
const [sortType, setSortType] = useState('Popular');
23+
const [fetchSearch, setSearch] = useState("");
24+
const [sortType, setSortType] = useState("Popular");
2525

2626
const handlePaginationChange = (e, value) => setPage(value);
2727

2828
const handleChange = (e) => {
2929
e.preventDefault();
3030
setSearch(e.target.value);
31-
setPage(1)
31+
setPage(1);
3232
};
3333

3434
return loading || users === null ? (
35-
<Spinner type='page' width='75px' height='200px' />
35+
<Spinner type="page" width="75px" height="200px" />
3636
) : (
3737
<Fragment>
38-
<div id='mainbar' className='users-page fc-black-800'>
39-
<h1 className='headline'>Users</h1>
40-
<div className='headline-count'>
38+
<div id="mainbar" className="users-page fc-black-800">
39+
<h1 className="headline">Users</h1>
40+
<div className="headline-count">
4141
<span>
42-
{new Intl.NumberFormat('en-IN').format(users.length)} users
42+
{new Intl.NumberFormat("en-IN").format(users.length)} users
4343
</span>
4444
</div>
45-
<div className='users-box pl16 pr16 pb16'>
45+
<div className="users-box pl16 pr16 pb16">
4646
<SearchBox
47-
placeholder={'filter by user'}
47+
placeholder={"filter by user"}
4848
handleChange={handleChange}
49-
width={'200px'}
49+
width={"200px"}
5050
/>
5151
<ButtonGroup
52-
buttons={['Popular', 'Name', 'Active', 'New Users']}
52+
buttons={["Popular", "Name", "Active", "New Users"]}
5353
selected={sortType}
5454
setSelected={setSortType}
5555
/>
5656
</div>
57-
<div className='user-browser'>
58-
<div className='grid-layout'>
57+
<div className="user-browser">
58+
<div className="grid-layout">
5959
{users
6060
.filter((user) =>
6161
user.username.toLowerCase().includes(fetchSearch.toLowerCase())
6262
)
63-
?.sort(handleSorting(sortType, 'users'))
64-
.slice((page - 1) * itemsPerPage, (page - 1) * itemsPerPage + itemsPerPage)
63+
?.sort(handleSorting(sortType, "users"))
64+
.slice(
65+
(page - 1) * itemsPerPage,
66+
(page - 1) * itemsPerPage + itemsPerPage
67+
)
6568
.map((user, index) => (
6669
<UserPanel key={index} user={user} />
6770
))}
6871
</div>
6972
</div>
7073
<Pagination
7174
page={page}
72-
itemList={users.filter((user) => user.username.toLowerCase().includes(fetchSearch.toLowerCase()))}
75+
itemList={users.filter((user) =>
76+
user.username.toLowerCase().includes(fetchSearch.toLowerCase())
77+
)}
7378
itemsPerPage={itemsPerPage}
7479
handlePaginationChange={handlePaginationChange}
7580
/>

src/pages/Post/AnswerSection/AnswerForm/AnswerForm.component.jsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {connect} from 'react-redux';
33
import PropTypes from 'prop-types';
44
import {addAnswer} from '../../../../redux/answers/answers.actions';
55

6+
67
import LinkButton from '../../../../components/LinkButton/LinkButton.component';
78
import MarkdownEditor from '../../../../components/MarkdownEditor/MarkdownEditor.component';
89

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,71 @@
1-
import React, {Fragment} from 'react';
2-
import {connect} from 'react-redux';
3-
import PropTypes from 'prop-types';
4-
import {Link} from 'react-router-dom';
5-
import {deleteAnswer} from '../../../../redux/answers/answers.actions';
1+
import React, { Fragment } from "react";
2+
import { connect } from "react-redux";
3+
import PropTypes from "prop-types";
4+
import { Link } from "react-router-dom";
5+
import { deleteAnswer } from "../../../../redux/answers/answers.actions";
66

7-
import {ReactComponent as UpVote} from '../../../../assets/ArrowUpLg.svg';
8-
import {ReactComponent as DownVote} from '../../../../assets/ArrowDownLg.svg';
9-
import UserCard from '../../../../components/UserCard/UserCard.component';
7+
import { ReactComponent as UpVote } from "../../../../assets/ArrowUpLg.svg";
8+
import { ReactComponent as DownVote } from "../../../../assets/ArrowDownLg.svg";
9+
import UserCard from "../../../../components/UserCard/UserCard.component";
1010

11-
import './AnswerItem.styles.scss';
11+
import "./AnswerItem.styles.scss";
12+
import censorBadWords from "../../../../services/censorBadWords";
1213

1314
const AnswerItem = ({
1415
deleteAnswer,
15-
answer: {body, user_id, gravatar, id, created_at, username},
16-
post: {post},
16+
answer: { body, user_id, gravatar, id, created_at, username },
17+
post: { post },
1718
auth,
1819
}) => {
1920
return (
2021
<Fragment>
21-
<div className='answer-layout'>
22-
<div className='vote-cell'>
23-
<div className='vote-container'>
22+
<div className="answer-layout">
23+
<div className="vote-cell">
24+
<div className="vote-container">
2425
<button
25-
className='vote-up'
26-
title='This answer is useful (click again to undo)'
26+
className="vote-up"
27+
title="This answer is useful (click again to undo)"
2728
>
28-
<UpVote className='icon' />
29+
<UpVote className="icon" />
2930
</button>
30-
<div className='vote-count fc-black-500'>0</div>
31+
<div className="vote-count fc-black-500">0</div>
3132
<button
32-
className='vote-down'
33-
title='This answer is not useful (click again to undo)'
33+
className="vote-down"
34+
title="This answer is not useful (click again to undo)"
3435
>
35-
<DownVote className='icon' />
36+
<DownVote className="icon" />
3637
</button>
3738
</div>
3839
</div>
39-
<div className='answer-item'>
40-
<div className='answer-content fc-black-800' dangerouslySetInnerHTML={{__html: body}}>
41-
</div>
42-
<div className='answer-actions'>
43-
<div className='action-btns'>
44-
<div className='answer-menu'>
40+
<div className="answer-item">
41+
<div
42+
className="answer-content fc-black-800"
43+
dangerouslySetInnerHTML={{ __html: censorBadWords(body) }}
44+
></div>
45+
<div className="answer-actions">
46+
<div className="action-btns">
47+
<div className="answer-menu">
4548
<Link
46-
className='answer-links'
47-
title='short permalink to this question'
48-
to='/'
49+
className="answer-links"
50+
title="short permalink to this question"
51+
to="/"
4952
>
5053
share
5154
</Link>
5255
<Link
53-
className='answer-links'
54-
title='Follow this question to receive notifications'
55-
to='/'
56+
className="answer-links"
57+
title="Follow this question to receive notifications"
58+
to="/"
5659
>
5760
follow
5861
</Link>
5962
{!auth.loading &&
6063
auth.isAuthenticated &&
6164
user_id === auth.user.id && (
6265
<Link
63-
className='s-link s-link__danger'
64-
style={{paddingLeft: '4px'}}
65-
title='Delete the answer'
66+
className="s-link s-link__danger"
67+
style={{ paddingLeft: "4px" }}
68+
title="Delete the answer"
6669
onClick={(e) => deleteAnswer(id)}
6770
to={`/questions/${post.id}`}
6871
>
@@ -76,8 +79,8 @@ const AnswerItem = ({
7679
user_id={user_id}
7780
gravatar={gravatar}
7881
username={username}
79-
dateType={'answered'}
80-
backgroundColor={'transparent'}
82+
dateType={"answered"}
83+
backgroundColor={"transparent"}
8184
/>
8285
</div>
8386
</div>
@@ -98,4 +101,4 @@ const mapStateToProps = (state) => ({
98101
post: state.post,
99102
});
100103

101-
export default connect(mapStateToProps, {deleteAnswer})(AnswerItem);
104+
export default connect(mapStateToProps, { deleteAnswer })(AnswerItem);

0 commit comments

Comments
 (0)