import { h, Component } from 'preact'; import api from 'common/api'; import { API_BASE, BASE_URL } from 'common/constants'; import { url } from 'common/settings'; import store from 'common/store'; import Input from 'components/input'; export default class Comment extends Component { constructor(props) { super(props); this.state = { isInputVisible: false, isUserIdVisible: false, }; this.updateState(props); this.decreaseScore = this.decreaseScore.bind(this); this.increaseScore = this.increaseScore.bind(this); this.toggleInputVisibility = this.toggleInputVisibility.bind(this); this.toggleUserIdVisibility = this.toggleUserIdVisibility.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.updateState(nextProps); } updateState(props) { const { data, data: { user: { block }, pin }, mods: { guest } = {} } = props; const votes = data && data.votes || []; const score = data && data.score || 0; if (guest) { this.setState({ guest, score, deleted: data ? data.delete : false, }); } else { const userId = store.get('user').id; this.setState({ guest, score, pinned: !!pin, deleted: data ? data.delete : false, userBlocked: !!block, scoreIncreased: userId in votes && votes[userId], scoreDecreased: userId in votes && !votes[userId], }); } } toggleInputVisibility() { const { isInputVisible } = this.state; this.setState({ isInputVisible: !isInputVisible }); if (this.props.onReplyClick) { if (!isInputVisible) { this.props.onReplyClick(() => this.setState({ isInputVisible: false })); } else { this.props.onReplyClick(null); } } } toggleUserIdVisibility() { this.setState({ isUserIdVisible: !this.state.isUserIdVisible }); } onPinClick() { const { id } = this.props.data; if (confirm('Do you want to pin this comment?')) { this.setState({ pinned: true }); api.pin({ id, url }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } } onUnpinClick() { const { id } = this.props.data; if (confirm('Do you want to unpin this comment?')) { this.setState({ pinned: false }); api.unpin({ id, url }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } } onBlockClick() { const { id, user: { id: userId } } = this.props.data; if (confirm('Do you want to block this user?')) { this.setState({ userBlocked: true }); api.blockUser({ id: userId }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } } onUnblockClick() { const { id, user: { id: userId } } = this.props.data; if (confirm('Do you want to unblock this user?')) { this.setState({ userBlocked: false }); api.unblockUser({ id: userId }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } } onDeleteClick() { const { id } = this.props.data; if (confirm('Do you want to delete this comment?')) { this.setState({ deleted: true }); api.remove({ id }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } } increaseScore() { const { score, scoreIncreased, scoreDecreased } = this.state; const { id } = this.props.data; if (scoreIncreased) return; this.setState({ scoreIncreased: !scoreDecreased, scoreDecreased: false, score: score + 1, }); api.vote({ id, url, value: 1 }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } decreaseScore() { const { score, scoreIncreased, scoreDecreased } = this.state; const { id } = this.props.data; if (scoreDecreased) return; this.setState({ scoreDecreased: !scoreIncreased, scoreIncreased: false, score: score - 1, }); api.vote({ id, url, value: -1 }).then(() => { api.getComment({ id }).then(comment => store.replaceComment(comment)); }); } onReply(...rest) { this.props.onReply(...rest); this.setState({ isInputVisible: false, }); } render(props, { guest, isUserIdVisible, userBlocked, pinned, score, scoreIncreased, scoreDecreased, deleted, isInputVisible }) { const { data, mix, mods = {} } = props; const isAdmin = !guest && store.get('user').admin; const isGuest = guest || !Object.keys(store.get('user')).length; const isCurrentUser = (data.user && data.user.id) === (store.get('user') && store.get('user').id); const o = { ...data, text: data.text.length ? (mods.view === 'preview' ? getTextSnippet(data.text) : data.text) : ( userBlocked ? 'This user was blocked' : ( deleted ? 'This comment was deleted' : data.text ) ), time: formatTime(new Date(data.time)), score: { value: Math.abs(score), sign: score > 0 ? '+' : (score < 0 ? '−' : null), view: score > 0 ? 'positive' : (score < 0 ? 'negative' : null), }, user: { ...data.user, picture: data.user.picture.indexOf(API_BASE) === 0 ? `${BASE_URL}${data.user.picture}` : data.user.picture, isDefaultPicture: !data.user.picture.length, }, }; const defaultMods = { pinned, useless: userBlocked || deleted, // TODO: add default view mod or don't? view: o.user.admin ? 'admin' : null, replying: isInputVisible, }; if (mods.view === 'preview') { return (
', ' '); const result = tmp.innerText || ''; const snippet = result.substr(0, LENGTH); return (snippet.length === LENGTH && result.length !== LENGTH) ? `${snippet}...` : snippet; } 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}`; }