Browse Source

feat: upvote & downvote

master
hole-thu 3 years ago
parent
commit
a3841ff54a
  1. 35
      src/Flows.css
  2. 80
      src/Flows.js
  3. 15
      src/flows_api.js

35
src/Flows.css

@ -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;
}

80
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,13 +341,62 @@ 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"> <div className="box-content-wrapper">
<HighlightedMarkdown <div className="box-content">
text={info.text} <HighlightedMarkdown
color_picker={color_picker} text={info.text}
show_pid={show_pid} color_picker={color_picker}
search_param={search_param} show_pid={show_pid}
/> search_param={search_param}
/>
</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> </div>
{info.poll && ( {info.poll && (
<div className="box-poll"> <div className="box-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>
); );

15
src/flows_api.js

@ -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],

Loading…
Cancel
Save