feat: upvote & downvote
This commit is contained in:
@@ -393,3 +393,38 @@
|
|||||||
.op-btn {
|
.op-btn {
|
||||||
margin: 0 0.25em;
|
margin: 0 0.25em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.box-content-wrapper {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-content-wrapper .box-content {
|
||||||
|
flex: 1 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box-content-wrapper .box-content-vote {
|
||||||
|
flex: 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-num {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-icon-inactive {
|
||||||
|
fill: None;
|
||||||
|
stroke: #333;
|
||||||
|
stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-dark-mode .vote-icon-inactive {
|
||||||
|
stroke: #777;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.vote-icon-active {
|
||||||
|
fill: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.root-dark-mode .vote-icon-active {
|
||||||
|
fill: #777;
|
||||||
|
}
|
||||||
|
|||||||
66
src/Flows.js
66
src/Flows.js
@@ -228,6 +228,7 @@ class FlowItem extends PureComponent {
|
|||||||
show_pid,
|
show_pid,
|
||||||
do_vote,
|
do_vote,
|
||||||
do_block,
|
do_block,
|
||||||
|
do_react,
|
||||||
search_param,
|
search_param,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
const { cw } = this.state;
|
const { cw } = this.state;
|
||||||
@@ -340,6 +341,7 @@ class FlowItem extends PureComponent {
|
|||||||
{!!info.hot_score && (
|
{!!info.hot_score && (
|
||||||
<span className="box-header">hot score: {info.hot_score}</span>
|
<span className="box-header">hot score: {info.hot_score}</span>
|
||||||
)}
|
)}
|
||||||
|
<div className="box-content-wrapper">
|
||||||
<div className="box-content">
|
<div className="box-content">
|
||||||
<HighlightedMarkdown
|
<HighlightedMarkdown
|
||||||
text={info.text}
|
text={info.text}
|
||||||
@@ -348,6 +350,54 @@ class FlowItem extends PureComponent {
|
|||||||
search_param={search_param}
|
search_param={search_param}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="box-content-vote">
|
||||||
|
<span
|
||||||
|
className={do_react ? 'clickable' : ''}
|
||||||
|
onClick={() => do_react && do_react(1)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className={
|
||||||
|
info.reaction_status === 1
|
||||||
|
? 'vote-icon-active'
|
||||||
|
: 'vote-icon-inactive'
|
||||||
|
}
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
viewBox="0 0 36 36"
|
||||||
|
>
|
||||||
|
<path d="M2 25h32L18 9 2 25Z" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
{do_react ? (
|
||||||
|
<>
|
||||||
|
<div className="vote-num">{info.up_votes}</div>
|
||||||
|
<hr />
|
||||||
|
<div className="vote-num">{info.down_votes}</div>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="vote-num">
|
||||||
|
{info.up_votes - info.down_votes}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<span
|
||||||
|
className={do_react ? 'clickable' : ''}
|
||||||
|
onClick={() => do_react && do_react(-1)}
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className={
|
||||||
|
info.reaction_status === -1
|
||||||
|
? 'vote-icon-active'
|
||||||
|
: 'vote-icon-inactive'
|
||||||
|
}
|
||||||
|
width="28"
|
||||||
|
height="28"
|
||||||
|
viewBox="0 0 36 36"
|
||||||
|
>
|
||||||
|
<path d="M2 11h32L18 27 2 11Z"></path>
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
{info.poll && (
|
{info.poll && (
|
||||||
<div className="box-poll">
|
<div className="box-poll">
|
||||||
<Poll
|
<Poll
|
||||||
@@ -469,6 +519,21 @@ class FlowSidebar extends PureComponent {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set_reaction(reaction_status) {
|
||||||
|
console.log(reaction_status);
|
||||||
|
const prev_info = this.state.info;
|
||||||
|
const pid = prev_info.pid;
|
||||||
|
API.set_reaction(pid, reaction_status, this.props.token).then((json) => {
|
||||||
|
this.setState({
|
||||||
|
info: Object.assign({}, prev_info, json.data),
|
||||||
|
});
|
||||||
|
// TODO: save to localStore
|
||||||
|
this.syncState({
|
||||||
|
info: Object.assign({}, prev_info, json.data),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
toggle_attention() {
|
toggle_attention() {
|
||||||
const prev_info = this.state.info;
|
const prev_info = this.state.info;
|
||||||
const pid = prev_info.pid;
|
const pid = prev_info.pid;
|
||||||
@@ -710,6 +775,7 @@ class FlowSidebar extends PureComponent {
|
|||||||
window.location.reload();
|
window.location.reload();
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
do_react={this.set_reaction.bind(this)}
|
||||||
/>
|
/>
|
||||||
</ClickHandler>
|
</ClickHandler>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -100,6 +100,21 @@ export const API = {
|
|||||||
return handle_response(response, true);
|
return handle_response(response, true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
set_reaction: async (pid, reaction_status, token) => {
|
||||||
|
let data = new URLSearchParams([['status', reaction_status]]);
|
||||||
|
|
||||||
|
let response = await fetch(`${get_api_base_2()}/post/${pid}/reaction`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
'User-Token': token,
|
||||||
|
},
|
||||||
|
body: data,
|
||||||
|
});
|
||||||
|
|
||||||
|
return handle_response(response, true);
|
||||||
|
},
|
||||||
|
|
||||||
report: async (pid, reason, should_hide, token) => {
|
report: async (pid, reason, should_hide, token) => {
|
||||||
let data = new URLSearchParams([
|
let data = new URLSearchParams([
|
||||||
['pid', pid],
|
['pid', pid],
|
||||||
|
|||||||
Reference in New Issue
Block a user