Add ability to ban users for limited time and to view in the 'Show blocked' (#140)
closes #88
This commit is contained in:
committed by
Aleksei Gurianov
parent
ce8c2c236f
commit
4cea9d5855
@@ -100,9 +100,9 @@ export const removeComment = ({ id }) =>
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
export const blockUser = ({ id }) =>
|
||||
export const blockUser = ({ id, ttl }) =>
|
||||
fetcher.put({
|
||||
url: `/admin/user/${id}?block=1`,
|
||||
url: ttl === 'permanently' ? `/admin/user/${id}?block=1` : `/admin/user/${id}?block=1&ttl=${ttl}`,
|
||||
withCredentials: true,
|
||||
});
|
||||
|
||||
|
||||
@@ -18,6 +18,25 @@ const PROVIDER_NAMES = {
|
||||
const LS_COLLAPSE_KEY = '__remarkCollapsed';
|
||||
const LS_SORT_KEY = '__remarkSort';
|
||||
|
||||
const BLOCKING_DURATIONS = [
|
||||
{
|
||||
label: 'Permanently',
|
||||
value: 'permanently',
|
||||
},
|
||||
{
|
||||
label: 'For a month',
|
||||
value: `${30 * 60 * 24}m`,
|
||||
},
|
||||
{
|
||||
label: 'For a week',
|
||||
value: `${7 * 60 * 24}m`,
|
||||
},
|
||||
{
|
||||
label: 'For a day',
|
||||
value: `${60 * 24}m`,
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = {
|
||||
BASE_URL,
|
||||
API_BASE,
|
||||
@@ -32,4 +51,5 @@ module.exports = {
|
||||
DEFAULT_SORT,
|
||||
LS_COLLAPSE_KEY,
|
||||
LS_SORT_KEY,
|
||||
BLOCKING_DURATIONS,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.blocked-users__user-id {
|
||||
color: #888;
|
||||
}
|
||||
@@ -51,6 +51,7 @@ export default class BlockedUsers extends Component {
|
||||
<li className={b('blocked-users__list-item', {}, { view: isUserUnblocked ? 'invisible' : null })}>
|
||||
<span className="blocked-users__username">{user.name}</span>{' '}
|
||||
<span className="blocked-users__user-id">({user.id})</span>
|
||||
<span className="blocked-users__user-block-ttl"> until {formatTime(new Date(user.time))}</span>
|
||||
{isUserUnblocked && (
|
||||
<span {...getHandleClickProps(() => this.block(user))} className="blocked-users__action">
|
||||
block
|
||||
@@ -70,3 +71,14 @@ export default class BlockedUsers extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
// 'ru-RU' adds a dot as a separator
|
||||
const date = time.toLocaleDateString(['ru-RU'], { day: '2-digit', month: '2-digit', year: '2-digit' });
|
||||
|
||||
// do it manually because Intl API doesn't add leading zeros to hours; idk why
|
||||
const hours = `0${time.getHours()}`.slice(-2);
|
||||
const mins = `0${time.getMinutes()}`.slice(-2);
|
||||
|
||||
return `${date} at ${hours}:${mins}`;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ require('./__action/blocked-users__action.scss');
|
||||
require('./__list/blocked-users__list.scss');
|
||||
|
||||
require('./__list-item/blocked-users__list-item.scss');
|
||||
require('./__user-block-ttl/blocked-users__user-block-ttl.scss');
|
||||
require('./__list-item/_view/_invisible/blocked-users__list-item_view_invisible.scss');
|
||||
|
||||
require('./__user-id/blocked-users__user-id.scss');
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.comment__control_select {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
.comment__control_select-label {
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
font-weight: 700;
|
||||
&::after {
|
||||
content: '▾';
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import { h, Component } from 'preact';
|
||||
|
||||
import api from 'common/api';
|
||||
import { getHandleClickProps } from 'common/accessibility';
|
||||
import { API_BASE, BASE_URL, COMMENT_NODE_CLASSNAME_PREFIX } from 'common/constants';
|
||||
import { API_BASE, BASE_URL, COMMENT_NODE_CLASSNAME_PREFIX, BLOCKING_DURATIONS } from 'common/constants';
|
||||
import { url } from 'common/settings';
|
||||
import store from 'common/store';
|
||||
|
||||
@@ -37,6 +37,8 @@ export default class Comment extends Component {
|
||||
this.onEdit = this.onEdit.bind(this);
|
||||
this.onReply = this.onReply.bind(this);
|
||||
this.onDeleteClick = this.onDeleteClick.bind(this);
|
||||
this.onBlockUserClick = this.onBlockUserClick.bind(this);
|
||||
this.onUnblockUserClick = this.onUnblockUserClick.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
@@ -164,17 +166,41 @@ export default class Comment extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
toggleBlock(isBlocked) {
|
||||
onBlockUserClick(e) {
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
const promptMessage = `Do you want to ${isBlocked ? 'unblock' : 'block'} this user?`;
|
||||
|
||||
const ttl = e.target.value;
|
||||
const duration = BLOCKING_DURATIONS.find(el => el.value === ttl).label;
|
||||
const promptMessage =
|
||||
ttl === 'permanently'
|
||||
? 'Do you want to permanently block this user?'
|
||||
: `Do you want to block this user (${duration.toLowerCase()})?`;
|
||||
if (confirm(promptMessage)) {
|
||||
this.setState({ userBlocked: true });
|
||||
|
||||
api
|
||||
.blockUser({ id: userId, ttl })
|
||||
.then(api.getComment({ id }))
|
||||
.then(comment => store.replaceComment(comment));
|
||||
}
|
||||
}
|
||||
|
||||
onUnblockUserClick() {
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
|
||||
const promptMessage = `Do you want to unblock this user?`;
|
||||
|
||||
if (confirm(promptMessage)) {
|
||||
this.setState({ userBlocked: !isBlocked });
|
||||
this.setState({ userBlocked: false });
|
||||
|
||||
(isBlocked ? api.unblockUser : api.blockUser)({ id: userId })
|
||||
api
|
||||
.unblockUser({ id: userId })
|
||||
.then(api.getComment({ id }))
|
||||
.then(comment => store.replaceComment(comment));
|
||||
}
|
||||
@@ -501,9 +527,26 @@ export default class Comment extends Component {
|
||||
<span {...getHandleClickProps(() => this.togglePin(pinned))} className="comment__control">
|
||||
{pinned ? 'Unpin' : 'Pin'}
|
||||
</span>
|
||||
<span {...getHandleClickProps(() => this.toggleBlock(userBlocked))} className="comment__control">
|
||||
{userBlocked ? 'Unblock' : 'Block'}
|
||||
</span>
|
||||
|
||||
{userBlocked && (
|
||||
<span {...getHandleClickProps(() => this.onUnblockUserClick())} className="comment__control">
|
||||
Unblock
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!userBlocked && (
|
||||
<span className="comment__control comment__control_select-label">
|
||||
Block
|
||||
{/* eslint-disable jsx-a11y/no-onchange */}
|
||||
<select className="comment__control_select" onChange={this.onBlockUserClick}>
|
||||
<option disabled selected value>
|
||||
{' '}
|
||||
Blocking period{' '}
|
||||
</option>
|
||||
{BLOCKING_DURATIONS.map(block => <option value={block.value}>{block.label}</option>)}
|
||||
</select>
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!deleted && (
|
||||
<span {...getHandleClickProps(this.onDeleteClick)} className="comment__control">
|
||||
|
||||
@@ -13,6 +13,8 @@ require('./__avatar/_default/comment__avatar_default.scss');
|
||||
|
||||
require('./__body/comment__body.scss');
|
||||
require('./__control/comment__control.scss');
|
||||
require('./__control/_select/comment__control_select.scss');
|
||||
require('./__control/_select-label/comment__control_select-label.scss');
|
||||
require('./__controls/comment__controls.scss');
|
||||
require('./__info/comment__info.scss');
|
||||
require('./__input/comment__input.scss');
|
||||
|
||||
Reference in New Issue
Block a user