diff options
Diffstat (limited to 'app/javascript/mastodon/components')
-rw-r--r-- | app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap (renamed from app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx (renamed from app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/avatar-test.jsx (renamed from app/javascript/mastodon/components/__tests__/avatar-test.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx (renamed from app/javascript/mastodon/components/__tests__/avatar_overlay-test.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/button-test.jsx (renamed from app/javascript/mastodon/components/__tests__/button-test.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/__tests__/display_name-test.jsx (renamed from app/javascript/mastodon/components/__tests__/display_name-test.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/account.jsx (renamed from app/javascript/mastodon/components/account.js) | 96 | ||||
-rw-r--r-- | app/javascript/mastodon/components/admin/Counter.jsx (renamed from app/javascript/mastodon/components/admin/Counter.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/admin/Dimension.jsx (renamed from app/javascript/mastodon/components/admin/Dimension.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/admin/ReportReasonSelector.jsx (renamed from app/javascript/mastodon/components/admin/ReportReasonSelector.js) | 7 | ||||
-rw-r--r-- | app/javascript/mastodon/components/admin/Retention.jsx (renamed from app/javascript/mastodon/components/admin/Retention.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/admin/Trends.jsx (renamed from app/javascript/mastodon/components/admin/Trends.js) | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/animated_number.jsx (renamed from app/javascript/mastodon/components/animated_number.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/attachment_list.jsx (renamed from app/javascript/mastodon/components/attachment_list.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/autosuggest_emoji.jsx (renamed from app/javascript/mastodon/components/autosuggest_emoji.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/autosuggest_hashtag.jsx (renamed from app/javascript/mastodon/components/autosuggest_hashtag.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/autosuggest_input.jsx (renamed from app/javascript/mastodon/components/autosuggest_input.js) | 22 | ||||
-rw-r--r-- | app/javascript/mastodon/components/autosuggest_textarea.jsx (renamed from app/javascript/mastodon/components/autosuggest_textarea.js) | 22 | ||||
-rw-r--r-- | app/javascript/mastodon/components/avatar.js | 62 | ||||
-rw-r--r-- | app/javascript/mastodon/components/avatar.tsx | 49 | ||||
-rw-r--r-- | app/javascript/mastodon/components/avatar_composite.jsx (renamed from app/javascript/mastodon/components/avatar_composite.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/avatar_overlay.jsx (renamed from app/javascript/mastodon/components/avatar_overlay.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/blurhash.jsx (renamed from app/javascript/mastodon/components/blurhash.js) | 1 | ||||
-rw-r--r-- | app/javascript/mastodon/components/button.jsx (renamed from app/javascript/mastodon/components/button.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/check.jsx (renamed from app/javascript/mastodon/components/check.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/column.jsx (renamed from app/javascript/mastodon/components/column.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/column_back_button.jsx (renamed from app/javascript/mastodon/components/column_back_button.js) | 8 | ||||
-rw-r--r-- | app/javascript/mastodon/components/column_back_button_slim.jsx (renamed from app/javascript/mastodon/components/column_back_button_slim.js) | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/column_header.jsx (renamed from app/javascript/mastodon/components/column_header.js) | 31 | ||||
-rw-r--r-- | app/javascript/mastodon/components/common_counter.jsx (renamed from app/javascript/mastodon/components/common_counter.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/dismissable_banner.jsx (renamed from app/javascript/mastodon/components/dismissable_banner.js) | 5 | ||||
-rw-r--r-- | app/javascript/mastodon/components/display_name.jsx (renamed from app/javascript/mastodon/components/display_name.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/domain.jsx (renamed from app/javascript/mastodon/components/domain.js) | 5 | ||||
-rw-r--r-- | app/javascript/mastodon/components/dropdown_menu.jsx (renamed from app/javascript/mastodon/components/dropdown_menu.js) | 36 | ||||
-rw-r--r-- | app/javascript/mastodon/components/edited_timestamp/index.jsx (renamed from app/javascript/mastodon/components/edited_timestamp/index.js) | 8 | ||||
-rw-r--r-- | app/javascript/mastodon/components/error_boundary.jsx (renamed from app/javascript/mastodon/components/error_boundary.js) | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/gifv.jsx (renamed from app/javascript/mastodon/components/gifv.js) | 13 | ||||
-rw-r--r-- | app/javascript/mastodon/components/hashtag.jsx (renamed from app/javascript/mastodon/components/hashtag.js) | 9 | ||||
-rw-r--r-- | app/javascript/mastodon/components/icon.jsx (renamed from app/javascript/mastodon/components/icon.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/icon_button.jsx (renamed from app/javascript/mastodon/components/icon_button.js) | 14 | ||||
-rw-r--r-- | app/javascript/mastodon/components/icon_with_badge.jsx (renamed from app/javascript/mastodon/components/icon_with_badge.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/image.jsx (renamed from app/javascript/mastodon/components/image.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/inline_account.jsx (renamed from app/javascript/mastodon/components/inline_account.js) | 3 | ||||
-rw-r--r-- | app/javascript/mastodon/components/intersection_observer_article.jsx (renamed from app/javascript/mastodon/components/intersection_observer_article.js) | 16 | ||||
-rw-r--r-- | app/javascript/mastodon/components/load_gap.jsx (renamed from app/javascript/mastodon/components/load_gap.js) | 5 | ||||
-rw-r--r-- | app/javascript/mastodon/components/load_more.jsx (renamed from app/javascript/mastodon/components/load_more.js) | 4 | ||||
-rw-r--r-- | app/javascript/mastodon/components/load_pending.jsx (renamed from app/javascript/mastodon/components/load_pending.js) | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/loading_indicator.jsx (renamed from app/javascript/mastodon/components/loading_indicator.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/logo.jsx (renamed from app/javascript/mastodon/components/logo.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/media_attachments.jsx (renamed from app/javascript/mastodon/components/media_attachments.js) | 12 | ||||
-rw-r--r-- | app/javascript/mastodon/components/media_gallery.jsx (renamed from app/javascript/mastodon/components/media_gallery.js) | 32 | ||||
-rw-r--r-- | app/javascript/mastodon/components/missing_indicator.jsx (renamed from app/javascript/mastodon/components/missing_indicator.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/modal_root.jsx (renamed from app/javascript/mastodon/components/modal_root.js) | 8 | ||||
-rw-r--r-- | app/javascript/mastodon/components/navigation_portal.jsx (renamed from app/javascript/mastodon/components/navigation_portal.js) | 2 | ||||
-rw-r--r-- | app/javascript/mastodon/components/not_signed_in_indicator.jsx (renamed from app/javascript/mastodon/components/not_signed_in_indicator.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/picture_in_picture_placeholder.jsx (renamed from app/javascript/mastodon/components/picture_in_picture_placeholder.js) | 9 | ||||
-rw-r--r-- | app/javascript/mastodon/components/poll.jsx (renamed from app/javascript/mastodon/components/poll.js) | 14 | ||||
-rw-r--r-- | app/javascript/mastodon/components/radio_button.jsx (renamed from app/javascript/mastodon/components/radio_button.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/regeneration_indicator.jsx (renamed from app/javascript/mastodon/components/regeneration_indicator.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/relative_timestamp.jsx (renamed from app/javascript/mastodon/components/relative_timestamp.js) | 3 | ||||
-rw-r--r-- | app/javascript/mastodon/components/scrollable_list.jsx (renamed from app/javascript/mastodon/components/scrollable_list.js) | 27 | ||||
-rw-r--r-- | app/javascript/mastodon/components/server_banner.jsx (renamed from app/javascript/mastodon/components/server_banner.js) | 6 | ||||
-rw-r--r-- | app/javascript/mastodon/components/short_number.jsx (renamed from app/javascript/mastodon/components/short_number.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/skeleton.jsx (renamed from app/javascript/mastodon/components/skeleton.js) | 0 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status.jsx (renamed from app/javascript/mastodon/components/status.js) | 72 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status_action_bar.jsx (renamed from app/javascript/mastodon/components/status_action_bar.js) | 52 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status_content.jsx (renamed from app/javascript/mastodon/components/status_content.js) | 41 | ||||
-rw-r--r-- | app/javascript/mastodon/components/status_list.jsx (renamed from app/javascript/mastodon/components/status_list.js) | 12 | ||||
-rw-r--r-- | app/javascript/mastodon/components/timeline_hint.jsx (renamed from app/javascript/mastodon/components/timeline_hint.js) | 0 |
73 files changed, 410 insertions, 324 deletions
diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap index 1c3727848..1c3727848 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.js.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/autosuggest_emoji-test.jsx.snap diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap index 7fbdedeb2..7fbdedeb2 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.js.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar-test.jsx.snap diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap index f8385357a..f8385357a 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.js.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/avatar_overlay-test.jsx.snap diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap index bfc091d45..bfc091d45 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.js.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/button-test.jsx.snap diff --git a/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap b/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap index 9c37580d7..9c37580d7 100644 --- a/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.js.snap +++ b/app/javascript/mastodon/components/__tests__/__snapshots__/display_name-test.jsx.snap diff --git a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx index 05616e444..05616e444 100644 --- a/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.js +++ b/app/javascript/mastodon/components/__tests__/autosuggest_emoji-test.jsx diff --git a/app/javascript/mastodon/components/__tests__/avatar-test.js b/app/javascript/mastodon/components/__tests__/avatar-test.jsx index dd3f7b7d2..dd3f7b7d2 100644 --- a/app/javascript/mastodon/components/__tests__/avatar-test.js +++ b/app/javascript/mastodon/components/__tests__/avatar-test.jsx diff --git a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx index 44addea83..44addea83 100644 --- a/app/javascript/mastodon/components/__tests__/avatar_overlay-test.js +++ b/app/javascript/mastodon/components/__tests__/avatar_overlay-test.jsx diff --git a/app/javascript/mastodon/components/__tests__/button-test.js b/app/javascript/mastodon/components/__tests__/button-test.jsx index f5a649f70..f5a649f70 100644 --- a/app/javascript/mastodon/components/__tests__/button-test.js +++ b/app/javascript/mastodon/components/__tests__/button-test.jsx diff --git a/app/javascript/mastodon/components/__tests__/display_name-test.js b/app/javascript/mastodon/components/__tests__/display_name-test.jsx index 0d040c4cd..0d040c4cd 100644 --- a/app/javascript/mastodon/components/__tests__/display_name-test.js +++ b/app/javascript/mastodon/components/__tests__/display_name-test.jsx diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.jsx index 7aebb124c..a8a47ecac 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.jsx @@ -1,4 +1,4 @@ -import React, { Fragment } from 'react'; +import React from 'react'; import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import Avatar from './avatar'; @@ -10,6 +10,10 @@ import { me } from '../initial_state'; import RelativeTimestamp from './relative_timestamp'; import Skeleton from 'mastodon/components/skeleton'; import { Link } from 'react-router-dom'; +import { counterRenderer } from 'mastodon/components/common_counter'; +import ShortNumber from 'mastodon/components/short_number'; +import Icon from 'mastodon/components/icon'; +import classNames from 'classnames'; const messages = defineMessages({ follow: { id: 'account.follow', defaultMessage: 'Follow' }, @@ -23,7 +27,26 @@ const messages = defineMessages({ block: { id: 'account.block', defaultMessage: 'Block @{name}' }, }); -export default @injectIntl +class VerifiedBadge extends React.PureComponent { + + static propTypes = { + link: PropTypes.string.isRequired, + verifiedAt: PropTypes.string.isRequired, + }; + + render () { + const { link } = this.props; + + return ( + <span className='verified-badge'> + <Icon id='check' className='verified-badge__mark' /> + <span dangerouslySetInnerHTML={{ __html: link }} /> + </span> + ); + } + +} + class Account extends ImmutablePureComponent { static propTypes = { @@ -35,6 +58,7 @@ class Account extends ImmutablePureComponent { onMuteNotifications: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, hidden: PropTypes.bool, + minimal: PropTypes.bool, actionIcon: PropTypes.string, actionTitle: PropTypes.string, defaultAction: PropTypes.string, @@ -47,38 +71,42 @@ class Account extends ImmutablePureComponent { handleFollow = () => { this.props.onFollow(this.props.account); - } + }; handleBlock = () => { this.props.onBlock(this.props.account); - } + }; handleMute = () => { this.props.onMute(this.props.account); - } + }; handleMuteNotifications = () => { this.props.onMuteNotifications(this.props.account, true); - } + }; handleUnmuteNotifications = () => { this.props.onMuteNotifications(this.props.account, false); - } + }; handleAction = () => { this.props.onActionClick(this.props.account); - } + }; render () { - const { account, intl, hidden, onActionClick, actionIcon, actionTitle, defaultAction, size } = this.props; + const { account, intl, hidden, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal } = this.props; if (!account) { return ( - <div className='account'> + <div className={classNames('account', { 'account--minimal': minimal })}> <div className='account__wrapper'> <div className='account__display-name'> - <div className='account__avatar-wrapper'><Skeleton width={36} height={36} /></div> - <DisplayName /> + <div className='account__avatar-wrapper'><Skeleton width={size} height={size} /></div> + + <div> + <DisplayName /> + <Skeleton width='7ch' /> + </div> </div> </div> </div> @@ -87,10 +115,10 @@ class Account extends ImmutablePureComponent { if (hidden) { return ( - <Fragment> + <> {account.get('display_name')} {account.get('username')} - </Fragment> + </> ); } @@ -118,10 +146,10 @@ class Account extends ImmutablePureComponent { hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />; } buttons = ( - <Fragment> + <> <IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} /> {hidingNotificationsButton} - </Fragment> + </> ); } else if (defaultAction === 'mute') { buttons = <IconButton icon='volume-off' title={intl.formatMessage(messages.mute, { name: account.get('username') })} onClick={this.handleMute} />; @@ -132,26 +160,44 @@ class Account extends ImmutablePureComponent { } } - let mute_expires_at; + let muteTimeRemaining; + if (account.get('mute_expires_at')) { - mute_expires_at = <div><RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></div>; + muteTimeRemaining = <>· <RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></>; + } + + let verification; + + const firstVerifiedField = account.get('fields').find(item => !!item.get('verified_at')); + + if (firstVerifiedField) { + verification = <>· <VerifiedBadge link={firstVerifiedField.get('value')} verifiedAt={firstVerifiedField.get('verified_at')} /></>; } return ( - <div className='account'> + <div className={classNames('account', { 'account--minimal': minimal })}> <div className='account__wrapper'> <Link key={account.get('id')} className='account__display-name' title={account.get('acct')} to={`/@${account.get('acct')}`}> - <div className='account__avatar-wrapper'><Avatar account={account} size={size} /></div> - {mute_expires_at} - <DisplayName account={account} /> + <div className='account__avatar-wrapper'> + <Avatar account={account} size={size} /> + </div> + + <div> + <DisplayName account={account} /> + {!minimal && <><ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}</>} + </div> </Link> - <div className='account__relationship'> - {buttons} - </div> + {!minimal && ( + <div className='account__relationship'> + {buttons} + </div> + )} </div> </div> ); } } + +export default injectIntl(Account); diff --git a/app/javascript/mastodon/components/admin/Counter.js b/app/javascript/mastodon/components/admin/Counter.jsx index 5a5b2b869..5a5b2b869 100644 --- a/app/javascript/mastodon/components/admin/Counter.js +++ b/app/javascript/mastodon/components/admin/Counter.jsx diff --git a/app/javascript/mastodon/components/admin/Dimension.js b/app/javascript/mastodon/components/admin/Dimension.jsx index 977c8208d..977c8208d 100644 --- a/app/javascript/mastodon/components/admin/Dimension.js +++ b/app/javascript/mastodon/components/admin/Dimension.jsx diff --git a/app/javascript/mastodon/components/admin/ReportReasonSelector.js b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx index 1f91d2517..cd14dac4e 100644 --- a/app/javascript/mastodon/components/admin/ReportReasonSelector.js +++ b/app/javascript/mastodon/components/admin/ReportReasonSelector.jsx @@ -33,7 +33,7 @@ class Category extends React.PureComponent { const { id, text, disabled, selected, children } = this.props; return ( - <div tabIndex='0' role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}> + <div tabIndex={0} role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}> {selected && <input type='hidden' name='report[category]' value={id} />} <div className='report-reason-selector__category__label'> @@ -74,7 +74,7 @@ class Rule extends React.PureComponent { const { id, text, disabled, selected } = this.props; return ( - <div tabIndex='0' role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}> + <div tabIndex={0} role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}> <span className={classNames('poll__input', { checkbox: true, active: selected, disabled })} /> {selected && <input type='hidden' name='report[rule_ids][]' value={id} />} {text} @@ -84,7 +84,6 @@ class Rule extends React.PureComponent { } -export default @injectIntl class ReportReasonSelector extends React.PureComponent { static propTypes = { @@ -157,3 +156,5 @@ class ReportReasonSelector extends React.PureComponent { } } + +export default injectIntl(ReportReasonSelector); diff --git a/app/javascript/mastodon/components/admin/Retention.js b/app/javascript/mastodon/components/admin/Retention.jsx index f312a45eb..f312a45eb 100644 --- a/app/javascript/mastodon/components/admin/Retention.js +++ b/app/javascript/mastodon/components/admin/Retention.jsx diff --git a/app/javascript/mastodon/components/admin/Trends.js b/app/javascript/mastodon/components/admin/Trends.jsx index 9530c2a5b..d01b8437e 100644 --- a/app/javascript/mastodon/components/admin/Trends.js +++ b/app/javascript/mastodon/components/admin/Trends.jsx @@ -50,7 +50,7 @@ export default class Trends extends React.PureComponent { <Hashtag key={hashtag.name} name={hashtag.name} - to={`/admin/tags/${hashtag.id}`} + to={hashtag.id === undefined ? undefined : `/admin/tags/${hashtag.id}`} people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1} uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1} history={hashtag.history.reverse().map(day => day.uses)} diff --git a/app/javascript/mastodon/components/animated_number.js b/app/javascript/mastodon/components/animated_number.jsx index b1aebc73e..ce688f04f 100644 --- a/app/javascript/mastodon/components/animated_number.js +++ b/app/javascript/mastodon/components/animated_number.jsx @@ -38,13 +38,13 @@ export default class AnimatedNumber extends React.PureComponent { const { direction } = this.state; return { y: -1 * direction }; - } + }; willLeave = () => { const { direction } = this.state; return { y: spring(1 * direction, { damping: 35, stiffness: 400 }) }; - } + }; render () { const { value, obfuscate } = this.props; diff --git a/app/javascript/mastodon/components/attachment_list.js b/app/javascript/mastodon/components/attachment_list.jsx index 0e23889de..0e23889de 100644 --- a/app/javascript/mastodon/components/attachment_list.js +++ b/app/javascript/mastodon/components/attachment_list.jsx diff --git a/app/javascript/mastodon/components/autosuggest_emoji.js b/app/javascript/mastodon/components/autosuggest_emoji.jsx index 4937e4d98..4937e4d98 100644 --- a/app/javascript/mastodon/components/autosuggest_emoji.js +++ b/app/javascript/mastodon/components/autosuggest_emoji.jsx diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.js b/app/javascript/mastodon/components/autosuggest_hashtag.jsx index 9e9d888f8..9e9d888f8 100644 --- a/app/javascript/mastodon/components/autosuggest_hashtag.js +++ b/app/javascript/mastodon/components/autosuggest_hashtag.jsx diff --git a/app/javascript/mastodon/components/autosuggest_input.js b/app/javascript/mastodon/components/autosuggest_input.jsx index 12d44b5d0..a68e2a01b 100644 --- a/app/javascript/mastodon/components/autosuggest_input.js +++ b/app/javascript/mastodon/components/autosuggest_input.jsx @@ -50,6 +50,8 @@ export default class AutosuggestInput extends ImmutablePureComponent { id: PropTypes.string, searchTokens: PropTypes.arrayOf(PropTypes.string), maxLength: PropTypes.number, + lang: PropTypes.string, + spellCheck: PropTypes.bool, }; static defaultProps = { @@ -77,7 +79,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { } this.props.onChange(e); - } + }; onKeyDown = (e) => { const { suggestions, disabled } = this.props; @@ -135,22 +137,22 @@ export default class AutosuggestInput extends ImmutablePureComponent { } this.props.onKeyDown(e); - } + }; onBlur = () => { this.setState({ suggestionsHidden: true, focused: false }); - } + }; onFocus = () => { this.setState({ focused: true }); - } + }; onSuggestionClick = (e) => { const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index')); e.preventDefault(); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.input.focus(); - } + }; componentWillReceiveProps (nextProps) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { @@ -160,7 +162,7 @@ export default class AutosuggestInput extends ImmutablePureComponent { setInput = (c) => { this.input = c; - } + }; renderSuggestion = (suggestion, i) => { const { selectedSuggestion } = this.state; @@ -178,14 +180,14 @@ export default class AutosuggestInput extends ImmutablePureComponent { } return ( - <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}> + <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}> {inner} </div> ); - } + }; render () { - const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, className, id, maxLength, lang, spellCheck } = this.props; const { suggestionsHidden } = this.state; return ( @@ -210,6 +212,8 @@ export default class AutosuggestInput extends ImmutablePureComponent { id={id} className={className} maxLength={maxLength} + lang={lang} + spellCheck={spellCheck} /> </label> diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.jsx index 08b9cd80b..a627bc1ec 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.jsx @@ -48,6 +48,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { onKeyDown: PropTypes.func, onPaste: PropTypes.func.isRequired, autoFocus: PropTypes.bool, + lang: PropTypes.string, }; static defaultProps = { @@ -74,7 +75,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } this.props.onChange(e); - } + }; onKeyDown = (e) => { const { suggestions, disabled } = this.props; @@ -132,25 +133,25 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } this.props.onKeyDown(e); - } + }; onBlur = () => { this.setState({ suggestionsHidden: true, focused: false }); - } + }; onFocus = (e) => { this.setState({ focused: true }); if (this.props.onFocus) { this.props.onFocus(e); } - } + }; onSuggestionClick = (e) => { const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index')); e.preventDefault(); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.textarea.focus(); - } + }; componentWillReceiveProps (nextProps) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden && this.state.focused) { @@ -160,14 +161,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { setTextarea = (c) => { this.textarea = c; - } + }; onPaste = (e) => { if (e.clipboardData && e.clipboardData.files.length === 1) { this.props.onPaste(e.clipboardData.files); e.preventDefault(); } - } + }; renderSuggestion = (suggestion, i) => { const { selectedSuggestion } = this.state; @@ -185,14 +186,14 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } return ( - <div role='button' tabIndex='0' key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}> + <div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}> {inner} </div> ); - } + }; render () { - const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, lang, children } = this.props; const { suggestionsHidden } = this.state; return [ @@ -216,6 +217,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { onPaste={this.onPaste} dir='auto' aria-autocomplete='list' + lang={lang} /> </label> </div> diff --git a/app/javascript/mastodon/components/avatar.js b/app/javascript/mastodon/components/avatar.js deleted file mode 100644 index e617c2889..000000000 --- a/app/javascript/mastodon/components/avatar.js +++ /dev/null @@ -1,62 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import { autoPlayGif } from '../initial_state'; -import classNames from 'classnames'; - -export default class Avatar extends React.PureComponent { - - static propTypes = { - account: ImmutablePropTypes.map, - size: PropTypes.number.isRequired, - style: PropTypes.object, - inline: PropTypes.bool, - animate: PropTypes.bool, - }; - - static defaultProps = { - animate: autoPlayGif, - size: 20, - inline: false, - }; - - state = { - hovering: false, - }; - - handleMouseEnter = () => { - if (this.props.animate) return; - this.setState({ hovering: true }); - } - - handleMouseLeave = () => { - if (this.props.animate) return; - this.setState({ hovering: false }); - } - - render () { - const { account, size, animate, inline } = this.props; - const { hovering } = this.state; - - const style = { - ...this.props.style, - width: `${size}px`, - height: `${size}px`, - }; - - let src; - - if (hovering || animate) { - src = account?.get('avatar'); - } else { - src = account?.get('avatar_static'); - } - - return ( - <div className={classNames('account__avatar', { 'account__avatar-inline': inline })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} style={style}> - {src && <img src={src} alt={account?.get('acct')} />} - </div> - ); - } - -} diff --git a/app/javascript/mastodon/components/avatar.tsx b/app/javascript/mastodon/components/avatar.tsx new file mode 100644 index 000000000..e64a8af74 --- /dev/null +++ b/app/javascript/mastodon/components/avatar.tsx @@ -0,0 +1,49 @@ +import * as React from 'react'; +import classNames from 'classnames'; +import { autoPlayGif } from '../initial_state'; +import { useHovering } from '../../hooks/useHovering'; +import type { Account } from '../../types/resources'; + +type Props = { + account: Account; + size: number; + style?: React.CSSProperties; + inline?: boolean; + animate?: boolean; +}; + +export const Avatar: React.FC<Props> = ({ + account, + animate = autoPlayGif, + size = 20, + inline = false, + style: styleFromParent, +}) => { + const { hovering, handleMouseEnter, handleMouseLeave } = useHovering(animate); + + const style = { + ...styleFromParent, + width: `${size}px`, + height: `${size}px`, + }; + + const src = + hovering || animate + ? account?.get('avatar') + : account?.get('avatar_static'); + + return ( + <div + className={classNames('account__avatar', { + 'account__avatar-inline': inline, + })} + onMouseEnter={handleMouseEnter} + onMouseLeave={handleMouseLeave} + style={style} + > + {src && <img src={src} alt={account?.get('acct')} />} + </div> + ); +}; + +export default Avatar; diff --git a/app/javascript/mastodon/components/avatar_composite.js b/app/javascript/mastodon/components/avatar_composite.jsx index 220bf5b4f..220bf5b4f 100644 --- a/app/javascript/mastodon/components/avatar_composite.js +++ b/app/javascript/mastodon/components/avatar_composite.jsx diff --git a/app/javascript/mastodon/components/avatar_overlay.js b/app/javascript/mastodon/components/avatar_overlay.jsx index 8d5d44ea5..034e8ba56 100644 --- a/app/javascript/mastodon/components/avatar_overlay.js +++ b/app/javascript/mastodon/components/avatar_overlay.jsx @@ -29,12 +29,12 @@ export default class AvatarOverlay extends React.PureComponent { handleMouseEnter = () => { if (this.props.animate) return; this.setState({ hovering: true }); - } + }; handleMouseLeave = () => { if (this.props.animate) return; this.setState({ hovering: false }); - } + }; render() { const { account, friend, animate, size, baseSize, overlaySize } = this.props; diff --git a/app/javascript/mastodon/components/blurhash.js b/app/javascript/mastodon/components/blurhash.jsx index 2af5cfc56..07cd31b6c 100644 --- a/app/javascript/mastodon/components/blurhash.js +++ b/app/javascript/mastodon/components/blurhash.jsx @@ -44,6 +44,7 @@ function Blurhash({ const ctx = canvas.getContext('2d'); const imageData = new ImageData(pixels, width, height); + // @ts-expect-error ctx.putImageData(imageData, 0, 0); } catch (err) { console.error('Blurhash decoding failure', { err, hash }); diff --git a/app/javascript/mastodon/components/button.js b/app/javascript/mastodon/components/button.jsx index 42ce01f38..a05a75e89 100644 --- a/app/javascript/mastodon/components/button.js +++ b/app/javascript/mastodon/components/button.jsx @@ -24,11 +24,11 @@ export default class Button extends React.PureComponent { if (!this.props.disabled && this.props.onClick) { this.props.onClick(e); } - } + }; setRef = (c) => { this.node = c; - } + }; focus() { this.node.focus(); diff --git a/app/javascript/mastodon/components/check.js b/app/javascript/mastodon/components/check.jsx index ee2ef1595..ee2ef1595 100644 --- a/app/javascript/mastodon/components/check.js +++ b/app/javascript/mastodon/components/check.jsx diff --git a/app/javascript/mastodon/components/column.js b/app/javascript/mastodon/components/column.jsx index 239824a4f..5780a1397 100644 --- a/app/javascript/mastodon/components/column.js +++ b/app/javascript/mastodon/components/column.jsx @@ -27,11 +27,11 @@ export default class Column extends React.PureComponent { } this._interruptScrollAnimation(); - } + }; setRef = c => { this.node = c; - } + }; componentDidMount () { if (this.props.bindToDocument) { diff --git a/app/javascript/mastodon/components/column_back_button.js b/app/javascript/mastodon/components/column_back_button.jsx index d97622705..5c5226b7e 100644 --- a/app/javascript/mastodon/components/column_back_button.js +++ b/app/javascript/mastodon/components/column_back_button.jsx @@ -15,12 +15,12 @@ export default class ColumnBackButton extends React.PureComponent { }; handleClick = () => { - if (window.history && window.history.length === 1) { - this.context.router.history.push('/'); - } else { + if (window.history && window.history.state) { this.context.router.history.goBack(); + } else { + this.context.router.history.push('/'); } - } + }; render () { const { multiColumn } = this.props; diff --git a/app/javascript/mastodon/components/column_back_button_slim.js b/app/javascript/mastodon/components/column_back_button_slim.jsx index cc8bfb151..46ac23736 100644 --- a/app/javascript/mastodon/components/column_back_button_slim.js +++ b/app/javascript/mastodon/components/column_back_button_slim.jsx @@ -8,7 +8,7 @@ export default class ColumnBackButtonSlim extends ColumnBackButton { render () { return ( <div className='column-back-button--slim'> - <div role='button' tabIndex='0' onClick={this.handleClick} className='column-back-button column-back-button--slim-button'> + <div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'> <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> </div> diff --git a/app/javascript/mastodon/components/column_header.js b/app/javascript/mastodon/components/column_header.jsx index 7850a93ec..afc526f27 100644 --- a/app/javascript/mastodon/components/column_header.js +++ b/app/javascript/mastodon/components/column_header.jsx @@ -12,7 +12,6 @@ const messages = defineMessages({ moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' }, }); -export default @injectIntl class ColumnHeader extends React.PureComponent { static contextTypes = { @@ -43,38 +42,34 @@ class ColumnHeader extends React.PureComponent { animating: false, }; - historyBack = () => { - if (window.history && window.history.length === 1) { - this.context.router.history.push('/'); - } else { - this.context.router.history.goBack(); - } - } - handleToggleClick = (e) => { e.stopPropagation(); this.setState({ collapsed: !this.state.collapsed, animating: true }); - } + }; handleTitleClick = () => { this.props.onClick?.(); - } + }; handleMoveLeft = () => { this.props.onMove(-1); - } + }; handleMoveRight = () => { this.props.onMove(1); - } + }; handleBackClick = () => { - this.historyBack(); - } + if (window.history && window.history.state) { + this.context.router.history.goBack(); + } else { + this.context.router.history.push('/'); + } + }; handleTransitionEnd = () => { this.setState({ animating: false }); - } + }; handlePin = () => { if (!this.props.pinned) { @@ -82,7 +77,7 @@ class ColumnHeader extends React.PureComponent { } this.props.onPin(); - } + }; render () { const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props; @@ -213,3 +208,5 @@ class ColumnHeader extends React.PureComponent { } } + +export default injectIntl(ColumnHeader); diff --git a/app/javascript/mastodon/components/common_counter.js b/app/javascript/mastodon/components/common_counter.jsx index dd9b62de9..dd9b62de9 100644 --- a/app/javascript/mastodon/components/common_counter.js +++ b/app/javascript/mastodon/components/common_counter.jsx diff --git a/app/javascript/mastodon/components/dismissable_banner.js b/app/javascript/mastodon/components/dismissable_banner.jsx index 1ee032056..242021e76 100644 --- a/app/javascript/mastodon/components/dismissable_banner.js +++ b/app/javascript/mastodon/components/dismissable_banner.jsx @@ -8,7 +8,6 @@ const messages = defineMessages({ dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' }, }); -export default @injectIntl class DismissableBanner extends React.PureComponent { static propTypes = { @@ -24,7 +23,7 @@ class DismissableBanner extends React.PureComponent { handleDismiss = () => { const { id } = this.props; this.setState({ visible: false }, () => bannerSettings.set(id, true)); - } + }; render () { const { visible } = this.state; @@ -49,3 +48,5 @@ class DismissableBanner extends React.PureComponent { } } + +export default injectIntl(DismissableBanner); diff --git a/app/javascript/mastodon/components/display_name.js b/app/javascript/mastodon/components/display_name.jsx index e9139ab0f..1dd9fb1d6 100644 --- a/app/javascript/mastodon/components/display_name.js +++ b/app/javascript/mastodon/components/display_name.jsx @@ -23,7 +23,7 @@ export default class DisplayName extends React.PureComponent { let emoji = emojis[i]; emoji.src = emoji.getAttribute('data-original'); } - } + }; handleMouseLeave = ({ currentTarget }) => { if (autoPlayGif) { @@ -36,7 +36,7 @@ export default class DisplayName extends React.PureComponent { let emoji = emojis[i]; emoji.src = emoji.getAttribute('data-static'); } - } + }; render () { const { others, localDomain } = this.props; diff --git a/app/javascript/mastodon/components/domain.js b/app/javascript/mastodon/components/domain.jsx index 697065d87..85ebdbde9 100644 --- a/app/javascript/mastodon/components/domain.js +++ b/app/javascript/mastodon/components/domain.jsx @@ -8,7 +8,6 @@ const messages = defineMessages({ unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unblock domain {domain}' }, }); -export default @injectIntl class Account extends ImmutablePureComponent { static propTypes = { @@ -19,7 +18,7 @@ class Account extends ImmutablePureComponent { handleDomainUnblock = () => { this.props.onUnblockDomain(this.props.domain); - } + }; render () { const { domain, intl } = this.props; @@ -40,3 +39,5 @@ class Account extends ImmutablePureComponent { } } + +export default injectIntl(Account); diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.jsx index 5897aada8..eaaa72fd8 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.jsx @@ -36,7 +36,7 @@ class DropdownMenu extends React.PureComponent { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); } - } + }; componentDidMount () { document.addEventListener('click', this.handleDocumentClick, false); @@ -56,11 +56,11 @@ class DropdownMenu extends React.PureComponent { setRef = c => { this.node = c; - } + }; setFocusRef = c => { this.focusedItem = c; - } + }; handleKeyDown = e => { const items = Array.from(this.node.querySelectorAll('a, button')); @@ -97,18 +97,18 @@ class DropdownMenu extends React.PureComponent { e.preventDefault(); e.stopPropagation(); } - } + }; handleItemKeyPress = e => { if (e.key === 'Enter' || e.key === ' ') { this.handleClick(e); } - } + }; handleClick = e => { const { onItemClick } = this.props; onItemClick(e); - } + }; renderItem = (option, i) => { if (option === null) { @@ -119,12 +119,12 @@ class DropdownMenu extends React.PureComponent { return ( <li className='dropdown-menu__item' key={`${text}-${i}`}> - <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex='0' ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}> + <a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}> {text} </a> </li> ); - } + }; render () { const { items, scrollable, renderHeader, loading } = this.props; @@ -194,7 +194,7 @@ export default class Dropdown extends React.PureComponent { } else { this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click'); } - } + }; handleClose = () => { if (this.activeElement) { @@ -202,13 +202,13 @@ export default class Dropdown extends React.PureComponent { this.activeElement = null; } this.props.onClose(this.state.id); - } + }; handleMouseDown = () => { if (!this.state.open) { this.activeElement = document.activeElement; } - } + }; handleButtonKeyDown = (e) => { switch(e.key) { @@ -217,7 +217,7 @@ export default class Dropdown extends React.PureComponent { this.handleMouseDown(); break; } - } + }; handleKeyPress = (e) => { switch(e.key) { @@ -228,7 +228,7 @@ export default class Dropdown extends React.PureComponent { e.preventDefault(); break; } - } + }; handleItemClick = e => { const { onItemClick } = this.props; @@ -247,25 +247,25 @@ export default class Dropdown extends React.PureComponent { e.preventDefault(); this.context.router.history.push(item.to); } - } + }; setTargetRef = c => { this.target = c; - } + }; findTarget = () => { return this.target; - } + }; componentWillUnmount = () => { if (this.state.id === this.props.openDropdownId) { this.handleClose(); } - } + }; close = () => { this.handleClose(); - } + }; render () { const { diff --git a/app/javascript/mastodon/components/edited_timestamp/index.js b/app/javascript/mastodon/components/edited_timestamp/index.jsx index bebf93886..1513f9361 100644 --- a/app/javascript/mastodon/components/edited_timestamp/index.js +++ b/app/javascript/mastodon/components/edited_timestamp/index.jsx @@ -16,8 +16,6 @@ const mapDispatchToProps = (dispatch, { statusId }) => ({ }); -export default @connect(null, mapDispatchToProps) -@injectIntl class EditedTimestamp extends React.PureComponent { static propTypes = { @@ -36,7 +34,7 @@ class EditedTimestamp extends React.PureComponent { return ( <FormattedMessage id='status.edited_x_times' defaultMessage='Edited {count, plural, one {{count} time} other {{count} times}}' values={{ count: items.size - 1 }} /> ); - } + }; renderItem = (item, index, { onClick, onKeyPress }) => { const formattedDate = <RelativeTimestamp timestamp={item.get('created_at')} short={false} />; @@ -53,7 +51,7 @@ class EditedTimestamp extends React.PureComponent { <button data-index={index} onClick={onClick} onKeyPress={onKeyPress}>{label}</button> </li> ); - } + }; render () { const { timestamp, intl, statusId } = this.props; @@ -68,3 +66,5 @@ class EditedTimestamp extends React.PureComponent { } } + +export default connect(null, mapDispatchToProps)(injectIntl(EditedTimestamp)); diff --git a/app/javascript/mastodon/components/error_boundary.js b/app/javascript/mastodon/components/error_boundary.jsx index 02d5616d6..b711f1e46 100644 --- a/app/javascript/mastodon/components/error_boundary.js +++ b/app/javascript/mastodon/components/error_boundary.jsx @@ -64,7 +64,7 @@ export default class ErrorBoundary extends React.PureComponent { this.setState({ copied: true }); setTimeout(() => this.setState({ copied: false }), 700); - } + }; render() { const { hasError, copied, errorMessage } = this.state; diff --git a/app/javascript/mastodon/components/gifv.js b/app/javascript/mastodon/components/gifv.jsx index b775e5200..1ce7e7c29 100644 --- a/app/javascript/mastodon/components/gifv.js +++ b/app/javascript/mastodon/components/gifv.jsx @@ -6,6 +6,7 @@ export default class GIFV extends React.PureComponent { static propTypes = { src: PropTypes.string.isRequired, alt: PropTypes.string, + lang: PropTypes.string, width: PropTypes.number, height: PropTypes.number, onClick: PropTypes.func, @@ -17,7 +18,7 @@ export default class GIFV extends React.PureComponent { handleLoadedData = () => { this.setState({ loading: false }); - } + }; componentWillReceiveProps (nextProps) { if (nextProps.src !== this.props.src) { @@ -32,10 +33,10 @@ export default class GIFV extends React.PureComponent { e.stopPropagation(); onClick(); } - } + }; render () { - const { src, width, height, alt } = this.props; + const { src, width, height, alt, lang } = this.props; const { loading } = this.state; return ( @@ -45,9 +46,10 @@ export default class GIFV extends React.PureComponent { width={width} height={height} role='button' - tabIndex='0' + tabIndex={0} aria-label={alt} title={alt} + lang={lang} onClick={this.handleClick} /> )} @@ -55,9 +57,10 @@ export default class GIFV extends React.PureComponent { <video src={src} role='button' - tabIndex='0' + tabIndex={0} aria-label={alt} title={alt} + lang={lang} muted loop autoPlay diff --git a/app/javascript/mastodon/components/hashtag.js b/app/javascript/mastodon/components/hashtag.jsx index e516fc086..94c61b654 100644 --- a/app/javascript/mastodon/components/hashtag.js +++ b/app/javascript/mastodon/components/hashtag.jsx @@ -5,7 +5,9 @@ import { FormattedMessage } from 'react-intl'; import PropTypes from 'prop-types'; import ImmutablePropTypes from 'react-immutable-proptypes'; import { Link } from 'react-router-dom'; +// @ts-expect-error import ShortNumber from 'mastodon/components/short_number'; +// @ts-expect-error import Skeleton from 'mastodon/components/skeleton'; import classNames from 'classnames'; @@ -19,11 +21,11 @@ class SilentErrorBoundary extends React.Component { error: false, }; - componentDidCatch () { + componentDidCatch() { this.setState({ error: true }); } - render () { + render() { if (this.state.error) { return null; } @@ -50,11 +52,13 @@ export const accountsCountRenderer = (displayNumber, pluralReady) => ( /> ); +// @ts-expect-error export const ImmutableHashtag = ({ hashtag }) => ( <Hashtag name={hashtag.get('name')} to={`/tags/${hashtag.get('name')}`} people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1} + // @ts-expect-error history={hashtag.get('history').reverse().map((day) => day.get('uses')).toArray()} /> ); @@ -63,6 +67,7 @@ ImmutableHashtag.propTypes = { hashtag: ImmutablePropTypes.map.isRequired, }; +// @ts-expect-error const Hashtag = ({ name, to, people, uses, history, className, description, withGraph }) => ( <div className={classNames('trends__item', className)}> <div className='trends__item__name'> diff --git a/app/javascript/mastodon/components/icon.js b/app/javascript/mastodon/components/icon.jsx index d3d7c591d..d3d7c591d 100644 --- a/app/javascript/mastodon/components/icon.js +++ b/app/javascript/mastodon/components/icon.jsx diff --git a/app/javascript/mastodon/components/icon_button.js b/app/javascript/mastodon/components/icon_button.jsx index b7daf82a4..989cae440 100644 --- a/app/javascript/mastodon/components/icon_button.js +++ b/app/javascript/mastodon/components/icon_button.jsx @@ -23,7 +23,7 @@ export default class IconButton extends React.PureComponent { inverted: PropTypes.bool, animate: PropTypes.bool, overlay: PropTypes.bool, - tabIndex: PropTypes.string, + tabIndex: PropTypes.number, counter: PropTypes.number, obfuscateCount: PropTypes.bool, href: PropTypes.string, @@ -36,14 +36,14 @@ export default class IconButton extends React.PureComponent { disabled: false, animate: false, overlay: false, - tabIndex: '0', + tabIndex: 0, ariaHidden: false, }; state = { activate: false, deactivate: false, - } + }; componentWillReceiveProps (nextProps) { if (!nextProps.animate) return; @@ -61,25 +61,25 @@ export default class IconButton extends React.PureComponent { if (!this.props.disabled) { this.props.onClick(e); } - } + }; handleKeyPress = (e) => { if (this.props.onKeyPress && !this.props.disabled) { this.props.onKeyPress(e); } - } + }; handleMouseDown = (e) => { if (!this.props.disabled && this.props.onMouseDown) { this.props.onMouseDown(e); } - } + }; handleKeyDown = (e) => { if (!this.props.disabled && this.props.onKeyDown) { this.props.onKeyDown(e); } - } + }; render () { const style = { diff --git a/app/javascript/mastodon/components/icon_with_badge.js b/app/javascript/mastodon/components/icon_with_badge.jsx index 4214eccfd..4214eccfd 100644 --- a/app/javascript/mastodon/components/icon_with_badge.js +++ b/app/javascript/mastodon/components/icon_with_badge.jsx diff --git a/app/javascript/mastodon/components/image.js b/app/javascript/mastodon/components/image.jsx index 6e81ddf08..6e81ddf08 100644 --- a/app/javascript/mastodon/components/image.js +++ b/app/javascript/mastodon/components/image.jsx diff --git a/app/javascript/mastodon/components/inline_account.js b/app/javascript/mastodon/components/inline_account.jsx index a1b495590..31dc63f93 100644 --- a/app/javascript/mastodon/components/inline_account.js +++ b/app/javascript/mastodon/components/inline_account.jsx @@ -14,7 +14,6 @@ const makeMapStateToProps = () => { return mapStateToProps; }; -export default @connect(makeMapStateToProps) class InlineAccount extends React.PureComponent { static propTypes = { @@ -32,3 +31,5 @@ class InlineAccount extends React.PureComponent { } } + +export default connect(makeMapStateToProps)(InlineAccount); diff --git a/app/javascript/mastodon/components/intersection_observer_article.js b/app/javascript/mastodon/components/intersection_observer_article.jsx index 26f85fa40..77957a21d 100644 --- a/app/javascript/mastodon/components/intersection_observer_article.js +++ b/app/javascript/mastodon/components/intersection_observer_article.jsx @@ -21,7 +21,7 @@ export default class IntersectionObserverArticle extends React.Component { state = { isHidden: false, // set to true in requestIdleCallback to trigger un-render - } + }; shouldComponentUpdate (nextProps, nextState) { const isUnrendered = !this.state.isIntersecting && (this.state.isHidden || this.props.cachedHeight); @@ -62,7 +62,7 @@ export default class IntersectionObserverArticle extends React.Component { scheduleIdleTask(this.calculateHeight); this.setState(this.updateStateAfterIntersection); - } + }; updateStateAfterIntersection = (prevState) => { if (prevState.isIntersecting !== false && !this.entry.isIntersecting) { @@ -72,7 +72,7 @@ export default class IntersectionObserverArticle extends React.Component { isIntersecting: this.entry.isIntersecting, isHidden: false, }; - } + }; calculateHeight = () => { const { onHeightChange, saveHeightKey, id } = this.props; @@ -83,7 +83,7 @@ export default class IntersectionObserverArticle extends React.Component { if (onHeightChange && saveHeightKey) { onHeightChange(saveHeightKey, id, this.height); } - } + }; hideIfNotIntersecting = () => { if (!this.componentMounted) { @@ -95,11 +95,11 @@ export default class IntersectionObserverArticle extends React.Component { // this is to save DOM nodes and avoid using up too much memory. // See: https://github.com/mastodon/mastodon/issues/2900 this.setState((prevState) => ({ isHidden: !prevState.isIntersecting })); - } + }; handleRef = (node) => { this.node = node; - } + }; render () { const { children, id, index, listLength, cachedHeight } = this.props; @@ -113,7 +113,7 @@ export default class IntersectionObserverArticle extends React.Component { aria-setsize={listLength} style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }} data-id={id} - tabIndex='0' + tabIndex={0} > {children && React.cloneElement(children, { hidden: true })} </article> @@ -121,7 +121,7 @@ export default class IntersectionObserverArticle extends React.Component { } return ( - <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex='0'> + <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex={0}> {children && React.cloneElement(children, { hidden: false })} </article> ); diff --git a/app/javascript/mastodon/components/load_gap.js b/app/javascript/mastodon/components/load_gap.jsx index a44d55d09..2c91d37be 100644 --- a/app/javascript/mastodon/components/load_gap.js +++ b/app/javascript/mastodon/components/load_gap.jsx @@ -7,7 +7,6 @@ const messages = defineMessages({ load_more: { id: 'status.load_more', defaultMessage: 'Load more' }, }); -export default @injectIntl class LoadGap extends React.PureComponent { static propTypes = { @@ -19,7 +18,7 @@ class LoadGap extends React.PureComponent { handleClick = () => { this.props.onClick(this.props.maxId); - } + }; render () { const { disabled, intl } = this.props; @@ -32,3 +31,5 @@ class LoadGap extends React.PureComponent { } } + +export default injectIntl(LoadGap); diff --git a/app/javascript/mastodon/components/load_more.js b/app/javascript/mastodon/components/load_more.jsx index 00e023ca2..150525214 100644 --- a/app/javascript/mastodon/components/load_more.js +++ b/app/javascript/mastodon/components/load_more.jsx @@ -8,11 +8,11 @@ export default class LoadMore extends React.PureComponent { onClick: PropTypes.func, disabled: PropTypes.bool, visible: PropTypes.bool, - } + }; static defaultProps = { visible: true, - } + }; render() { const { disabled, visible } = this.props; diff --git a/app/javascript/mastodon/components/load_pending.js b/app/javascript/mastodon/components/load_pending.jsx index 7e2702403..a75259146 100644 --- a/app/javascript/mastodon/components/load_pending.js +++ b/app/javascript/mastodon/components/load_pending.jsx @@ -7,7 +7,7 @@ export default class LoadPending extends React.PureComponent { static propTypes = { onClick: PropTypes.func, count: PropTypes.number, - } + }; render() { const { count } = this.props; diff --git a/app/javascript/mastodon/components/loading_indicator.js b/app/javascript/mastodon/components/loading_indicator.jsx index 33c59d94c..33c59d94c 100644 --- a/app/javascript/mastodon/components/loading_indicator.js +++ b/app/javascript/mastodon/components/loading_indicator.jsx diff --git a/app/javascript/mastodon/components/logo.js b/app/javascript/mastodon/components/logo.jsx index ee5c22496..ee5c22496 100644 --- a/app/javascript/mastodon/components/logo.js +++ b/app/javascript/mastodon/components/logo.jsx diff --git a/app/javascript/mastodon/components/media_attachments.js b/app/javascript/mastodon/components/media_attachments.jsx index d27720de4..0e25e5973 100644 --- a/app/javascript/mastodon/components/media_attachments.js +++ b/app/javascript/mastodon/components/media_attachments.jsx @@ -10,6 +10,7 @@ export default class MediaAttachments extends ImmutablePureComponent { static propTypes = { status: ImmutablePropTypes.map.isRequired, + lang: PropTypes.string, height: PropTypes.number, width: PropTypes.number, }; @@ -29,7 +30,7 @@ export default class MediaAttachments extends ImmutablePureComponent { return ( <div className='media-gallery' style={{ height, width }} /> ); - } + }; renderLoadingVideoPlayer = () => { const { height, width } = this.props; @@ -37,7 +38,7 @@ export default class MediaAttachments extends ImmutablePureComponent { return ( <div className='video-player' style={{ height, width }} /> ); - } + }; renderLoadingAudioPlayer = () => { const { height, width } = this.props; @@ -45,10 +46,10 @@ export default class MediaAttachments extends ImmutablePureComponent { return ( <div className='audio-player' style={{ height, width }} /> ); - } + }; render () { - const { status, width, height } = this.props; + const { status, lang, width, height } = this.props; const mediaAttachments = status.get('media_attachments'); if (mediaAttachments.size === 0) { @@ -64,6 +65,7 @@ export default class MediaAttachments extends ImmutablePureComponent { <Component src={audio.get('url')} alt={audio.get('description')} + lang={lang || status.get('language')} width={width} height={height} poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])} @@ -87,6 +89,7 @@ export default class MediaAttachments extends ImmutablePureComponent { blurhash={video.get('blurhash')} src={video.get('url')} alt={video.get('description')} + lang={lang || status.get('language')} width={width} height={height} inline @@ -102,6 +105,7 @@ export default class MediaAttachments extends ImmutablePureComponent { {Component => ( <Component media={mediaAttachments} + lang={lang || status.get('language')} sensitive={status.get('sensitive')} defaultWidth={width} height={height} diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.jsx index e4a8be338..5be0070a3 100644 --- a/app/javascript/mastodon/components/media_gallery.js +++ b/app/javascript/mastodon/components/media_gallery.jsx @@ -17,6 +17,7 @@ class Item extends React.PureComponent { static propTypes = { attachment: ImmutablePropTypes.map.isRequired, + lang: PropTypes.string, standalone: PropTypes.bool, index: PropTypes.number.isRequired, size: PropTypes.number.isRequired, @@ -40,14 +41,14 @@ class Item extends React.PureComponent { if (this.hoverToPlay()) { e.target.play(); } - } + }; handleMouseLeave = (e) => { if (this.hoverToPlay()) { e.target.pause(); e.target.currentTime = 0; } - } + }; getAutoPlay() { return this.props.autoplay || autoPlayGif; @@ -71,14 +72,14 @@ class Item extends React.PureComponent { } e.stopPropagation(); - } + }; handleImageLoad = () => { this.setState({ loaded: true }); - } + }; render () { - const { attachment, index, size, standalone, displayWidth, visible } = this.props; + const { attachment, lang, index, size, standalone, displayWidth, visible } = this.props; let width = 50; let height = 100; @@ -134,7 +135,7 @@ class Item extends React.PureComponent { if (attachment.get('type') === 'unknown') { return ( <div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}> - <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} target='_blank' rel='noopener noreferrer'> + <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} lang={lang} target='_blank' rel='noopener noreferrer'> <Blurhash hash={attachment.get('blurhash')} className='media-gallery__preview' @@ -174,6 +175,7 @@ class Item extends React.PureComponent { sizes={sizes} alt={attachment.get('description')} title={attachment.get('description')} + lang={lang} style={{ objectPosition: `${x}% ${y}%` }} onLoad={this.handleImageLoad} /> @@ -188,6 +190,7 @@ class Item extends React.PureComponent { className='media-gallery__item-gifv-thumbnail' aria-label={attachment.get('description')} title={attachment.get('description')} + lang={lang} role='application' src={attachment.get('url')} onClick={this.handleClick} @@ -220,13 +223,13 @@ class Item extends React.PureComponent { } -export default @injectIntl class MediaGallery extends React.PureComponent { static propTypes = { sensitive: PropTypes.bool, standalone: PropTypes.bool, media: ImmutablePropTypes.list.isRequired, + lang: PropTypes.string, size: PropTypes.object, height: PropTypes.number.isRequired, onOpenMedia: PropTypes.func.isRequired, @@ -277,11 +280,11 @@ class MediaGallery extends React.PureComponent { } else { this.setState({ visible: !this.state.visible }); } - } + }; handleClick = (index) => { this.props.onOpenMedia(this.props.media, index); - } + }; handleRef = c => { this.node = c; @@ -289,7 +292,7 @@ class MediaGallery extends React.PureComponent { if (this.node) { this._setDimensions(); } - } + }; _setDimensions () { const width = this.node.offsetWidth; @@ -310,9 +313,8 @@ class MediaGallery extends React.PureComponent { } render () { - const { media, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props; + const { media, lang, intl, sensitive, height, defaultWidth, standalone, autoplay } = this.props; const { visible } = this.state; - const width = this.state.width || defaultWidth; let children, spoilerButton; @@ -333,9 +335,9 @@ class MediaGallery extends React.PureComponent { const uncached = media.every(attachment => attachment.get('type') === 'unknown'); if (standalone && this.isFullSizeEligible()) { - children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} displayWidth={width} visible={visible} />; + children = <Item standalone autoplay={autoplay} onClick={this.handleClick} attachment={media.get(0)} lang={lang} displayWidth={width} visible={visible} />; } else { - children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} size={size} displayWidth={width} visible={visible || uncached} />); + children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} autoplay={autoplay} onClick={this.handleClick} attachment={attachment} index={i} lang={lang} size={size} displayWidth={width} visible={visible || uncached} />); } if (uncached) { @@ -366,3 +368,5 @@ class MediaGallery extends React.PureComponent { } } + +export default injectIntl(MediaGallery); diff --git a/app/javascript/mastodon/components/missing_indicator.js b/app/javascript/mastodon/components/missing_indicator.jsx index 05e0d653d..05e0d653d 100644 --- a/app/javascript/mastodon/components/missing_indicator.js +++ b/app/javascript/mastodon/components/missing_indicator.jsx diff --git a/app/javascript/mastodon/components/modal_root.js b/app/javascript/mastodon/components/modal_root.jsx index b894aeaf9..c0525c221 100644 --- a/app/javascript/mastodon/components/modal_root.js +++ b/app/javascript/mastodon/components/modal_root.jsx @@ -28,7 +28,7 @@ export default class ModalRoot extends React.PureComponent { && !!this.props.children) { this.props.onClose(); } - } + }; handleKeyDown = (e) => { if (e.key === 'Tab') { @@ -49,7 +49,7 @@ export default class ModalRoot extends React.PureComponent { e.preventDefault(); } } - } + }; componentDidMount () { window.addEventListener('keyup', this.handleKeyUp, false); @@ -122,11 +122,11 @@ export default class ModalRoot extends React.PureComponent { getSiblings = () => { return Array(...this.node.parentElement.childNodes).filter(node => node !== this.node); - } + }; setRef = ref => { this.node = ref; - } + }; render () { const { children, onClose } = this.props; diff --git a/app/javascript/mastodon/components/navigation_portal.js b/app/javascript/mastodon/components/navigation_portal.jsx index 45407be43..a100dc04a 100644 --- a/app/javascript/mastodon/components/navigation_portal.js +++ b/app/javascript/mastodon/components/navigation_portal.jsx @@ -15,7 +15,6 @@ const DefaultNavigation = () => ( </> ); -export default @withRouter class NavigationPortal extends React.PureComponent { render () { @@ -33,3 +32,4 @@ class NavigationPortal extends React.PureComponent { } } +export default withRouter(NavigationPortal); diff --git a/app/javascript/mastodon/components/not_signed_in_indicator.js b/app/javascript/mastodon/components/not_signed_in_indicator.jsx index b440c6be2..b440c6be2 100644 --- a/app/javascript/mastodon/components/not_signed_in_indicator.js +++ b/app/javascript/mastodon/components/not_signed_in_indicator.jsx diff --git a/app/javascript/mastodon/components/picture_in_picture_placeholder.js b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx index 19d15c18b..6322b1c66 100644 --- a/app/javascript/mastodon/components/picture_in_picture_placeholder.js +++ b/app/javascript/mastodon/components/picture_in_picture_placeholder.jsx @@ -6,7 +6,6 @@ import { connect } from 'react-redux'; import { debounce } from 'lodash'; import { FormattedMessage } from 'react-intl'; -export default @connect() class PictureInPicturePlaceholder extends React.PureComponent { static propTypes = { @@ -22,7 +21,7 @@ class PictureInPicturePlaceholder extends React.PureComponent { handleClick = () => { const { dispatch } = this.props; dispatch(removePictureInPicture()); - } + }; setRef = c => { this.node = c; @@ -30,7 +29,7 @@ class PictureInPicturePlaceholder extends React.PureComponent { if (this.node) { this._setDimensions(); } - } + }; _setDimensions () { const width = this.node.offsetWidth; @@ -59,7 +58,7 @@ class PictureInPicturePlaceholder extends React.PureComponent { const { height } = this.state; return ( - <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex='0' onClick={this.handleClick}> + <div ref={this.setRef} className='picture-in-picture-placeholder' style={{ height }} role='button' tabIndex={0} onClick={this.handleClick}> <Icon id='window-restore' /> <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' /> </div> @@ -67,3 +66,5 @@ class PictureInPicturePlaceholder extends React.PureComponent { } } + +export default connect()(PictureInPicturePlaceholder); diff --git a/app/javascript/mastodon/components/poll.js b/app/javascript/mastodon/components/poll.jsx index 3e643168e..b9b96a700 100644 --- a/app/javascript/mastodon/components/poll.js +++ b/app/javascript/mastodon/components/poll.jsx @@ -31,7 +31,6 @@ const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => { return obj; }, {}); -export default @injectIntl class Poll extends ImmutablePureComponent { static contextTypes = { @@ -40,6 +39,7 @@ class Poll extends ImmutablePureComponent { static propTypes = { poll: ImmutablePropTypes.map, + lang: PropTypes.string, intl: PropTypes.object.isRequired, disabled: PropTypes.bool, refresh: PropTypes.func, @@ -95,7 +95,7 @@ class Poll extends ImmutablePureComponent { tmp[value] = true; this.setState({ selected: tmp }); } - } + }; handleOptionChange = ({ target: { value } }) => { this._toggleOption(value); @@ -107,7 +107,7 @@ class Poll extends ImmutablePureComponent { e.stopPropagation(); e.preventDefault(); } - } + }; handleVote = () => { if (this.props.disabled) { @@ -126,7 +126,7 @@ class Poll extends ImmutablePureComponent { }; renderOption (option, optionIndex, showResults) { - const { poll, disabled, intl } = this.props; + const { poll, lang, disabled, intl } = this.props; const pollVotesCount = poll.get('voters_count') || poll.get('votes_count'); const percent = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100; const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count')); @@ -154,11 +154,12 @@ class Poll extends ImmutablePureComponent { {!showResults && ( <span className={classNames('poll__input', { checkbox: poll.get('multiple'), active })} - tabIndex='0' + tabIndex={0} role={poll.get('multiple') ? 'checkbox' : 'radio'} onKeyPress={this.handleOptionKeyPress} aria-checked={active} aria-label={option.get('title')} + lang={lang} data-index={optionIndex} /> )} @@ -175,6 +176,7 @@ class Poll extends ImmutablePureComponent { <span className='poll__option__text translate' + lang={lang} dangerouslySetInnerHTML={{ __html: titleEmojified }} /> @@ -231,3 +233,5 @@ class Poll extends ImmutablePureComponent { } } + +export default injectIntl(Poll); diff --git a/app/javascript/mastodon/components/radio_button.js b/app/javascript/mastodon/components/radio_button.jsx index 0496fa286..0496fa286 100644 --- a/app/javascript/mastodon/components/radio_button.js +++ b/app/javascript/mastodon/components/radio_button.jsx diff --git a/app/javascript/mastodon/components/regeneration_indicator.js b/app/javascript/mastodon/components/regeneration_indicator.jsx index 52696a4a7..52696a4a7 100644 --- a/app/javascript/mastodon/components/regeneration_indicator.js +++ b/app/javascript/mastodon/components/regeneration_indicator.jsx diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.jsx index 512480339..e6c3e0880 100644 --- a/app/javascript/mastodon/components/relative_timestamp.js +++ b/app/javascript/mastodon/components/relative_timestamp.jsx @@ -121,7 +121,6 @@ const timeRemainingString = (intl, date, now, timeGiven = true) => { return relativeTime; }; -export default @injectIntl class RelativeTimestamp extends React.Component { static propTypes = { @@ -197,3 +196,5 @@ class RelativeTimestamp extends React.Component { } } + +export default injectIntl(RelativeTimestamp); diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.jsx index 91d04bf4d..57bc88121 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.jsx @@ -20,7 +20,6 @@ const mapStateToProps = (state, { scrollKey }) => { }; }; -export default @connect(mapStateToProps, null, null, { forwardRef: true }) class ScrollableList extends PureComponent { static contextTypes = { @@ -97,7 +96,7 @@ class ScrollableList extends PureComponent { } else { return this.node; } - } + }; setScrollTop = newScrollTop => { if (this.getScrollTop() !== newScrollTop) { @@ -143,7 +142,7 @@ class ScrollableList extends PureComponent { this.mouseMovedRecently = false; this.scrollToTopOnMouseIdle = false; - } + }; componentDidMount () { this.attachScrollListener(); @@ -161,25 +160,25 @@ class ScrollableList extends PureComponent { } else { return null; } - } + }; getScrollTop = () => { return this._getScrollingElement().scrollTop; - } + }; getScrollHeight = () => { return this._getScrollingElement().scrollHeight; - } + }; getClientHeight = () => { return this._getScrollingElement().clientHeight; - } + }; updateScrollBottom = (snapshot) => { const newScrollTop = this.getScrollHeight() - snapshot; this.setScrollTop(newScrollTop); - } + }; getSnapshotBeforeUpdate (prevProps) { const someItemInserted = React.Children.count(prevProps.children) > 0 && @@ -206,7 +205,7 @@ class ScrollableList extends PureComponent { if (width && this.state.cachedMediaWidth !== width) { this.setState({ cachedMediaWidth: width }); } - } + }; componentWillUnmount () { this.clearMouseIdleTimer(); @@ -218,7 +217,7 @@ class ScrollableList extends PureComponent { onFullScreenChange = () => { this.setState({ fullscreen: isFullscreen() }); - } + }; attachIntersectionObserver () { let nodeOptions = { @@ -269,12 +268,12 @@ class ScrollableList extends PureComponent { setRef = (c) => { this.node = c; - } + }; handleLoadMore = e => { e.preventDefault(); this.props.onLoadMore(); - } + }; handleLoadPending = e => { e.preventDefault(); @@ -286,7 +285,7 @@ class ScrollableList extends PureComponent { this.clearMouseIdleTimer(); this.mouseIdleTimer = setTimeout(this.handleMouseIdle, MOUSE_IDLE_DELAY); this.mouseMovedRecently = true; - } + }; render () { const { children, scrollKey, trackScroll, showLoading, isLoading, hasMore, numPending, prepend, alwaysPrepend, append, emptyMessage, onLoadMore } = this.props; @@ -365,3 +364,5 @@ class ScrollableList extends PureComponent { } } + +export default connect(mapStateToProps, null, null, { forwardRef: true })(ScrollableList); diff --git a/app/javascript/mastodon/components/server_banner.js b/app/javascript/mastodon/components/server_banner.jsx index 617fdecdf..e5f5aa8ee 100644 --- a/app/javascript/mastodon/components/server_banner.js +++ b/app/javascript/mastodon/components/server_banner.jsx @@ -18,8 +18,6 @@ const mapStateToProps = state => ({ server: state.getIn(['server', 'server']), }); -export default @connect(mapStateToProps) -@injectIntl class ServerBanner extends React.PureComponent { static propTypes = { @@ -61,7 +59,7 @@ class ServerBanner extends React.PureComponent { <div className='server-banner__meta__column'> <h4><FormattedMessage id='server_banner.administered_by' defaultMessage='Administered by:' /></h4> - <Account id={server.getIn(['contact', 'account', 'id'])} size={36} /> + <Account id={server.getIn(['contact', 'account', 'id'])} size={36} minimal /> </div> <div className='server-banner__meta__column'> @@ -91,3 +89,5 @@ class ServerBanner extends React.PureComponent { } } + +export default connect(mapStateToProps)(injectIntl(ServerBanner)); diff --git a/app/javascript/mastodon/components/short_number.js b/app/javascript/mastodon/components/short_number.jsx index 535c17727..535c17727 100644 --- a/app/javascript/mastodon/components/short_number.js +++ b/app/javascript/mastodon/components/short_number.jsx diff --git a/app/javascript/mastodon/components/skeleton.js b/app/javascript/mastodon/components/skeleton.jsx index 6a17ffb26..6a17ffb26 100644 --- a/app/javascript/mastodon/components/skeleton.js +++ b/app/javascript/mastodon/components/skeleton.jsx diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.jsx index a1384ba58..923dc892d 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.jsx @@ -59,7 +59,6 @@ const messages = defineMessages({ edited: { id: 'status.edited', defaultMessage: 'Edited {date}' }, }); -export default @injectIntl class Status extends ImmutablePureComponent { static contextTypes = { @@ -135,7 +134,7 @@ class Status extends ImmutablePureComponent { handleToggleMediaVisibility = () => { this.setState({ showMedia: !this.state.showMedia }); - } + }; handleClick = e => { if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) { @@ -147,11 +146,11 @@ class Status extends ImmutablePureComponent { } this.handleHotkeyOpen(); - } + }; handlePrependAccountClick = e => { this.handleAccountClick(e, false); - } + }; handleAccountClick = (e, proper = true) => { if (e && (e.button !== 0 || e.ctrlKey || e.metaKey)) { @@ -160,22 +159,23 @@ class Status extends ImmutablePureComponent { if (e) { e.preventDefault(); + e.stopPropagation(); } this._openProfile(proper); - } + }; handleExpandedToggle = () => { this.props.onToggleHidden(this._properStatus()); - } + }; handleCollapsedToggle = isCollapsed => { this.props.onToggleCollapsed(this._properStatus(), isCollapsed); - } + }; handleTranslate = () => { this.props.onTranslate(this._properStatus()); - } + }; renderLoadingMediaGallery () { return <div className='media-gallery' style={{ height: '110px' }} />; @@ -192,11 +192,11 @@ class Status extends ImmutablePureComponent { handleOpenVideo = (options) => { const status = this._properStatus(); this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), options); - } + }; handleOpenMedia = (media, index) => { this.props.onOpenMedia(this._properStatus().get('id'), media, index); - } + }; handleHotkeyOpenMedia = e => { const { onOpenMedia, onOpenVideo } = this.props; @@ -211,32 +211,32 @@ class Status extends ImmutablePureComponent { onOpenMedia(status.get('id'), status.get('media_attachments'), 0); } } - } + }; handleDeployPictureInPicture = (type, mediaProps) => { const { deployPictureInPicture } = this.props; const status = this._properStatus(); deployPictureInPicture(status, type, mediaProps); - } + }; handleHotkeyReply = e => { e.preventDefault(); this.props.onReply(this._properStatus(), this.context.router.history); - } + }; handleHotkeyFavourite = () => { this.props.onFavourite(this._properStatus()); - } + }; handleHotkeyBoost = e => { this.props.onReblog(this._properStatus(), e); - } + }; handleHotkeyMention = e => { e.preventDefault(); this.props.onMention(this._properStatus().get('account'), this.context.router.history); - } + }; handleHotkeyOpen = () => { if (this.props.onClick) { @@ -252,11 +252,11 @@ class Status extends ImmutablePureComponent { } router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`); - } + }; handleHotkeyOpenProfile = () => { this._openProfile(); - } + }; _openProfile = (proper = true) => { const { router } = this.context; @@ -267,32 +267,32 @@ class Status extends ImmutablePureComponent { } router.history.push(`/@${status.getIn(['account', 'acct'])}`); - } + }; handleHotkeyMoveUp = e => { this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured')); - } + }; handleHotkeyMoveDown = e => { this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured')); - } + }; handleHotkeyToggleHidden = () => { this.props.onToggleHidden(this._properStatus()); - } + }; handleHotkeyToggleSensitive = () => { this.handleToggleMediaVisibility(); - } + }; handleUnfilterClick = e => { this.setState({ forceFilter: false }); e.preventDefault(); - } + }; handleFilterClick = () => { this.setState({ forceFilter: true }); - } + }; _properStatus () { const { status } = this.props; @@ -306,7 +306,7 @@ class Status extends ImmutablePureComponent { handleRef = c => { this.node = c; - } + }; render () { let media = null; @@ -337,7 +337,7 @@ class Status extends ImmutablePureComponent { if (hidden) { return ( <HotKeys handlers={handlers}> - <div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex='0'> + <div ref={this.handleRef} className={classNames('status__wrapper', { focusable: !this.props.muted })} tabIndex={0}> <span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span> <span>{status.get('content')}</span> </div> @@ -354,7 +354,7 @@ class Status extends ImmutablePureComponent { return ( <HotKeys handlers={minHandlers}> - <div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}> + <div className='status__wrapper status__wrapper--filtered focusable' tabIndex={0} ref={this.handleRef}> <FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {matchedFilters.join(', ')}. {' '} <button className='status__wrapper--filtered__button' onClick={this.handleUnfilterClick}> @@ -386,6 +386,13 @@ class Status extends ImmutablePureComponent { account = status.get('account'); status = status.get('reblog'); + } else if (status.get('visibility') === 'direct') { + prepend = ( + <div className='status__prepend'> + <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div> + <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' /> + </div> + ); } else if (showThread && status.get('in_reply_to_id') && status.get('in_reply_to_account_id') === status.getIn(['account', 'id'])) { const display_name_html = { __html: status.getIn(['account', 'display_name_html']) }; @@ -416,6 +423,7 @@ class Status extends ImmutablePureComponent { <Component src={attachment.get('url')} alt={attachment.get('description')} + lang={status.get('language')} poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])} backgroundColor={attachment.getIn(['meta', 'colors', 'background'])} foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])} @@ -445,6 +453,7 @@ class Status extends ImmutablePureComponent { blurhash={attachment.get('blurhash')} src={attachment.get('url')} alt={attachment.get('description')} + lang={status.get('language')} width={this.props.cachedMediaWidth} height={110} inline @@ -464,6 +473,7 @@ class Status extends ImmutablePureComponent { {Component => ( <Component media={status.get('media_attachments')} + lang={status.get('language')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.handleOpenMedia} @@ -510,8 +520,8 @@ class Status extends ImmutablePureComponent { {prepend} <div className={classNames('status', `status-${status.get('visibility')}`, { 'status-reply': !!status.get('in_reply_to_id'), muted: this.props.muted })} data-id={status.get('id')}> - <div className='status__info'> - <a onClick={this.handleClick} href={`/@${status.getIn(['account', 'acct'])}\/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> + <div onClick={this.handleClick} className='status__info'> + <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>} </a> @@ -545,3 +555,5 @@ class Status extends ImmutablePureComponent { } } + +export default injectIntl(Status); diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.jsx index 00fc94358..7b4031b68 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.jsx @@ -14,7 +14,7 @@ const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' }, edit: { id: 'status.edit', defaultMessage: 'Edit' }, - direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' }, + direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' }, @@ -53,8 +53,6 @@ const mapStateToProps = (state, { status }) => ({ relationship: state.getIn(['relationships', status.getIn(['account', 'id'])]), }); -export default @connect(mapStateToProps) -@injectIntl class StatusActionBar extends ImmutablePureComponent { static contextTypes = { @@ -97,7 +95,7 @@ class StatusActionBar extends ImmutablePureComponent { 'status', 'relationship', 'withDismiss', - ] + ]; handleReplyClick = () => { const { signedIn } = this.context.identity; @@ -107,7 +105,7 @@ class StatusActionBar extends ImmutablePureComponent { } else { this.props.onInteractionModal('reply', this.props.status); } - } + }; handleShareClick = () => { navigator.share({ @@ -116,7 +114,7 @@ class StatusActionBar extends ImmutablePureComponent { }).catch((e) => { if (e.name !== 'AbortError') console.error(e); }); - } + }; handleFavouriteClick = () => { const { signedIn } = this.context.identity; @@ -126,7 +124,7 @@ class StatusActionBar extends ImmutablePureComponent { } else { this.props.onInteractionModal('favourite', this.props.status); } - } + }; handleReblogClick = e => { const { signedIn } = this.context.identity; @@ -136,35 +134,35 @@ class StatusActionBar extends ImmutablePureComponent { } else { this.props.onInteractionModal('reblog', this.props.status); } - } + }; handleBookmarkClick = () => { this.props.onBookmark(this.props.status); - } + }; handleDeleteClick = () => { this.props.onDelete(this.props.status, this.context.router.history); - } + }; handleRedraftClick = () => { this.props.onDelete(this.props.status, this.context.router.history, true); - } + }; handleEditClick = () => { this.props.onEdit(this.props.status, this.context.router.history); - } + }; handlePinClick = () => { this.props.onPin(this.props.status); - } + }; handleMentionClick = () => { this.props.onMention(this.props.status.get('account'), this.context.router.history); - } + }; handleDirectClick = () => { this.props.onDirect(this.props.status.get('account'), this.context.router.history); - } + }; handleMuteClick = () => { const { status, relationship, onMute, onUnmute } = this.props; @@ -175,7 +173,7 @@ class StatusActionBar extends ImmutablePureComponent { } else { onMute(account); } - } + }; handleBlockClick = () => { const { status, relationship, onBlock, onUnblock } = this.props; @@ -186,50 +184,50 @@ class StatusActionBar extends ImmutablePureComponent { } else { onBlock(status); } - } + }; handleBlockDomain = () => { const { status, onBlockDomain } = this.props; const account = status.get('account'); onBlockDomain(account.get('acct').split('@')[1]); - } + }; handleUnblockDomain = () => { const { status, onUnblockDomain } = this.props; const account = status.get('account'); onUnblockDomain(account.get('acct').split('@')[1]); - } + }; handleOpen = () => { this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`); - } + }; handleEmbed = () => { this.props.onEmbed(this.props.status); - } + }; handleReport = () => { this.props.onReport(this.props.status); - } + }; handleConversationMuteClick = () => { this.props.onMuteConversation(this.props.status); - } + }; handleFilterClick = () => { this.props.onAddFilter(this.props.status); - } + }; handleCopy = () => { const url = this.props.status.get('url'); navigator.clipboard.writeText(url); - } + }; handleHideClick = () => { this.props.onFilter(); - } + }; render () { const { status, relationship, intl, withDismiss, withCounters, scrollKey } = this.props; @@ -385,3 +383,5 @@ class StatusActionBar extends ImmutablePureComponent { } } + +export default connect(mapStateToProps)(injectIntl(StatusActionBar)); diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.jsx index 6f3093d63..fb953b9dd 100644 --- a/app/javascript/mastodon/components/status_content.js +++ b/app/javascript/mastodon/components/status_content.jsx @@ -3,10 +3,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import { FormattedMessage, injectIntl } from 'react-intl'; import { Link } from 'react-router-dom'; +import { connect } from 'react-redux'; import classnames from 'classnames'; import PollContainer from 'mastodon/containers/poll_container'; import Icon from 'mastodon/components/icon'; -import { autoPlayGif, languages as preloadedLanguages, translationEnabled } from 'mastodon/initial_state'; +import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top) @@ -47,7 +48,10 @@ class TranslateButton extends React.PureComponent { } -export default @injectIntl +const mapStateToProps = state => ({ + languages: state.getIn(['server', 'translationLanguages', 'items']), +}); + class StatusContent extends React.PureComponent { static contextTypes = { @@ -63,6 +67,7 @@ class StatusContent extends React.PureComponent { onClick: PropTypes.func, collapsable: PropTypes.bool, onCollapsedToggle: PropTypes.func, + languages: ImmutablePropTypes.map, intl: PropTypes.object, }; @@ -130,7 +135,7 @@ class StatusContent extends React.PureComponent { let emoji = emojis[i]; emoji.src = emoji.getAttribute('data-original'); } - } + }; handleMouseLeave = ({ currentTarget }) => { if (autoPlayGif) { @@ -143,7 +148,7 @@ class StatusContent extends React.PureComponent { let emoji = emojis[i]; emoji.src = emoji.getAttribute('data-static'); } - } + }; componentDidMount () { this._updateStatusLinks(); @@ -158,7 +163,7 @@ class StatusContent extends React.PureComponent { e.preventDefault(); this.context.router.history.push(`/@${mention.get('acct')}`); } - } + }; onHashtagClick = (hashtag, e) => { hashtag = hashtag.replace(/^#/, ''); @@ -167,11 +172,11 @@ class StatusContent extends React.PureComponent { e.preventDefault(); this.context.router.history.push(`/tags/${hashtag}`); } - } + }; handleMouseDown = (e) => { this.startXY = [e.clientX, e.clientY]; - } + }; handleMouseUp = (e) => { if (!this.startXY) { @@ -194,7 +199,7 @@ class StatusContent extends React.PureComponent { } this.startXY = null; - } + }; handleSpoilerClick = (e) => { e.preventDefault(); @@ -205,22 +210,24 @@ class StatusContent extends React.PureComponent { } else { this.setState({ hidden: !this.state.hidden }); } - } + }; handleTranslate = () => { this.props.onTranslate(); - } + }; setRef = (c) => { this.node = c; - } + }; render () { const { status, intl } = this.props; const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; const renderReadMore = this.props.onClick && status.get('collapsed'); - const renderTranslate = translationEnabled && this.context.identity.signedIn && this.props.onTranslate && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && status.get('language') !== null && intl.locale !== status.get('language'); + const contentLocale = intl.locale.replace(/[_-].*/, ''); + const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); + const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && targetLanguages?.includes(contentLocale); const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') }; const spoilerContent = { __html: status.get('spoilerHtml') }; @@ -242,7 +249,7 @@ class StatusContent extends React.PureComponent { ); const poll = !!status.get('poll') && ( - <PollContainer pollId={status.get('poll')} /> + <PollContainer pollId={status.get('poll')} lang={status.get('language')} /> ); if (status.get('spoiler_text').length > 0) { @@ -261,7 +268,7 @@ class StatusContent extends React.PureComponent { } return ( - <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> + <div className={classNames} ref={this.setRef} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <p style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}> <span dangerouslySetInnerHTML={spoilerContent} className='translate' lang={lang} /> {' '} @@ -279,7 +286,7 @@ class StatusContent extends React.PureComponent { } else if (this.props.onClick) { return ( <> - <div className={classNames} ref={this.setRef} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> + <div className={classNames} ref={this.setRef} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} /> {poll} @@ -291,7 +298,7 @@ class StatusContent extends React.PureComponent { ); } else { return ( - <div className={classNames} ref={this.setRef} tabIndex='0' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> + <div className={classNames} ref={this.setRef} tabIndex={0} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}> <div className='status__content__text status__content__text--visible translate' lang={lang} dangerouslySetInnerHTML={content} /> {poll} @@ -302,3 +309,5 @@ class StatusContent extends React.PureComponent { } } + +export default connect(mapStateToProps)(injectIntl(StatusContent)); diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.jsx index 35e5749a3..3d513bbf8 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.jsx @@ -34,7 +34,7 @@ export default class StatusList extends ImmutablePureComponent { getFeaturedStatusCount = () => { return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0; - } + }; getCurrentStatusIndex = (id, featured) => { if (featured) { @@ -42,21 +42,21 @@ export default class StatusList extends ImmutablePureComponent { } else { return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount(); } - } + }; handleMoveUp = (id, featured) => { const elementIndex = this.getCurrentStatusIndex(id, featured) - 1; this._selectChild(elementIndex, true); - } + }; handleMoveDown = (id, featured) => { const elementIndex = this.getCurrentStatusIndex(id, featured) + 1; this._selectChild(elementIndex, false); - } + }; handleLoadOlder = debounce(() => { this.props.onLoadMore(this.props.statusIds.size > 0 ? this.props.statusIds.last() : undefined); - }, 300, { leading: true }) + }, 300, { leading: true }); _selectChild (index, align_top) { const container = this.node.node; @@ -74,7 +74,7 @@ export default class StatusList extends ImmutablePureComponent { setRef = c => { this.node = c; - } + }; render () { const { statusIds, featuredStatusIds, onLoadMore, timelineId, ...other } = this.props; diff --git a/app/javascript/mastodon/components/timeline_hint.js b/app/javascript/mastodon/components/timeline_hint.jsx index ac9a79dcc..ac9a79dcc 100644 --- a/app/javascript/mastodon/components/timeline_hint.js +++ b/app/javascript/mastodon/components/timeline_hint.jsx |