set prettier rules to warn and fix jsx pragma

This commit is contained in:
Aleksey Gurianov
2018-07-06 19:05:35 +04:00
committed by Aleksei Gurianov
parent dec8bb4fc9
commit 9949632cd7
15 changed files with 3620 additions and 3653 deletions
+1 -1
View File
@@ -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');
});
});
});
+51 -54
View File
@@ -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>
);
}
+211 -223
View File
@@ -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) {
+48 -54
View File
@@ -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&nbsp;this
{' '}
<a className="input__rss-link" href={RSS_THREAD_URL} target="_blank">Thread</a>
{' '}
or&nbsp;
<a className="input__rss-link" href={RSS_SITE_URL} target="_blank">Site</a>
{' '}
by&nbsp;RSS
</div>
)
}
{mods.type === 'main' && (
<div className="input__rss">
Subscribe to&nbsp;this{' '}
<a className="input__rss-link" href={RSS_THREAD_URL} target="_blank">
Thread
</a>{' '}
or&nbsp;
<a className="input__rss-link" href={RSS_SITE_URL} target="_blank">
Site
</a>{' '}
by&nbsp;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;
});
+4 -3
View File
@@ -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>
);
}
+82 -79
View File
@@ -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>
+12 -7
View File
@@ -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>
);
}
+19 -23
View File
@@ -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
View File
@@ -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
View File
@@ -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);
}
+3134 -3133
View File
File diff suppressed because it is too large Load Diff