diff --git a/web/app/common/api.js b/web/app/common/api.js
index 2f648d37..96ea41ef 100644
--- a/web/app/common/api.js
+++ b/web/app/common/api.js
@@ -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,
});
diff --git a/web/app/common/constants.js b/web/app/common/constants.js
index 8a0e38ca..c6fba2ad 100644
--- a/web/app/common/constants.js
+++ b/web/app/common/constants.js
@@ -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,
};
diff --git a/web/app/components/blocked-users/__user-block-ttl/blocked-users__user-block-ttl.scss b/web/app/components/blocked-users/__user-block-ttl/blocked-users__user-block-ttl.scss
new file mode 100644
index 00000000..1ec52c28
--- /dev/null
+++ b/web/app/components/blocked-users/__user-block-ttl/blocked-users__user-block-ttl.scss
@@ -0,0 +1,3 @@
+.blocked-users__user-id {
+ color: #888;
+}
diff --git a/web/app/components/blocked-users/blocked-users.jsx b/web/app/components/blocked-users/blocked-users.jsx
index c6752411..77d1e932 100644
--- a/web/app/components/blocked-users/blocked-users.jsx
+++ b/web/app/components/blocked-users/blocked-users.jsx
@@ -51,6 +51,7 @@ export default class BlockedUsers extends Component {
{user.name}{' '}
({user.id})
+ until {formatTime(new Date(user.time))}
{isUserUnblocked && (
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}`;
+}
diff --git a/web/app/components/blocked-users/index.js b/web/app/components/blocked-users/index.js
index 73878021..454cfc95 100644
--- a/web/app/components/blocked-users/index.js
+++ b/web/app/components/blocked-users/index.js
@@ -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');
diff --git a/web/app/components/comment/__control/_select-label/comment__control_select-label.scss b/web/app/components/comment/__control/_select-label/comment__control_select-label.scss
new file mode 100644
index 00000000..8248322d
--- /dev/null
+++ b/web/app/components/comment/__control/_select-label/comment__control_select-label.scss
@@ -0,0 +1,10 @@
+.comment__control_select {
+ position: absolute;
+ left: 0;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ opacity: 0;
+ width: 100%;
+ cursor: pointer;
+}
diff --git a/web/app/components/comment/__control/_select/comment__control_select.scss b/web/app/components/comment/__control/_select/comment__control_select.scss
new file mode 100644
index 00000000..211c70ff
--- /dev/null
+++ b/web/app/components/comment/__control/_select/comment__control_select.scss
@@ -0,0 +1,9 @@
+.comment__control_select-label {
+ position: relative;
+ white-space: nowrap;
+ font-weight: 700;
+ &::after {
+ content: '▾';
+ margin-left: 2px;
+ }
+}
diff --git a/web/app/components/comment/comment.jsx b/web/app/components/comment/comment.jsx
index 07efd2ee..72d7b4da 100644
--- a/web/app/components/comment/comment.jsx
+++ b/web/app/components/comment/comment.jsx
@@ -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 {
this.togglePin(pinned))} className="comment__control">
{pinned ? 'Unpin' : 'Pin'}
- this.toggleBlock(userBlocked))} className="comment__control">
- {userBlocked ? 'Unblock' : 'Block'}
-
+
+ {userBlocked && (
+ this.onUnblockUserClick())} className="comment__control">
+ Unblock
+
+ )}
+
+ {!userBlocked && (
+
+ Block
+ {/* eslint-disable jsx-a11y/no-onchange */}
+
+
+ )}
{!deleted && (
diff --git a/web/app/components/comment/index.js b/web/app/components/comment/index.js
index 33518776..dcb2e024 100644
--- a/web/app/components/comment/index.js
+++ b/web/app/components/comment/index.js
@@ -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');