Merge remote-tracking branch 'origin/master'
This commit is contained in:
+7
-1
@@ -30,7 +30,7 @@ Content-Type: application/json
|
||||
}
|
||||
|
||||
### pin comment
|
||||
PUT {{host}}/api/v1/admin/pin/3665976683?site=remark&url=https://radio-t.com/p/2017/12/16/podcast-576/&pin=0
|
||||
PUT {{host}}/api/v1/admin/pin/3665976683?site=remark&url=https://radio-t.com/p/2017/12/16/podcast-576/&pin=1
|
||||
|
||||
### vote for comment
|
||||
PUT {{host}}/api/v1/vote/3665976683?site=remark&url=https://radio-t.com/p/2017/12/16/podcast-576/&vote=1
|
||||
@@ -55,3 +55,9 @@ GET {{host}}/api/v1/list?site=remark
|
||||
|
||||
### get config
|
||||
GET {{host}}/api/v1/config
|
||||
|
||||
### block user
|
||||
PUT {{host}}/api/v1/admin/user/username?site=remark&block=1
|
||||
|
||||
### delete comment by id
|
||||
DELETE {{host}}/api/v1/admin/comment/3665976683?site=remark&url=https://radio-t.com/p/2017/12/16/podcast-576/
|
||||
|
||||
+21
-3
@@ -2,11 +2,13 @@ import { siteId, url } from './settings';
|
||||
|
||||
import fetcher from './fetcher'
|
||||
|
||||
/* common */
|
||||
|
||||
export const find = ({ url }) => fetcher.get(`/find?url=${url}&sort=-score&format=tree`);
|
||||
|
||||
export const getComment = ({ id }) => fetcher.get(`/id/${id}?url=${url}`);
|
||||
|
||||
export const getUser = () => fetcher.get('/user');
|
||||
export const vote = ({ id, url, value }) => fetcher.put(`/vote/${id}?url=${url}&vote=${value}`);
|
||||
|
||||
export const send = ({ text, pid }) => fetcher.post('/comment', {
|
||||
text,
|
||||
@@ -17,12 +19,28 @@ export const send = ({ text, pid }) => fetcher.post('/comment', {
|
||||
...(pid ? { pid } : {}),
|
||||
});
|
||||
|
||||
export const vote = ({ id, url, value }) => fetcher.put(`/vote/${id}?url=${url}&vote=${value}`);
|
||||
export const getUser = () => fetcher.get('/user');
|
||||
|
||||
/* admin */
|
||||
export const pin = ({ id, url }) => fetcher.put(`/admin/pin/${id}?url=${url}&pin=1`);
|
||||
|
||||
export const unpin = ({ id, url }) => fetcher.put(`/admin/pin/${id}?url=${url}&pin=0`);
|
||||
|
||||
export const remove = ({ id }) => fetcher.delete(`/admin/comment/${id}?url=${url}`);
|
||||
|
||||
export const blockUser = ({ id }) => fetcher.put(`/admin/user/${id}?&block=1`);
|
||||
|
||||
export const unblockUser = ({ id }) => fetcher.put(`/admin/user/${id}?&block=0`);
|
||||
|
||||
export default {
|
||||
find,
|
||||
getComment,
|
||||
send,
|
||||
vote,
|
||||
send,
|
||||
getUser,
|
||||
pin,
|
||||
unpin,
|
||||
remove,
|
||||
blockUser,
|
||||
unblockUser,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
.comment__action {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
.comment__controls_view_admin {
|
||||
color: #ff4700;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
.comment__controls {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
color: #06c5c5;
|
||||
user-select: none;
|
||||
|
||||
@media (hover: hover) {
|
||||
margin-left: 0;
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.comment__reply {
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
.comment_pinned {
|
||||
&, &.comment_view_admin {
|
||||
.comment__username {
|
||||
color: #FFD700;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,24 +14,31 @@ export default class Comment extends Component {
|
||||
isInputVisible: false,
|
||||
};
|
||||
|
||||
this.updateScoreState(props);
|
||||
this.updateState(props);
|
||||
|
||||
this.decreaseScore = this.decreaseScore.bind(this);
|
||||
this.increaseScore = this.increaseScore.bind(this);
|
||||
this.onReplyClick = this.onReplyClick.bind(this);
|
||||
this.onReply = this.onReply.bind(this);
|
||||
this.onPinClick = this.onPinClick.bind(this);
|
||||
this.onUnpinClick = this.onUnpinClick.bind(this);
|
||||
this.onBlockClick = this.onBlockClick.bind(this);
|
||||
this.onUnblockClick = this.onUnblockClick.bind(this);
|
||||
this.onDeleteClick = this.onDeleteClick.bind(this);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.updateScoreState(nextProps);
|
||||
this.updateState(nextProps);
|
||||
}
|
||||
|
||||
updateScoreState(props) {
|
||||
const { score = 0, votes = [] } = props.data;
|
||||
updateState(props) {
|
||||
const { pin, score = 0, votes = [] } = props.data;
|
||||
const userId = store.get('user').id;
|
||||
|
||||
this.setState({
|
||||
score: score,
|
||||
pinned: !!pin,
|
||||
userBlocked: !!store.get('user').block,
|
||||
scoreIncreased: userId in votes && votes[userId],
|
||||
scoreDecreased: userId in votes && !votes[userId],
|
||||
});
|
||||
@@ -43,6 +50,52 @@ export default class Comment extends Component {
|
||||
this.setState({ isInputVisible: !isInputVisible });
|
||||
}
|
||||
|
||||
onPinClick() {
|
||||
const { id } = this.props.data;
|
||||
|
||||
this.setState({ pinned: true });
|
||||
|
||||
api.pin({ id, url }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
}
|
||||
|
||||
onUnpinClick() {
|
||||
const { id } = this.props.data;
|
||||
|
||||
this.setState({ pinned: false });
|
||||
|
||||
api.pin({ id, url }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
}
|
||||
|
||||
onBlockClick() {
|
||||
const { user: { id } } = this.props.data;
|
||||
|
||||
this.setState({ userBlocked: true });
|
||||
|
||||
api.blockUser({ id });
|
||||
}
|
||||
|
||||
onUnblockClick() {
|
||||
const { user: { id } } = this.props.data;
|
||||
|
||||
this.setState({ userBlocked: false });
|
||||
|
||||
api.unblockUser({ id });
|
||||
}
|
||||
|
||||
onDeleteClick() {
|
||||
const { id } = this.props.data;
|
||||
|
||||
api.remove({ id }).then(() => {
|
||||
if (this.props.onDelete) {
|
||||
this.props.onDelete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
increaseScore() {
|
||||
const { score, scoreIncreased, scoreDecreased } = this.state;
|
||||
const { id } = this.props.data;
|
||||
@@ -82,8 +135,9 @@ export default class Comment extends Component {
|
||||
this.setState({ isInputVisible: false });
|
||||
}
|
||||
|
||||
render(props, { score, scoreIncreased, scoreDecreased, isInputVisible }) {
|
||||
render(props, { userBlocked, pinned, score, scoreIncreased, scoreDecreased, isInputVisible }) {
|
||||
const { data, mix, mods = {} } = props;
|
||||
const isAdmin = store.get('user').admin;
|
||||
|
||||
const time = new Date(data.time);
|
||||
// TODO: which format for datetime should we choose?
|
||||
@@ -100,9 +154,14 @@ export default class Comment extends Component {
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
const defaultMods = {
|
||||
pinned,
|
||||
// TODO: add default view mod or don't?
|
||||
<div className={b('comment', props, { view: data.user.admin ? 'admin' : null })}>
|
||||
view: o.user.admin ? 'admin' : null,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={b('comment', props, defaultMods)}>
|
||||
<div className="comment__body">
|
||||
<img src={o.user.picture} alt="" className="comment__avatar"/>
|
||||
|
||||
@@ -111,23 +170,57 @@ export default class Comment extends Component {
|
||||
<span className="comment__username">{o.user.name}</span>
|
||||
|
||||
<span className="comment__score">
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'up', selected: scoreIncreased })}
|
||||
onClick={this.increaseScore}
|
||||
>vote up</span>
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'up', selected: scoreIncreased })}
|
||||
onClick={this.increaseScore}
|
||||
>vote up</span>
|
||||
<span className="comment__score-sign">{o.score.sign}</span>
|
||||
<span className="comment__score-value">{o.score.value}</span>
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'down', selected: scoreDecreased })}
|
||||
onClick={this.decreaseScore}
|
||||
>vote down</span>
|
||||
</span>
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'down', selected: scoreDecreased })}
|
||||
onClick={this.decreaseScore}
|
||||
>vote down</span>
|
||||
</span>
|
||||
|
||||
<span className="comment__time">{o.time}</span>
|
||||
|
||||
<span className="comment__controls">
|
||||
<span className="comment__reply" onClick={this.onReplyClick}>reply</span>
|
||||
</span>
|
||||
<span className="comment__action" onClick={this.onReplyClick}>reply</span>
|
||||
</span>
|
||||
|
||||
{
|
||||
isAdmin &&
|
||||
(
|
||||
<span className={b('comment__controls', {}, { view: 'admin' })}>
|
||||
|
||||
{
|
||||
!pinned && (
|
||||
<span className="comment__action" onClick={this.onPinClick}>pin</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
pinned && (
|
||||
<span className="comment__action" onClick={this.onUnpinClick}>unpin</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
userBlocked && (
|
||||
<span className="comment__action" onClick={this.onUnblockClick}>unblock</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!userBlocked && (
|
||||
<span className="comment__action" onClick={this.onBlockClick}>block</span>
|
||||
)
|
||||
}
|
||||
|
||||
<span className="comment__action" onClick={this.onDeleteClick}>delete</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="comment__text" dangerouslySetInnerHTML={{ __html: o.text }}/>
|
||||
|
||||
@@ -2,12 +2,15 @@ export { default } from './comment';
|
||||
|
||||
require('./comment.scss');
|
||||
|
||||
require('./__action/comment__action.scss');
|
||||
require('./__avatar/comment__avatar.scss');
|
||||
require('./__body/comment__body.scss');
|
||||
|
||||
require('./__controls/comment__controls.scss');
|
||||
require('./__controls/_view/_admin/comment__controls_view_admin.scss');
|
||||
|
||||
require('./__info/comment__info.scss');
|
||||
require('./__input/comment__input.scss');
|
||||
require('./__reply/comment__reply.scss');
|
||||
require('./__score/comment__score.scss');
|
||||
require('./__score-sign/comment__score-sign.scss');
|
||||
require('./__score-value/comment__score-value.scss');
|
||||
@@ -22,4 +25,6 @@ require('./__vote/_type/_up/comment__vote_type_up.scss');
|
||||
|
||||
require('./_level/comment_level.scss');
|
||||
|
||||
require('./_pinned/comment_pinned.scss');
|
||||
|
||||
require('./_view/_admin/comment_view_admin.scss');
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.thread_hidden {
|
||||
display: none;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
export { default } from './thread';
|
||||
|
||||
require('./thread.scss');
|
||||
|
||||
require('./_hidden/thread_hidden.scss');
|
||||
|
||||
@@ -3,12 +3,31 @@ import { h, Component } from 'preact';
|
||||
import Comment from 'components/comment';
|
||||
|
||||
export default class Thread extends Component {
|
||||
render(props, state) {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
hidden: false,
|
||||
};
|
||||
|
||||
this.hide = this.hide.bind(this);
|
||||
}
|
||||
|
||||
hide() {
|
||||
this.setState({ hidden: true });
|
||||
}
|
||||
|
||||
render(props, { hidden }) {
|
||||
const { data: { comment, replies = [] }, mix, mods = {} } = props;
|
||||
|
||||
return (
|
||||
<div className={b('thread', props)}>
|
||||
<Comment data={comment} mods={{ level: mods.level }} onReply={props.onReply}/>
|
||||
<div className={b('thread', props, { hidden })}>
|
||||
<Comment
|
||||
data={comment}
|
||||
mods={{ level: mods.level }}
|
||||
onReply={props.onReply}
|
||||
onDelete={this.hide}
|
||||
/>
|
||||
|
||||
{
|
||||
!!replies.length && replies.map(thread => (
|
||||
|
||||
Reference in New Issue
Block a user