rewrite draft using preact

This commit is contained in:
igoradamenko
2018-01-23 20:15:35 +02:00
parent 26e2445913
commit 1f4e1b160f
31 changed files with 374 additions and 266 deletions
@@ -0,0 +1,10 @@
.comment__avatar {
flex-shrink: 0;
flex-grow: 0;
display: inline-block;
width: 48px;
height: 48px;
margin-right: 8px;
border-radius: 4px;
background: #eee;
}
@@ -0,0 +1,3 @@
.comment__info {
margin-bottom: 4px;
}
@@ -0,0 +1,4 @@
.comment__score-sign {
font-size: 14px;
vertical-align: 1px;
}
@@ -0,0 +1,3 @@
.comment__score-value {
font-size: 14px;
}
@@ -0,0 +1,6 @@
.comment__score {
margin-left: 8px;
font-size: 0;
color: #666;
cursor: default;
}
@@ -0,0 +1,9 @@
.comment__text {
p {
margin: 0;
+ p {
margin-top: 5px;
}
}
}
@@ -0,0 +1,5 @@
.comment__time {
margin-left: 8px;
font-size: 14px;
color: #666;
}
@@ -0,0 +1,4 @@
.comment__username {
text-decoration: none;
color: #19f;
}
@@ -0,0 +1,4 @@
.comment__vote_selected {
border-top-color: #19f;
cursor: default;
}
@@ -0,0 +1,3 @@
.comment__vote_type_up {
transform: scale(1, -1);
}
@@ -0,0 +1,13 @@
.comment__vote {
display: inline-block;
border-top: 9px solid #ccc;
border-right: 7px solid transparent;
border-left: 7px solid transparent;
font-size: 0;
line-height: 0;
cursor: pointer;
&:hover {
border-top-color: #19f;
}
}
@@ -0,0 +1,9 @@
@import 'common/variables';
.comment_level {
@for $i from 1 through 5 {
&_#{$i} {
margin-left: $i * $offset;
}
}
}
@@ -0,0 +1,6 @@
.comment_view_admin {
.comment__username {
font-weight: 700;
color: #ff4700;
};
}
+98
View File
@@ -0,0 +1,98 @@
import { h, Component } from 'preact';
import { vote } from 'common/api';
import { url, userId } from 'common/settings';
export default class Comment extends Component {
constructor(props) {
super(props);
const { score, votes } = props.data;
this.state = {
score: score,
scoreIncreased: userId in votes && votes[userId],
scoreDecreased: userId in votes && !votes[userId],
};
this.decreaseScore = this.decreaseScore.bind(this);
this.increaseScore = this.increaseScore.bind(this);
}
increaseScore() {
const { score, scoreIncreased, scoreDecreased } = this.state;
if (scoreIncreased) return;
this.setState({
scoreIncreased: !scoreDecreased,
scoreDecreased: false,
score: score + 1,
});
vote({ id: this.props.data.id, url, value: 1 });
}
decreaseScore() {
const { score, scoreIncreased, scoreDecreased } = this.state;
if (scoreDecreased) return;
this.setState({
scoreDecreased: !scoreIncreased,
scoreIncreased: false,
score: score - 1,
});
vote({ id: this.props.data.id, url, value: -1 });
}
render(props, { score, scoreIncreased, scoreDecreased }) {
const { data, mix, mods = {} } = props;
console.log('rerender', data.id)
const time = new Date(data.time);
// TODO: which format for datetime should we choose?
// TODO: add smth that will count 'hours ago'
// TODO: check out stash's impl
const timeStr = `${time.toLocaleDateString()} ${time.toLocaleTimeString()}`;
const o = {
...data,
time: timeStr,
score: {
value: Math.abs(score),
sign: score > 0 ? '+' : (score < 0 ? '' : ''),
},
};
return (
// TODO: add default view mod or don't?
<div className={b('comment', props, { view: data.user.admin ? 'admin' : null })} data-id={o.id}>
<img src={o.user.picture} alt="" className="comment__avatar"/>
<div className="comment__content">
<div className="comment__info">
<a href="#" className="comment__username">{o.user.name}</a>
<span className="comment__score">
<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="comment__time">{o.time}</span>
</div>
<div className="comment__text" dangerouslySetInnerHTML={{ __html: o.text }}/>
</div>
</div>
);
}
}
+10
View File
@@ -0,0 +1,10 @@
@import 'common/variables';
.comment {
display: flex;
position: relative;
overflow: hidden;
margin-bottom: $offset;
font-size: 16px;
line-height: 20px;
}
+20
View File
@@ -0,0 +1,20 @@
export { default } from './comment';
require('./comment.scss');
require('./__avatar/comment__avatar.scss');
require('./__info/comment__info.scss');
require('./__score/comment__score.scss');
require('./__score-sign/comment__score-sign.scss');
require('./__score-value/comment__score-value.scss');
require('./__text/comment__text.scss');
require('./__time/comment__time.scss');
require('./__username/comment__username.scss');
require('./__vote/comment__vote.scss');
require('./__vote/_selected/comment__vote_selected.scss');
require('./__vote/_type/_up/comment__vote_type_up.scss');
require('./_level/comment_level.scss');
require('./_view/_admin/comment_view_admin.scss');
+2
View File
@@ -1 +1,3 @@
export { default } from './root';
require('./root.scss');
+15 -14
View File
@@ -1,22 +1,23 @@
import { Component } from 'preact';
import fetcher from 'common/fetcher';
import { h, Component } from 'preact';
import api from 'common/api';
import { url, id } from 'common/settings';
import Thread from 'components/thread';
export default class Root extends Component {
componentDidMount() {
// TODO: add preloader
// TODO: all of these settings must be optional params
fetcher
.get('/find?url=https://radio-t.com/p/2017/12/16/podcast-576/&sort=time&format=tree')
.then(this.loadData.bind(this));
api.find({ url }).then(({ comments } = {}) => this.setState({ comments }));
}
loadData(data) {
this.setState({ data: JSON.stringify(data) })
}
render() {
const { data } = this.state;
return data;
render({}, { comments = [] }) {
return (
<div className="root" id={id}>
{
comments.map(thread => <Thread data={thread} mods={{ level: 0 }}/>)
}
</div>
);
}
}
+5
View File
@@ -0,0 +1,5 @@
@import 'common/variables';
.root {
margin-bottom: -$offset;
}
+3
View File
@@ -0,0 +1,3 @@
export { default } from './thread';
require('./thread.scss');
+24
View File
@@ -0,0 +1,24 @@
import { h, Component } from 'preact';
import Comment from 'components/comment';
export default class Thread extends Component {
render(props, state) {
const { data: { comment, replies = [] }, mix, mods = {} } = props;
return (
<div className={b('thread', props)}>
<Comment data={comment} mods={{ level: mods.level }}/>
{
!!replies.length && replies.map(thread => (
<Thread
data={thread}
mods={{ level: mods.level < 5 ? mods.level + 1 : mods.level }}
/>
))
}
</div>
);
}
}
+5
View File
@@ -0,0 +1,5 @@
@import 'common/variables';
.thread {
margin-bottom: $offset;
}