set prettier rules to warn and fix jsx pragma
This commit is contained in:
committed by
Aleksei Gurianov
parent
dec8bb4fc9
commit
9949632cd7
+1
-1
@@ -53,6 +53,6 @@ module.exports = {
|
||||
'no-var': 2,
|
||||
'object-shorthand': 2,
|
||||
'prefer-arrow-callback': 2,
|
||||
'prettier/prettier': 'error',
|
||||
'prettier/prettier': 1,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/** @jsx h */
|
||||
import { h, render } from 'preact';
|
||||
import AuthPanel from '../auth-panel';
|
||||
import {createDomContainer} from 'testUtils';
|
||||
import { createDomContainer } from 'testUtils';
|
||||
|
||||
describe('<AuthPanel />', () => {
|
||||
describe('For not authorized user', () => {
|
||||
let container;
|
||||
|
||||
createDomContainer(({domContainer}) => {
|
||||
createDomContainer(({ domContainer }) => {
|
||||
container = domContainer;
|
||||
});
|
||||
|
||||
@@ -21,8 +22,7 @@ describe('<AuthPanel />', () => {
|
||||
|
||||
const authForm = authPanelColumn[0];
|
||||
|
||||
expect(authForm.textContent)
|
||||
.toEqual(expect.stringContaining('Sign in to comment using'));
|
||||
expect(authForm.textContent).toEqual(expect.stringContaining('Sign in to comment using'));
|
||||
|
||||
const providerLinks = authForm.querySelectorAll('.auth-panel__pseudo-link');
|
||||
|
||||
@@ -33,12 +33,12 @@ describe('<AuthPanel />', () => {
|
||||
describe('For authorized user', () => {
|
||||
let container;
|
||||
|
||||
createDomContainer(({domContainer}) => {
|
||||
createDomContainer(({ domContainer }) => {
|
||||
container = domContainer;
|
||||
});
|
||||
|
||||
it('should render info about current user', () => {
|
||||
const element = <AuthPanel user={{id: `test`, name: 'John'}} sort="-score" providers={[`google`, `github`]} />;
|
||||
const element = <AuthPanel user={{ id: `test`, name: 'John' }} sort="-score" providers={[`google`, `github`]} />;
|
||||
|
||||
render(element, container);
|
||||
|
||||
@@ -48,26 +48,26 @@ describe('<AuthPanel />', () => {
|
||||
|
||||
const userInfo = authPanelColumn[0];
|
||||
|
||||
expect(userInfo.textContent)
|
||||
.toEqual(expect.stringContaining('You signed in as John. Sign out?'));
|
||||
expect(userInfo.textContent).toEqual(expect.stringContaining('You signed in as John. Sign out?'));
|
||||
});
|
||||
});
|
||||
describe('For admin user', () => {
|
||||
let container;
|
||||
|
||||
createDomContainer(({domContainer}) => {
|
||||
createDomContainer(({ domContainer }) => {
|
||||
container = domContainer;
|
||||
});
|
||||
|
||||
it('should render admin action', () => {
|
||||
const element = <AuthPanel user={{id: `test`, admin: true, name: 'John'}} sort="-score" providers={[`google`, `github`]} />;
|
||||
const element = (
|
||||
<AuthPanel user={{ id: `test`, admin: true, name: 'John' }} sort="-score" providers={[`google`, `github`]} />
|
||||
);
|
||||
|
||||
render(element, container);
|
||||
|
||||
const adminAction = container.querySelector('.auth-panel__admin-action');
|
||||
|
||||
expect(adminAction.textContent)
|
||||
.toEqual('Show blocked');
|
||||
expect(adminAction.textContent).toEqual('Show blocked');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { PROVIDER_NAMES } from 'common/constants';
|
||||
@@ -37,71 +38,67 @@ export default class AuthPanel extends Component {
|
||||
let loggedIn = !!user.id;
|
||||
return (
|
||||
<div className={b('auth-panel', props, { loggedIn })}>
|
||||
{
|
||||
loggedIn && (
|
||||
<div className="auth-panel__column">
|
||||
You signed in as
|
||||
{' '}
|
||||
<strong className="auth-panel__username" onClick={this.toggleUserId}>{user.name}</strong>
|
||||
{isUserIdVisible && <span className="auth-panel__user-id"> ({user.id})</span>}.
|
||||
{' '}
|
||||
<span className="auth-panel__pseudo-link" role="link" tabIndex="0" onClick={props.onSignOut}>Sign out?</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{loggedIn && (
|
||||
<div className="auth-panel__column">
|
||||
You signed in as{' '}
|
||||
<strong className="auth-panel__username" onClick={this.toggleUserId}>
|
||||
{user.name}
|
||||
</strong>
|
||||
{isUserIdVisible && <span className="auth-panel__user-id"> ({user.id})</span>}.{' '}
|
||||
<span className="auth-panel__pseudo-link" role="link" tabIndex="0" onClick={props.onSignOut}>
|
||||
Sign out?
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
!loggedIn && (
|
||||
<div className="auth-panel__column">
|
||||
Sign in to comment using
|
||||
{' '}
|
||||
{
|
||||
providers.map((provider, i) => {
|
||||
const comma = i === 0 ? '' : (i === providers.length - 1 ? ' or ' : ', ');
|
||||
{!loggedIn && (
|
||||
<div className="auth-panel__column">
|
||||
Sign in to comment using{' '}
|
||||
{providers.map((provider, i) => {
|
||||
const comma = i === 0 ? '' : i === providers.length - 1 ? ' or ' : ', ';
|
||||
|
||||
return (
|
||||
<span>
|
||||
{comma}
|
||||
<span
|
||||
className="auth-panel__pseudo-link"
|
||||
role="link"
|
||||
tabIndex="0"
|
||||
onClick={() => props.onSignIn(provider)}
|
||||
>{PROVIDER_NAMES[provider]}</span>
|
||||
</span>
|
||||
);
|
||||
})
|
||||
}
|
||||
{'.'}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<span>
|
||||
{comma}
|
||||
<span
|
||||
className="auth-panel__pseudo-link"
|
||||
role="link"
|
||||
tabIndex="0"
|
||||
onClick={() => props.onSignIn(provider)}
|
||||
>
|
||||
{PROVIDER_NAMES[provider]}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
{'.'}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="auth-panel__column">
|
||||
{
|
||||
user.admin && (
|
||||
<span
|
||||
className="auth-panel__pseudo-link auth-panel__admin-action"
|
||||
role="link"
|
||||
tabIndex="0"
|
||||
onClick={this.toggleBlockedVisibility}
|
||||
>{isBlockedVisible ? 'Hide' : 'Show'} blocked</span>
|
||||
)
|
||||
}
|
||||
{user.admin && (
|
||||
<span
|
||||
className="auth-panel__pseudo-link auth-panel__admin-action"
|
||||
role="link"
|
||||
tabIndex="0"
|
||||
onClick={this.toggleBlockedVisibility}
|
||||
>
|
||||
{isBlockedVisible ? 'Hide' : 'Show'} blocked
|
||||
</span>
|
||||
)}
|
||||
|
||||
{user.admin && ' • '}
|
||||
|
||||
<span className="auth-panel__sort">
|
||||
Sort by
|
||||
{' '}
|
||||
Sort by{' '}
|
||||
<label className="auth-panel__select-label">
|
||||
{sortArray.find(x => x.selected).label}
|
||||
<select className="auth-panel__select" onChange={this.onSortChange}>
|
||||
{
|
||||
sortArray.map(sort => (
|
||||
<option value={sort.value} selected={sort.selected}>{sort.label}</option>
|
||||
))
|
||||
}
|
||||
{sortArray.map(sort => (
|
||||
<option value={sort.value} selected={sort.selected}>
|
||||
{sort.label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import api from 'common/api';
|
||||
@@ -36,49 +37,34 @@ export default class BlockedUsers extends Component {
|
||||
|
||||
return (
|
||||
<div className={b('blocked-users', props)} role="region" aria-label="Blocked users">
|
||||
{
|
||||
!users.length && (
|
||||
<p>There are no blocked users.</p>
|
||||
)
|
||||
}
|
||||
{!users.length && <p>There are no blocked users.</p>}
|
||||
|
||||
{
|
||||
!!users.length && (
|
||||
<p>List of blocked users:</p>
|
||||
)
|
||||
}
|
||||
{!!users.length && <p>List of blocked users:</p>}
|
||||
|
||||
{
|
||||
!!users.length && (
|
||||
<ul className="blocked-users__list">
|
||||
{
|
||||
users.map(user => {
|
||||
const isUserUnblocked = unblockedUsers.includes(user.id);
|
||||
{!!users.length && (
|
||||
<ul className="blocked-users__list">
|
||||
{users.map(user => {
|
||||
const isUserUnblocked = unblockedUsers.includes(user.id);
|
||||
|
||||
return (
|
||||
<li className={b('blocked-users__list-item', {}, { view: isUserUnblocked ? 'invisible' : null })}>
|
||||
<span className="blocked-users__username">{user.name}</span>
|
||||
{' '}
|
||||
<span className="blocked-users__user-id">({user.id})</span>
|
||||
|
||||
{
|
||||
isUserUnblocked && (
|
||||
<span className="blocked-users__action" onClick={() => this.block(user)}>block</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!isUserUnblocked && (
|
||||
<span className="blocked-users__action" onClick={() => this.unblock(user)}>unblock</span>
|
||||
)
|
||||
}
|
||||
</li>
|
||||
);
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
return (
|
||||
<li className={b('blocked-users__list-item', {}, { view: isUserUnblocked ? 'invisible' : null })}>
|
||||
<span className="blocked-users__username">{user.name}</span>{' '}
|
||||
<span className="blocked-users__user-id">({user.id})</span>
|
||||
{isUserUnblocked && (
|
||||
<span className="blocked-users__action" onClick={() => this.block(user)}>
|
||||
block
|
||||
</span>
|
||||
)}
|
||||
{!isUserUnblocked && (
|
||||
<span className="blocked-users__action" onClick={() => this.unblock(user)}>
|
||||
unblock
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import api from 'common/api';
|
||||
@@ -47,10 +48,17 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
updateState(props) {
|
||||
const { data, data: { user: { block, id: commentUserId }, pin }, mods: { guest } = {} } = props;
|
||||
const {
|
||||
data,
|
||||
data: {
|
||||
user: { block, id: commentUserId },
|
||||
pin,
|
||||
},
|
||||
mods: { guest } = {},
|
||||
} = props;
|
||||
|
||||
const votes = data && data.votes || [];
|
||||
const score = data && data.score || 0;
|
||||
const votes = (data && data.votes) || [];
|
||||
const score = (data && data.score) || 0;
|
||||
|
||||
if (this.editTimerInterval) {
|
||||
clearInterval(this.editTimerInterval);
|
||||
@@ -156,7 +164,10 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
onVerifyClick() {
|
||||
const { id, user: { id: userId } } = this.props.data;
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
|
||||
if (confirm('Do you want to verify this user?')) {
|
||||
this.setState({ isUserVerified: true });
|
||||
@@ -168,7 +179,10 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
onUnverifyClick() {
|
||||
const { id, user: { id: userId } } = this.props.data;
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
|
||||
if (confirm('Do you want to unverify this user?')) {
|
||||
this.setState({ isUserVerified: false });
|
||||
@@ -180,7 +194,10 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
onBlockClick() {
|
||||
const { id, user: { id: userId } } = this.props.data;
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
|
||||
if (confirm('Do you want to block this user?')) {
|
||||
this.setState({ userBlocked: true });
|
||||
@@ -192,7 +209,10 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
onUnblockClick() {
|
||||
const { id, user: { id: userId } } = this.props.data;
|
||||
const {
|
||||
id,
|
||||
user: { id: userId },
|
||||
} = this.props.data;
|
||||
|
||||
if (confirm('Do you want to unblock this user?')) {
|
||||
this.setState({ userBlocked: false });
|
||||
@@ -231,13 +251,11 @@ export default class Comment extends Component {
|
||||
score: score + 1,
|
||||
});
|
||||
|
||||
this.votingPromise = this.votingPromise
|
||||
.then(() => {
|
||||
return api.putCommentVote({ id, url, value: 1 })
|
||||
.then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
this.votingPromise = this.votingPromise.then(() => {
|
||||
return api.putCommentVote({ id, url, value: 1 }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
decreaseScore() {
|
||||
@@ -252,13 +270,11 @@ export default class Comment extends Component {
|
||||
score: score - 1,
|
||||
});
|
||||
|
||||
this.votingPromise = this.votingPromise
|
||||
.then(() => {
|
||||
return api.putCommentVote({ id, url, value: -1 })
|
||||
.then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
this.votingPromise = this.votingPromise.then(() => {
|
||||
return api.putCommentVote({ id, url, value: -1 }).then(() => {
|
||||
api.getComment({ id }).then(comment => store.replaceComment(comment));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onReply(...rest) {
|
||||
@@ -278,7 +294,9 @@ export default class Comment extends Component {
|
||||
}
|
||||
|
||||
scrollToParent(e) {
|
||||
const { data: { pid } } = this.props;
|
||||
const {
|
||||
data: { pid },
|
||||
} = this.props;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
@@ -300,20 +318,23 @@ export default class Comment extends Component {
|
||||
}
|
||||
}
|
||||
|
||||
render(props, {
|
||||
guest,
|
||||
userBlocked,
|
||||
pinned,
|
||||
score,
|
||||
scoreIncreased,
|
||||
scoreDecreased,
|
||||
deleted,
|
||||
isReplying,
|
||||
isEditing,
|
||||
isUserVerified,
|
||||
editTimeLeft,
|
||||
isUserInfoShown,
|
||||
}) {
|
||||
render(
|
||||
props,
|
||||
{
|
||||
guest,
|
||||
userBlocked,
|
||||
pinned,
|
||||
score,
|
||||
scoreIncreased,
|
||||
scoreDecreased,
|
||||
deleted,
|
||||
isReplying,
|
||||
isEditing,
|
||||
isUserVerified,
|
||||
editTimeLeft,
|
||||
isUserInfoShown,
|
||||
}
|
||||
) {
|
||||
const { data, mods = {} } = props;
|
||||
const isAdmin = !guest && store.get('user').admin;
|
||||
const isGuest = guest || !Object.keys(store.get('user')).length;
|
||||
@@ -323,33 +344,28 @@ export default class Comment extends Component {
|
||||
|
||||
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
|
||||
)
|
||||
),
|
||||
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)),
|
||||
orig: isEditing
|
||||
? (
|
||||
data.orig && data.orig
|
||||
.replace(/&[#A-Za-z0-9]+;/gi, entity => {
|
||||
const span = document.createElement('span');
|
||||
span.innerHTML = entity;
|
||||
return span.innerText;
|
||||
})
|
||||
)
|
||||
? data.orig &&
|
||||
data.orig.replace(/&[#A-Za-z0-9]+;/gi, entity => {
|
||||
const span = document.createElement('span');
|
||||
span.innerHTML = entity;
|
||||
return span.innerText;
|
||||
})
|
||||
: data.orig,
|
||||
score: {
|
||||
value: Math.abs(score),
|
||||
sign: score > 0 ? '+' : (score < 0 ? '−' : null),
|
||||
view: score > 0 ? 'positive' : (score < 0 ? 'negative' : null),
|
||||
sign: score > 0 ? '+' : score < 0 ? '−' : null,
|
||||
view: score > 0 ? 'positive' : score < 0 ? 'negative' : null,
|
||||
},
|
||||
user: {
|
||||
...data.user,
|
||||
@@ -374,66 +390,59 @@ export default class Comment extends Component {
|
||||
<article className={b('comment', props, defaultMods)}>
|
||||
<div className="comment__body">
|
||||
<div className="comment__info">
|
||||
<a href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`} className="comment__username">{o.user.name}</a>
|
||||
</div>
|
||||
{' '}
|
||||
<div
|
||||
className={b('comment__text', { mix: 'raw-content' })}
|
||||
dangerouslySetInnerHTML={{ __html: o.text }}
|
||||
/>
|
||||
<a href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`} className="comment__username">
|
||||
{o.user.name}
|
||||
</a>
|
||||
</div>{' '}
|
||||
<div className={b('comment__text', { mix: 'raw-content' })} dangerouslySetInnerHTML={{ __html: o.text }} />
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<article className={b('comment', props, defaultMods)} id={mods.disabled ? null : `${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`}>
|
||||
<article
|
||||
className={b('comment', props, defaultMods)}
|
||||
id={mods.disabled ? null : `${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`}
|
||||
>
|
||||
<div className="comment__body">
|
||||
<div className="comment__info">
|
||||
{
|
||||
mods.view !== 'user' && (
|
||||
<img
|
||||
className={b('comment__avatar', {}, { default: o.user.isDefaultPicture })}
|
||||
src={o.user.isDefaultPicture ? require('./__avatar/comment__avatar.svg') : o.user.picture}
|
||||
alt=""
|
||||
/>
|
||||
)
|
||||
}
|
||||
{mods.view !== 'user' && (
|
||||
<img
|
||||
className={b('comment__avatar', {}, { default: o.user.isDefaultPicture })}
|
||||
src={o.user.isDefaultPicture ? require('./__avatar/comment__avatar.svg') : o.user.picture}
|
||||
alt=""
|
||||
/>
|
||||
)}
|
||||
|
||||
{
|
||||
mods.view !== 'user' && (
|
||||
<span
|
||||
className="comment__username"
|
||||
title={o.user.id}
|
||||
onClick={this.toggleUserInfoVisibility}
|
||||
>{o.user.name}</span>
|
||||
)
|
||||
}
|
||||
{mods.view !== 'user' && (
|
||||
<span className="comment__username" title={o.user.id} onClick={this.toggleUserInfoVisibility}>
|
||||
{o.user.name}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{
|
||||
isAdmin && mods.view !== 'user' && (
|
||||
{isAdmin &&
|
||||
mods.view !== 'user' && (
|
||||
<span
|
||||
onClick={o.user.verified ? this.onUnverifyClick : this.onVerifyClick}
|
||||
aria-label="Toggle verification"
|
||||
title={o.user.verified ? 'Verified user' : 'Unverified user'}
|
||||
className={b('comment__verification', {}, { active: o.user.verified, clickable: true })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
|
||||
{
|
||||
!isAdmin && !!o.user.verified && mods.view !== 'user' && (
|
||||
<span
|
||||
title="Verified user"
|
||||
className={b('comment__verification', {}, { active: true })}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{!isAdmin &&
|
||||
!!o.user.verified &&
|
||||
mods.view !== 'user' && (
|
||||
<span title="Verified user" className={b('comment__verification', {}, { active: true })} />
|
||||
)}
|
||||
|
||||
<a href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`} className="comment__time">{o.time}</a>
|
||||
<a href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.id}`} className="comment__time">
|
||||
{o.time}
|
||||
</a>
|
||||
|
||||
{
|
||||
mods.level > 0 && mods.view !== 'user' && (
|
||||
{mods.level > 0 &&
|
||||
mods.view !== 'user' && (
|
||||
<a
|
||||
className="comment__link-to-parent"
|
||||
href={`${o.locator.url}#${COMMENT_NODE_CLASSNAME_PREFIX}${o.pid}`}
|
||||
@@ -441,168 +450,148 @@ export default class Comment extends Component {
|
||||
title="Go to parent comment"
|
||||
onClick={this.scrollToParent}
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
|
||||
{
|
||||
isAdmin && userBlocked && mods.view !== 'user' && (
|
||||
<span className="comment__status">Blocked</span>
|
||||
)
|
||||
}
|
||||
{isAdmin && userBlocked && mods.view !== 'user' && <span className="comment__status">Blocked</span>}
|
||||
|
||||
{
|
||||
isAdmin && !userBlocked && deleted && (
|
||||
<span className="comment__status">Deleted</span>
|
||||
)
|
||||
}
|
||||
{isAdmin && !userBlocked && deleted && <span className="comment__status">Deleted</span>}
|
||||
|
||||
{
|
||||
!mods.disabled && mods.view !== 'user' && (
|
||||
{!mods.disabled &&
|
||||
mods.view !== 'user' && (
|
||||
<span
|
||||
className={b('comment__action', {}, { type: 'collapse', selected: mods.collapsed })}
|
||||
tabIndex="0"
|
||||
onClick={this.toggleCollapse}
|
||||
>{mods.collapsed ? '+' : '−'}</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
{mods.collapsed ? '+' : '−'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<span className={b('comment__score', {}, { view: o.score.view })}>
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'up', selected: scoreIncreased, disabled: isGuest || isCurrentUser })}
|
||||
className={b(
|
||||
'comment__vote',
|
||||
{},
|
||||
{ type: 'up', selected: scoreIncreased, disabled: isGuest || isCurrentUser }
|
||||
)}
|
||||
role="button"
|
||||
aria-disabled={isGuest || isCurrentUser}
|
||||
tabIndex="0"
|
||||
onClick={isGuest || isCurrentUser ? null : this.increaseScore}
|
||||
title={isGuest ? 'Only authorized users are allowed to vote' : (isCurrentUser ? 'You can\'t vote for your own comment' : null)}
|
||||
>Vote up</span>
|
||||
|
||||
<span className="comment__score-value">
|
||||
{o.score.sign}{o.score.value}
|
||||
title={
|
||||
isGuest
|
||||
? 'Only authorized users are allowed to vote'
|
||||
: isCurrentUser
|
||||
? "You can't vote for your own comment"
|
||||
: null
|
||||
}
|
||||
>
|
||||
Vote up
|
||||
</span>
|
||||
|
||||
<span className="comment__score-value">
|
||||
{o.score.sign}
|
||||
{o.score.value}
|
||||
</span>
|
||||
|
||||
<span
|
||||
className={b('comment__vote', {}, { type: 'down', selected: scoreDecreased, disabled: isGuest || isCurrentUser })}
|
||||
className={b(
|
||||
'comment__vote',
|
||||
{},
|
||||
{ type: 'down', selected: scoreDecreased, disabled: isGuest || isCurrentUser }
|
||||
)}
|
||||
role="button"
|
||||
aria-disabled={isGuest || isCurrentUser ? 'true' : 'false'}
|
||||
tabIndex="0"
|
||||
onClick={isGuest || isCurrentUser ? null : this.decreaseScore}
|
||||
title={isGuest ? 'Only authorized users are allowed to vote' : (isCurrentUser ? 'You can\'t vote for your own comment' : null)}
|
||||
>Vote down</span>
|
||||
title={
|
||||
isGuest
|
||||
? 'Only authorized users are allowed to vote'
|
||||
: isCurrentUser
|
||||
? "You can't vote for your own comment"
|
||||
: null
|
||||
}
|
||||
>
|
||||
Vote down
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={b('comment__text', { mix: 'raw-content' })}
|
||||
dangerouslySetInnerHTML={{ __html: o.text }}
|
||||
/>
|
||||
<div className={b('comment__text', { mix: 'raw-content' })} dangerouslySetInnerHTML={{ __html: o.text }} />
|
||||
|
||||
<div className="comment__actions">
|
||||
{
|
||||
!deleted && !mods.disabled && !isGuest && mods.view !== 'user' && (
|
||||
<span
|
||||
className="comment__action"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.toggleReplying}
|
||||
>{isReplying ? 'Cancel' : 'Reply'}</span>
|
||||
)
|
||||
}
|
||||
{!deleted &&
|
||||
!mods.disabled &&
|
||||
!isGuest &&
|
||||
mods.view !== 'user' && (
|
||||
<span className="comment__action" role="button" tabIndex="0" onClick={this.toggleReplying}>
|
||||
{isReplying ? 'Cancel' : 'Reply'}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{
|
||||
!deleted && !mods.disabled && !!o.orig && isCurrentUser && (!!editTimeLeft || isEditing) && mods.view !== 'user' && (
|
||||
{!deleted &&
|
||||
!mods.disabled &&
|
||||
!!o.orig &&
|
||||
isCurrentUser &&
|
||||
(!!editTimeLeft || isEditing) &&
|
||||
mods.view !== 'user' && (
|
||||
<span
|
||||
className="comment__action comment__action_type_edit"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.toggleEditing}
|
||||
>{isEditing ? 'Cancel' : 'Edit'}{editTimeLeft && ` (${editTimeLeft})`}</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!deleted && isAdmin && (
|
||||
<span className="comment__controls">
|
||||
{
|
||||
!pinned && (
|
||||
<span
|
||||
className="comment__control"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.onPinClick}
|
||||
>Pin</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
pinned && (
|
||||
<span
|
||||
className="comment__control"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.onUnpinClick}
|
||||
>Unpin</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
userBlocked && (
|
||||
<span
|
||||
className="comment__control"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.onUnblockClick}
|
||||
>Unblock</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!userBlocked && (
|
||||
<span
|
||||
className="comment__control"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.onBlockClick}
|
||||
>Block</span>
|
||||
)
|
||||
}
|
||||
|
||||
{
|
||||
!deleted && (
|
||||
<span
|
||||
className="comment__control"
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
onClick={this.onDeleteClick}
|
||||
>Delete</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
{isEditing ? 'Cancel' : 'Edit'}
|
||||
{editTimeLeft && ` (${editTimeLeft})`}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
)}
|
||||
|
||||
{!deleted &&
|
||||
isAdmin && (
|
||||
<span className="comment__controls">
|
||||
{!pinned && (
|
||||
<span className="comment__control" role="button" tabIndex="0" onClick={this.onPinClick}>
|
||||
Pin
|
||||
</span>
|
||||
)}
|
||||
|
||||
{pinned && (
|
||||
<span className="comment__control" role="button" tabIndex="0" onClick={this.onUnpinClick}>
|
||||
Unpin
|
||||
</span>
|
||||
)}
|
||||
|
||||
{userBlocked && (
|
||||
<span className="comment__control" role="button" tabIndex="0" onClick={this.onUnblockClick}>
|
||||
Unblock
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!userBlocked && (
|
||||
<span className="comment__control" role="button" tabIndex="0" onClick={this.onBlockClick}>
|
||||
Block
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!deleted && (
|
||||
<span className="comment__control" role="button" tabIndex="0" onClick={this.onDeleteClick}>
|
||||
Delete
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
isUserInfoShown && (
|
||||
<UserInfo mix="comment__user-info" user={o.user} onClose={this.toggleUserInfoVisibility}/>
|
||||
)
|
||||
}
|
||||
{isUserInfoShown && <UserInfo mix="comment__user-info" user={o.user} onClose={this.toggleUserInfoVisibility} />}
|
||||
|
||||
{
|
||||
isReplying && mods.view !== 'user' && (
|
||||
<Input
|
||||
mix="comment__input"
|
||||
onSubmit={this.onReply}
|
||||
onCancel={this.toggleReplying}
|
||||
pid={o.id}
|
||||
autoFocus
|
||||
/>
|
||||
)
|
||||
}
|
||||
{isReplying &&
|
||||
mods.view !== 'user' && (
|
||||
<Input mix="comment__input" onSubmit={this.onReply} onCancel={this.toggleReplying} pid={o.id} autoFocus />
|
||||
)}
|
||||
|
||||
{
|
||||
isEditing && mods.view !== 'user' && (
|
||||
{isEditing &&
|
||||
mods.view !== 'user' && (
|
||||
<Input
|
||||
mix="comment__input"
|
||||
mods={{ mode: 'edit' }}
|
||||
@@ -613,8 +602,7 @@ export default class Comment extends Component {
|
||||
errorMessage={!editTimeLeft && 'Editing time has expired.'}
|
||||
autoFocus
|
||||
/>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</article>
|
||||
);
|
||||
}
|
||||
@@ -628,7 +616,7 @@ function getTextSnippet(html) {
|
||||
const result = tmp.innerText || '';
|
||||
const snippet = result.substr(0, LENGTH);
|
||||
|
||||
return (snippet.length === LENGTH && result.length !== LENGTH) ? `${snippet}...` : snippet;
|
||||
return snippet.length === LENGTH && result.length !== LENGTH ? `${snippet}...` : snippet;
|
||||
}
|
||||
|
||||
function formatTime(time) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { BASE_URL, API_BASE, DEFAULT_MAX_COMMENT_SIZE } from 'common/constants';
|
||||
@@ -44,7 +45,7 @@ export default class Input extends Component {
|
||||
}
|
||||
|
||||
store.onUpdate('config', config => {
|
||||
this.setState({ maxLength: config && config.max_comment_size || DEFAULT_MAX_COMMENT_SIZE });
|
||||
this.setState({ maxLength: (config && config.max_comment_size) || DEFAULT_MAX_COMMENT_SIZE });
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,11 +54,13 @@ export default class Input extends Component {
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextProps.id !== this.props.id
|
||||
|| nextProps.pid !== this.props.pid
|
||||
|| nextProps.value !== this.props.value
|
||||
|| nextProps.errorMessage !== this.props.errorMessage
|
||||
|| nextState !== this.state;
|
||||
return (
|
||||
nextProps.id !== this.props.id ||
|
||||
nextProps.pid !== this.props.pid ||
|
||||
nextProps.value !== this.props.value ||
|
||||
nextProps.errorMessage !== this.props.errorMessage ||
|
||||
nextState !== this.state
|
||||
);
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
@@ -92,9 +95,8 @@ export default class Input extends Component {
|
||||
|
||||
this.setState({ isDisabled: true, isErrorShown: false });
|
||||
|
||||
const request = mods.mode === 'edit'
|
||||
? api.updateComment({ text, id })
|
||||
: api.addComment({ text, ...(pid ? { pid } : {}) });
|
||||
const request =
|
||||
mods.mode === 'edit' ? api.updateComment({ text, id }) : api.addComment({ text, ...(pid ? { pid } : {}) });
|
||||
|
||||
request
|
||||
.then(comment => {
|
||||
@@ -119,7 +121,8 @@ export default class Input extends Component {
|
||||
|
||||
this.setState({ isErrorShown: false });
|
||||
|
||||
api.getPreview({ text })
|
||||
api
|
||||
.getPreview({ text })
|
||||
.then(preview => this.setState({ preview }))
|
||||
.catch(() => {
|
||||
this.setState({ isErrorShown: true });
|
||||
@@ -144,20 +147,14 @@ export default class Input extends Component {
|
||||
disabled={isDisabled}
|
||||
/>
|
||||
|
||||
{
|
||||
(charactersLeft < 100) && (
|
||||
<span className="input__counter">{charactersLeft}</span>
|
||||
)
|
||||
}
|
||||
{charactersLeft < 100 && <span className="input__counter">{charactersLeft}</span>}
|
||||
</div>
|
||||
|
||||
{
|
||||
(isErrorShown || !!errorMessage) && (
|
||||
<p className="input__error" role="alert">
|
||||
{errorMessage || 'Something went wrong. Please try again a bit later.'}
|
||||
</p>
|
||||
)
|
||||
}
|
||||
{(isErrorShown || !!errorMessage) && (
|
||||
<p className="input__error" role="alert">
|
||||
{errorMessage || 'Something went wrong. Please try again a bit later.'}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<div className="input__actions">
|
||||
<button
|
||||
@@ -165,42 +162,39 @@ export default class Input extends Component {
|
||||
type="button"
|
||||
disabled={isDisabled}
|
||||
onClick={this.getPreview}
|
||||
>Preview</button>
|
||||
>
|
||||
Preview
|
||||
</button>
|
||||
|
||||
<button
|
||||
className={b('input__button', {}, { type: 'send' })}
|
||||
type="submit"
|
||||
disabled={isDisabled}
|
||||
>Send</button>
|
||||
<button className={b('input__button', {}, { type: 'send' })} type="submit" disabled={isDisabled}>
|
||||
Send
|
||||
</button>
|
||||
|
||||
{
|
||||
mods.type === 'main' && (
|
||||
<div className="input__rss">
|
||||
Subscribe to this
|
||||
{' '}
|
||||
<a className="input__rss-link" href={RSS_THREAD_URL} target="_blank">Thread</a>
|
||||
{' '}
|
||||
or
|
||||
<a className="input__rss-link" href={RSS_SITE_URL} target="_blank">Site</a>
|
||||
{' '}
|
||||
by RSS
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{mods.type === 'main' && (
|
||||
<div className="input__rss">
|
||||
Subscribe to this{' '}
|
||||
<a className="input__rss-link" href={RSS_THREAD_URL} target="_blank">
|
||||
Thread
|
||||
</a>{' '}
|
||||
or
|
||||
<a className="input__rss-link" href={RSS_SITE_URL} target="_blank">
|
||||
Site
|
||||
</a>{' '}
|
||||
by RSS
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{
|
||||
// TODO: it can be more elegant;
|
||||
// for example it can render full comment component here (or above textarea on mobile)
|
||||
!!preview && (
|
||||
<div className="input__preview-wrapper">
|
||||
<div
|
||||
className={b('input__preview', { mix: 'raw-content' })}
|
||||
dangerouslySetInnerHTML={{ __html: preview }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{// TODO: it can be more elegant;
|
||||
// for example it can render full comment component here (or above textarea on mobile)
|
||||
!!preview && (
|
||||
<div className="input__preview-wrapper">
|
||||
<div
|
||||
className={b('input__preview', { mix: 'raw-content' })}
|
||||
dangerouslySetInnerHTML={{ __html: preview }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { NODE_ID } from 'common/constants';
|
||||
@@ -9,15 +10,9 @@ export default class ListComments extends Component {
|
||||
return (
|
||||
<div id={NODE_ID}>
|
||||
<div className="list-comments">
|
||||
{
|
||||
comments.map(comment => (
|
||||
<Comment
|
||||
data={comment}
|
||||
mods={{ level: 0, guest: true, view: 'preview' }}
|
||||
mix="list-comments__item"
|
||||
/>
|
||||
))
|
||||
}
|
||||
{comments.map(comment => (
|
||||
<Comment data={comment} mods={{ level: 0, guest: true, view: 'preview' }} mix="list-comments__item" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
/** @jsx h */
|
||||
import { h, render } from 'preact';
|
||||
import Preloader from '../preloader';
|
||||
import {createDomContainer} from 'testUtils';
|
||||
import { createDomContainer } from 'testUtils';
|
||||
|
||||
describe(`<Preloader />`, () => {
|
||||
let container;
|
||||
|
||||
createDomContainer(({domContainer}) => {
|
||||
createDomContainer(({ domContainer }) => {
|
||||
container = domContainer;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
export default class Preloader extends Component {
|
||||
render(props) {
|
||||
return (
|
||||
<div className={b('preloader', props)}>
|
||||
<div className="preloader__bounce"/>
|
||||
<div className="preloader__bounce"/>
|
||||
<div className="preloader__bounce"/>
|
||||
<div className="preloader__bounce" />
|
||||
<div className="preloader__bounce" />
|
||||
<div className="preloader__bounce" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
import api from 'common/api';
|
||||
|
||||
import { BASE_URL, NODE_ID, COMMENT_NODE_CLASSNAME_PREFIX, DEFAULT_SORT, LS_SORT_KEY, MAX_SHOWN_ROOT_COMMENTS } from 'common/constants';
|
||||
import {
|
||||
BASE_URL,
|
||||
NODE_ID,
|
||||
COMMENT_NODE_CLASSNAME_PREFIX,
|
||||
DEFAULT_SORT,
|
||||
LS_SORT_KEY,
|
||||
MAX_SHOWN_ROOT_COMMENTS,
|
||||
} from 'common/constants';
|
||||
import { siteId, url, maxShownComments } from 'common/settings';
|
||||
import store from 'common/store';
|
||||
|
||||
@@ -59,10 +67,12 @@ export default class Root extends Component {
|
||||
});
|
||||
|
||||
Promise.all([
|
||||
api.getUser()
|
||||
api
|
||||
.getUser()
|
||||
.then(data => store.set('user', data))
|
||||
.catch(() => store.set('user', {})),
|
||||
api.getPostComments({ sort, url })
|
||||
api
|
||||
.getPostComments({ sort, url })
|
||||
.then(({ comments = [] } = {}) => store.set('comments', comments))
|
||||
.catch(() => store.set('comments', [])),
|
||||
]).finally(() => {
|
||||
@@ -100,7 +110,9 @@ export default class Root extends Component {
|
||||
}
|
||||
|
||||
onSignIn(provider) {
|
||||
const newWindow = window.open(`${BASE_URL}/auth/${provider}/login?from=${encodeURIComponent(location.href)}&site=${siteId}`);
|
||||
const newWindow = window.open(
|
||||
`${BASE_URL}/auth/${provider}/login?from=${encodeURIComponent(location.href)}&site=${siteId}`
|
||||
);
|
||||
|
||||
let secondsPass = 0;
|
||||
const checkMsDelay = 100;
|
||||
@@ -112,7 +124,8 @@ export default class Root extends Component {
|
||||
secondsPass = 0;
|
||||
newWindow.close();
|
||||
|
||||
api.getUser()
|
||||
api
|
||||
.getUser()
|
||||
.then(user => {
|
||||
store.set('user', user);
|
||||
this.setState({ user });
|
||||
@@ -153,7 +166,8 @@ export default class Root extends Component {
|
||||
// can't save; ignore it
|
||||
}
|
||||
|
||||
api.getPostComments({ sort, url })
|
||||
api
|
||||
.getPostComments({ sort, url })
|
||||
.then(({ comments } = {}) => store.set('comments', comments))
|
||||
.finally(() => {
|
||||
this.setState({ isCommentsListLoading: false });
|
||||
@@ -178,12 +192,25 @@ export default class Root extends Component {
|
||||
});
|
||||
}
|
||||
|
||||
render(props, { config = {}, comments = [], user, sort, isLoaded, isBlockedVisible, isCommentsListLoading, bannedUsers, commentsShown }) {
|
||||
render(
|
||||
props,
|
||||
{
|
||||
config = {},
|
||||
comments = [],
|
||||
user,
|
||||
sort,
|
||||
isLoaded,
|
||||
isBlockedVisible,
|
||||
isCommentsListLoading,
|
||||
bannedUsers,
|
||||
commentsShown,
|
||||
}
|
||||
) {
|
||||
if (!isLoaded) {
|
||||
return (
|
||||
<div id={NODE_ID}>
|
||||
<div className="root">
|
||||
<Preloader mix="root__preloader"/>
|
||||
<Preloader mix="root__preloader" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -207,84 +234,60 @@ export default class Root extends Component {
|
||||
onSortChange={this.onSortChange}
|
||||
/>
|
||||
|
||||
{
|
||||
!isBlockedVisible && (
|
||||
<div className="root__main">
|
||||
{
|
||||
!isGuest && (
|
||||
<Input
|
||||
mix="root__input"
|
||||
mods={{ type: 'main' }}
|
||||
onSubmit={this.addComment}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{!isBlockedVisible && (
|
||||
<div className="root__main">
|
||||
{!isGuest && <Input mix="root__input" mods={{ type: 'main' }} onSubmit={this.addComment} />}
|
||||
|
||||
{
|
||||
!!pinnedComments.length && (
|
||||
<div className="root__pinned-comments" role="region" aria-label="Pinned comments">
|
||||
{
|
||||
pinnedComments.map(comment => (
|
||||
<Comment
|
||||
data={comment}
|
||||
mods={{ level: 0, disabled: true }}
|
||||
mix="root__pinned-comment"
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{!!pinnedComments.length && (
|
||||
<div className="root__pinned-comments" role="region" aria-label="Pinned comments">
|
||||
{pinnedComments.map(comment => (
|
||||
<Comment data={comment} mods={{ level: 0, disabled: true }} mix="root__pinned-comment" />
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
!!comments.length && !isCommentsListLoading && (
|
||||
<div className="root__threads" role="list">
|
||||
{
|
||||
(IS_MOBILE ? comments.slice(0, commentsShown) : comments).map(thread => (
|
||||
<Thread
|
||||
key={thread.comment.id}
|
||||
mix="root__thread"
|
||||
mods={{ level: 0 }}
|
||||
data={thread}
|
||||
onReply={this.addComment}
|
||||
onEdit={this.replaceComment}
|
||||
/>
|
||||
))
|
||||
}
|
||||
{!!comments.length &&
|
||||
!isCommentsListLoading && (
|
||||
<div className="root__threads" role="list">
|
||||
{(IS_MOBILE ? comments.slice(0, commentsShown) : comments).map(thread => (
|
||||
<Thread
|
||||
key={thread.comment.id}
|
||||
mix="root__thread"
|
||||
mods={{ level: 0 }}
|
||||
data={thread}
|
||||
onReply={this.addComment}
|
||||
onEdit={this.replaceComment}
|
||||
/>
|
||||
))}
|
||||
|
||||
{
|
||||
commentsShown < comments.length && IS_MOBILE && (
|
||||
<button
|
||||
className="root__show-more"
|
||||
onClick={this.showMore}
|
||||
>Show more</button>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{commentsShown < comments.length &&
|
||||
IS_MOBILE && (
|
||||
<button className="root__show-more" onClick={this.showMore}>
|
||||
Show more
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
isCommentsListLoading && (
|
||||
<div className="root__threads" role="list">
|
||||
<Preloader mix="root__preloader"/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{isCommentsListLoading && (
|
||||
<div className="root__threads" role="list">
|
||||
<Preloader mix="root__preloader" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{
|
||||
isBlockedVisible && (
|
||||
<div className="root__main">
|
||||
<BlockedUsers users={bannedUsers} onUnblock={this.onUnblockSomeone}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{isBlockedVisible && (
|
||||
<div className="root__main">
|
||||
<BlockedUsers users={bannedUsers} onUnblock={this.onUnblockSomeone} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<p className="root__copyright" role="contentinfo">
|
||||
Powered by <a href="https://remark42.com/" className="root__copyright-link">Remark42</a>
|
||||
Powered by{' '}
|
||||
<a href="https://remark42.com/" className="root__copyright-link">
|
||||
Remark42
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import { LS_COLLAPSE_KEY } from 'common/constants';
|
||||
@@ -30,8 +31,9 @@ export default class Thread extends Component {
|
||||
this.lsCollapsedID = `${siteId}_${url}_${comment.id}`;
|
||||
|
||||
this.state = {
|
||||
collapsed: !this.state.isCollapsedChanged && score <= config.critical_score
|
||||
|| getCollapsedComments().includes(this.lsCollapsedID),
|
||||
collapsed:
|
||||
(!this.state.isCollapsedChanged && score <= config.critical_score) ||
|
||||
getCollapsedComments().includes(this.lsCollapsedID),
|
||||
isCollapsedChanged: true,
|
||||
};
|
||||
}
|
||||
@@ -55,7 +57,10 @@ export default class Thread extends Component {
|
||||
}
|
||||
|
||||
render(props, { collapsed }) {
|
||||
const { data: { comment, replies = [] }, mods = {} } = props;
|
||||
const {
|
||||
data: { comment, replies = [] },
|
||||
mods = {},
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -71,8 +76,9 @@ export default class Thread extends Component {
|
||||
onCollapseToggle={this.onCollapseToggle}
|
||||
/>
|
||||
|
||||
{
|
||||
!collapsed && !!replies.length && replies.map(thread => (
|
||||
{!collapsed &&
|
||||
!!replies.length &&
|
||||
replies.map(thread => (
|
||||
<Thread
|
||||
key={thread.comment.id}
|
||||
data={thread}
|
||||
@@ -80,8 +86,7 @@ export default class Thread extends Component {
|
||||
onReply={props.onReply}
|
||||
onEdit={props.onEdit}
|
||||
/>
|
||||
))
|
||||
}
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/** @jsx h */
|
||||
import { h, Component } from 'preact';
|
||||
|
||||
import api from 'common/api';
|
||||
@@ -16,43 +17,38 @@ export default class UserInfo extends Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
const { user: { id } } = this.props;
|
||||
const {
|
||||
user: { id },
|
||||
} = this.props;
|
||||
|
||||
api.getUserComments({ user: id, limit: 10 })
|
||||
api
|
||||
.getUserComments({ user: id, limit: 10 })
|
||||
.then(({ comments = [] }) => this.setState({ comments }))
|
||||
.finally(() => this.setState({ isLoading: false }));
|
||||
}
|
||||
|
||||
render(props, { comments, isLoading }) {
|
||||
const { user: { name, id }, onClose } = props;
|
||||
const {
|
||||
user: { name, id },
|
||||
onClose,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className={b('user-info', props)}>
|
||||
<p className="user-info__title">Last comments by {name}</p>
|
||||
<p className="user-info__id">{id}</p>
|
||||
|
||||
{
|
||||
isLoading && (
|
||||
<Preloader mix="user-info__preloader"/>
|
||||
)
|
||||
}
|
||||
{isLoading && <Preloader mix="user-info__preloader" />}
|
||||
|
||||
{
|
||||
!isLoading && (
|
||||
<div>
|
||||
{
|
||||
comments.map(comment => (
|
||||
<Comment
|
||||
data={comment}
|
||||
mods={{ level: 0, view: 'user' }}
|
||||
/>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{!isLoading && (
|
||||
<div>{comments.map(comment => <Comment data={comment} mods={{ level: 0, view: 'user' }} />)}</div>
|
||||
)}
|
||||
|
||||
<div><span className="user-info__close" onClick={onClose}>Close</span></div>
|
||||
<div>
|
||||
<span className="user-info__close" onClick={onClose}>
|
||||
Close
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+10
-11
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
/** @jsx h */
|
||||
import { h, render } from 'preact';
|
||||
|
||||
import { BASE_URL, DEFAULT_LAST_COMMENTS_MAX, LAST_COMMENTS_NODE_CLASSNAME } from './common/constants';
|
||||
@@ -17,7 +18,7 @@ function init() {
|
||||
const nodes = document.getElementsByClassName(LAST_COMMENTS_NODE_CLASSNAME);
|
||||
|
||||
if (!nodes) {
|
||||
console.error('Remark42: Can\'t find last comments nodes.');
|
||||
console.error("Remark42: Can't find last comments nodes.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,15 +41,13 @@ function init() {
|
||||
|
||||
[].slice.call(nodes).forEach(node => {
|
||||
const max = node.dataset.max || remark_config.max_last_comments || DEFAULT_LAST_COMMENTS_MAX;
|
||||
api.getLastComments({ max, siteId: remark_config.site_id })
|
||||
.then(comments => {
|
||||
try {
|
||||
render(<ListComments comments={comments}/>, node);
|
||||
} catch (e) {
|
||||
console.error('Remark42: Something went wrong with last comments rendering');
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
api.getLastComments({ max, siteId: remark_config.site_id }).then(comments => {
|
||||
try {
|
||||
render(<ListComments comments={comments} />, node);
|
||||
} catch (e) {
|
||||
console.error('Remark42: Something went wrong with last comments rendering');
|
||||
console.error(e);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -1,4 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
/** @jsx h */
|
||||
import loadPolyfills from 'common/polyfills';
|
||||
|
||||
import { h, render } from 'preact';
|
||||
@@ -20,9 +21,9 @@ function init() {
|
||||
const node = document.getElementById(NODE_ID);
|
||||
|
||||
if (!node) {
|
||||
console.error('Remark42: Can\'t find root node.');
|
||||
console.error("Remark42: Can't find root node.");
|
||||
return;
|
||||
}
|
||||
|
||||
render(<Root/>, node.parentElement, node);
|
||||
render(<Root />, node.parentElement, node);
|
||||
}
|
||||
|
||||
Generated
+3134
-3133
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user