[build] upgrade eslint to 9.37.0 (#88)

Co-authored-by: tobi <tobi.smethurst@protonmail.com>
Reviewed-on: https://codeberg.org/superseriousbusiness/masto-fe-standalone/pulls/88
Co-authored-by: Zoë Bijl <moiety@noreply.codeberg.org>
Co-committed-by: Zoë Bijl <moiety@noreply.codeberg.org>
This commit is contained in:
Zoë Bijl
2025-10-12 13:42:02 +02:00
committed by tobi
parent 75d7a62693
commit 1ff70886a1
975 changed files with 22196 additions and 21964 deletions
@@ -1,24 +1,24 @@
import { render, fireEvent, screen } from '@testing-library/react';
import { render, fireEvent, screen } from "@testing-library/react";
import Column from '../column';
import Column from "../column";
describe('<Column />', () => {
describe('<ColumnHeader /> click handler', () => {
it('runs the scroll animation if the column contains scrollable content', () => {
describe("<Column />", () => {
describe("<ColumnHeader /> click handler", () => {
it("runs the scroll animation if the column contains scrollable content", () => {
const scrollToMock = jest.fn();
const { container } = render(
<Column heading='notifications'>
<div className='scrollable' />
</Column>,
);
container.querySelector('.scrollable').scrollTo = scrollToMock;
fireEvent.click(screen.getByText('notifications'));
expect(scrollToMock).toHaveBeenCalledWith({ behavior: 'smooth', top: 0 });
container.querySelector(".scrollable").scrollTo = scrollToMock;
fireEvent.click(screen.getByText("notifications"));
expect(scrollToMock).toHaveBeenCalledWith({ behavior: "smooth", top: 0 });
});
it('does not try to scroll if there is no scrollable content', () => {
it("does not try to scroll if there is no scrollable content", () => {
render(<Column heading='notifications' />);
fireEvent.click(screen.getByText('notifications'));
fireEvent.click(screen.getByText("notifications"));
});
});
});
@@ -1,11 +1,11 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import classNames from 'classnames';
import classNames from "classnames";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { IconButton } from '../../../components/icon_button';
import { IconButton } from "../../../components/icon_button";
export default class ActionsModal extends ImmutablePureComponent {
@@ -20,14 +20,14 @@ export default class ActionsModal extends ImmutablePureComponent {
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
}
const { icon = null, text, meta = null, active = false, href = '#' } = action;
const { icon = null, text, meta = null, active = false, href = "#" } = action;
return (
<li key={`${text}-${i}`}>
<a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}>
{icon && <IconButton title={text} icon={icon} role='presentation' tabIndex={-1} inverted />}
<div>
<div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div>
<div className={classNames({ "actions-modal__item-label": !!meta })}>{text}</div>
<div>{meta}</div>
</div>
</a>
@@ -38,7 +38,7 @@ export default class ActionsModal extends ImmutablePureComponent {
render () {
return (
<div className='modal-root__modal actions-modal'>
<ul className={classNames({ 'with-status': !!status })}>
<ul className={classNames({ "with-status": !!status })}>
{this.props.actions.map(this.renderAction)}
</ul>
</div>
@@ -1,15 +1,15 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import Audio from 'mastodon/features/audio';
import Footer from 'mastodon/features/picture_in_picture/components/footer';
import Audio from "mastodon/features/audio";
import Footer from "mastodon/features/picture_in_picture/components/footer";
const mapStateToProps = (state, { statusId }) => ({
status: state.getIn(['statuses', statusId]),
accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']),
status: state.getIn(["statuses", statusId]),
accountStaticAvatar: state.getIn(["accounts", state.getIn(["statuses", statusId, "account"]), "avatar_static"]),
});
class AudioModal extends ImmutablePureComponent {
@@ -29,28 +29,28 @@ class AudioModal extends ImmutablePureComponent {
render () {
const { media, status, accountStaticAvatar, onClose } = this.props;
const options = this.props.options || {};
const language = status.getIn(['translation', 'language']) || status.get('language');
const description = media.getIn(['translation', 'description']) || media.get('description');
const language = status.getIn(["translation", "language"]) || status.get("language");
const description = media.getIn(["translation", "description"]) || media.get("description");
return (
<div className='modal-root__modal audio-modal'>
<div className='audio-modal__container'>
<Audio
src={media.get('url')}
src={media.get("url")}
alt={description}
lang={language}
duration={media.getIn(['meta', 'original', 'duration'], 0)}
duration={media.getIn(["meta", "original", "duration"], 0)}
height={150}
poster={media.get('preview_url') || accountStaticAvatar}
backgroundColor={media.getIn(['meta', 'colors', 'background'])}
foregroundColor={media.getIn(['meta', 'colors', 'foreground'])}
accentColor={media.getIn(['meta', 'colors', 'accent'])}
poster={media.get("preview_url") || accountStaticAvatar}
backgroundColor={media.getIn(["meta", "colors", "background"])}
foregroundColor={media.getIn(["meta", "colors", "foreground"])}
accentColor={media.getIn(["meta", "colors", "accent"])}
autoPlay={options.autoPlay}
/>
</div>
<div className='media-modal__overlay'>
{status && <Footer statusId={status.get('id')} withOpenButton onClose={onClose} />}
{status && <Footer statusId={status.get("id")} withOpenButton onClose={onClose} />}
</div>
</div>
);
@@ -1,21 +1,21 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { injectIntl, FormattedMessage } from 'react-intl';
import { injectIntl, FormattedMessage } from "react-intl";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { blockAccount } from '../../../actions/accounts';
import { closeModal } from '../../../actions/modal';
import { initReport } from '../../../actions/reports';
import Button from '../../../components/button';
import { makeGetAccount } from '../../../selectors';
import { blockAccount } from "../../../actions/accounts";
import { closeModal } from "../../../actions/modal";
import { initReport } from "../../../actions/reports";
import Button from "../../../components/button";
import { makeGetAccount } from "../../../selectors";
const makeMapStateToProps = () => {
const getAccount = makeGetAccount();
const mapStateToProps = state => ({
account: getAccount(state, state.getIn(['blocks', 'new', 'account_id'])),
account: getAccount(state, state.getIn(["blocks", "new", "account_id"])),
});
return mapStateToProps;
@@ -24,11 +24,11 @@ const makeMapStateToProps = () => {
const mapDispatchToProps = dispatch => {
return {
onConfirm(account) {
dispatch(blockAccount(account.get('id')));
dispatch(blockAccount(account.get("id")));
},
onBlockAndReport(account) {
dispatch(blockAccount(account.get('id')));
dispatch(blockAccount(account.get("id")));
dispatch(initReport(account));
},
@@ -83,7 +83,7 @@ class BlockModal extends PureComponent {
<FormattedMessage
id='confirmations.block.message'
defaultMessage='Are you sure you want to block {name}?'
values={{ name: <strong>@{account.get('acct')}</strong> }}
values={{ name: <strong>@{account.get("acct")}</strong> }}
/>
</p>
</div>
@@ -1,36 +1,36 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl, FormattedMessage } from "react-intl";
import classNames from 'classnames';
import classNames from "classnames";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import { changeBoostPrivacy } from 'mastodon/actions/boosts';
import AttachmentList from 'mastodon/components/attachment_list';
import { Icon } from 'mastodon/components/icon';
import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown';
import { changeBoostPrivacy } from "mastodon/actions/boosts";
import AttachmentList from "mastodon/components/attachment_list";
import { Icon } from "mastodon/components/icon";
import PrivacyDropdown from "mastodon/features/compose/components/privacy_dropdown";
import { Avatar } from '../../../components/avatar';
import Button from '../../../components/button';
import { DisplayName } from '../../../components/display_name';
import { RelativeTimestamp } from '../../../components/relative_timestamp';
import StatusContent from '../../../components/status_content';
import { Avatar } from "../../../components/avatar";
import Button from "../../../components/button";
import { DisplayName } from "../../../components/display_name";
import { RelativeTimestamp } from "../../../components/relative_timestamp";
import StatusContent from "../../../components/status_content";
const messages = defineMessages({
cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
cancel_reblog: { id: "status.cancel_reblog_private", defaultMessage: "Unboost" },
reblog: { id: "status.reblog", defaultMessage: "Boost" },
public_short: { id: "privacy.public.short", defaultMessage: "Public" },
unlisted_short: { id: "privacy.unlisted.short", defaultMessage: "Unlisted" },
private_short: { id: "privacy.private.short", defaultMessage: "Followers only" },
direct_short: { id: "privacy.direct.short", defaultMessage: "Mentioned people only" },
});
const mapStateToProps = state => {
return {
privacy: state.getIn(['boosts', 'new', 'privacy']),
privacy: state.getIn(["boosts", "new", "privacy"]),
};
};
@@ -70,12 +70,12 @@ class BoostModal extends ImmutablePureComponent {
if (e.button === 0 && !(e.ctrlKey || e.metaKey)) {
e.preventDefault();
this.props.onClose();
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
this.context.router.history.push(`/@${this.props.status.getIn(["account", "acct"])}`);
}
};
_findContainer = () => {
return document.getElementsByClassName('modal-root__container')[0];
return document.getElementsByClassName("modal-root__container")[0];
};
setRef = (c) => {
@@ -84,42 +84,42 @@ class BoostModal extends ImmutablePureComponent {
render () {
const { status, privacy, intl } = this.props;
const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
const buttonText = status.get("reblogged") ? messages.cancel_reblog : messages.reblog;
const visibilityIconInfo = {
'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) },
"public": { icon: "globe", text: intl.formatMessage(messages.public_short) },
"unlisted": { icon: "unlock", text: intl.formatMessage(messages.unlisted_short) },
"private": { icon: "lock", text: intl.formatMessage(messages.private_short) },
"direct": { icon: "at", text: intl.formatMessage(messages.direct_short) },
};
const visibilityIcon = visibilityIconInfo[status.get('visibility')];
const visibilityIcon = visibilityIconInfo[status.get("visibility")];
return (
<div className='modal-root__modal boost-modal'>
<div className='boost-modal__container'>
<div className={classNames('status', `status-${status.get('visibility')}`, 'light')}>
<div className={classNames("status", `status-${status.get("visibility")}`, "light")}>
<div className='status__info'>
<a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'>
<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')} />
<RelativeTimestamp timestamp={status.get("created_at")} />
</a>
<a onClick={this.handleAccountClick} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name'>
<a onClick={this.handleAccountClick} href={`/@${status.getIn(["account", "acct"])}`} className='status__display-name'>
<div className='status__avatar'>
<Avatar account={status.get('account')} size={48} />
<Avatar account={status.get("account")} size={48} />
</div>
<DisplayName account={status.get('account')} />
<DisplayName account={status.get("account")} />
</a>
</div>
<StatusContent status={status} />
{status.get('media_attachments').size > 0 && (
{status.get("media_attachments").size > 0 && (
<AttachmentList
compact
media={status.get('media_attachments')}
media={status.get("media_attachments")}
/>
)}
</div>
@@ -127,7 +127,7 @@ class BoostModal extends ImmutablePureComponent {
<div className='boost-modal__action-bar'>
<div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></span> }} /></div>
{status.get('visibility') !== 'private' && !status.get('reblogged') && (
{status.get("visibility") !== "private" && !status.get("reblogged") && (
<PrivacyDropdown
noDirect
value={privacy}
@@ -1,5 +1,5 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
const emptyComponent = () => null;
const noop = () => { };
@@ -1,15 +1,15 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { injectIntl, FormattedMessage } from 'react-intl';
import { injectIntl, FormattedMessage } from "react-intl";
import classNames from 'classnames';
import { Helmet } from 'react-helmet';
import { Link } from 'react-router-dom';
import classNames from "classnames";
import { Helmet } from "react-helmet";
import { Link } from "react-router-dom";
import Button from 'mastodon/components/button';
import Column from 'mastodon/components/column';
import { autoPlayGif } from 'mastodon/initial_state';
import Button from "mastodon/components/button";
import Column from "mastodon/components/column";
import { autoPlayGif } from "mastodon/initial_state";
class GIF extends PureComponent {
@@ -81,7 +81,9 @@ class CopyButton extends PureComponent {
};
componentWillUnmount () {
if (this.timeout) clearTimeout(this.timeout);
if (this.timeout) {
clearTimeout(this.timeout);
}
}
render () {
@@ -89,7 +91,7 @@ class CopyButton extends PureComponent {
const { copied } = this.state;
return (
<Button onClick={this.handleClick} className={copied ? 'copied' : 'copyable'}>{copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : children}</Button>
<Button onClick={this.handleClick} className={copied ? "copied" : "copyable"}>{copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : children}</Button>
);
}
@@ -98,7 +100,7 @@ class CopyButton extends PureComponent {
class BundleColumnError extends PureComponent {
static propTypes = {
errorType: PropTypes.oneOf(['routing', 'network', 'error']),
errorType: PropTypes.oneOf(["routing", "network", "error"]),
onRetry: PropTypes.func,
intl: PropTypes.object.isRequired,
multiColumn: PropTypes.bool,
@@ -106,7 +108,7 @@ class BundleColumnError extends PureComponent {
};
static defaultProps = {
errorType: 'routing',
errorType: "routing",
};
handleRetry = () => {
@@ -123,18 +125,18 @@ class BundleColumnError extends PureComponent {
let title, body;
switch(errorType) {
case 'routing':
title = <FormattedMessage id='bundle_column_error.routing.title' defaultMessage='404' />;
body = <FormattedMessage id='bundle_column_error.routing.body' defaultMessage='The requested page could not be found. Are you sure the URL in the address bar is correct?' />;
break;
case 'network':
title = <FormattedMessage id='bundle_column_error.network.title' defaultMessage='Network error' />;
body = <FormattedMessage id='bundle_column_error.network.body' defaultMessage='There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.' />;
break;
case 'error':
title = <FormattedMessage id='bundle_column_error.error.title' defaultMessage='Oh, no!' />;
body = <FormattedMessage id='bundle_column_error.error.body' defaultMessage='The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.' />;
break;
case "routing":
title = <FormattedMessage id='bundle_column_error.routing.title' defaultMessage='404' />;
body = <FormattedMessage id='bundle_column_error.routing.body' defaultMessage='The requested page could not be found. Are you sure the URL in the address bar is correct?' />;
break;
case "network":
title = <FormattedMessage id='bundle_column_error.network.title' defaultMessage='Network error' />;
body = <FormattedMessage id='bundle_column_error.network.body' defaultMessage='There was an error when trying to load this page. This could be due to a temporary problem with your internet connection or this server.' />;
break;
case "error":
title = <FormattedMessage id='bundle_column_error.error.title' defaultMessage='Oh, no!' />;
body = <FormattedMessage id='bundle_column_error.error.body' defaultMessage='The requested page could not be rendered. It could be due to a bug in our code, or a browser compatibility issue.' />;
break;
}
return (
@@ -147,9 +149,9 @@ class BundleColumnError extends PureComponent {
<p>{body}</p>
<div className='error-column__message__actions'>
{errorType === 'network' && <Button onClick={this.handleRetry}><FormattedMessage id='bundle_column_error.retry' defaultMessage='Try again' /></Button>}
{errorType === 'error' && <CopyButton value={stacktrace}><FormattedMessage id='bundle_column_error.copy_stacktrace' defaultMessage='Copy error report' /></CopyButton>}
<Link to='/' className={classNames('button', { 'button-tertiary': errorType !== 'routing' })}><FormattedMessage id='bundle_column_error.return' defaultMessage='Go back home' /></Link>
{errorType === "network" && <Button onClick={this.handleRetry}><FormattedMessage id='bundle_column_error.retry' defaultMessage='Try again' /></Button>}
{errorType === "error" && <CopyButton value={stacktrace}><FormattedMessage id='bundle_column_error.copy_stacktrace' defaultMessage='Copy error report' /></CopyButton>}
<Link to='/' className={classNames("button", { "button-tertiary": errorType !== "routing" })}><FormattedMessage id='bundle_column_error.return' defaultMessage='Go back home' /></Link>
</div>
</div>
</div>
@@ -1,14 +1,14 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from "react-intl";
import { IconButton } from '../../../components/icon_button';
import { IconButton } from "../../../components/icon_button";
const messages = defineMessages({
error: { id: 'bundle_modal_error.message', defaultMessage: 'Something went wrong while loading this component.' },
retry: { id: 'bundle_modal_error.retry', defaultMessage: 'Try again' },
close: { id: 'bundle_modal_error.close', defaultMessage: 'Close' },
error: { id: "bundle_modal_error.message", defaultMessage: "Something went wrong while loading this component." },
retry: { id: "bundle_modal_error.retry", defaultMessage: "Try again" },
close: { id: "bundle_modal_error.close", defaultMessage: "Close" },
});
class BundleModalError extends PureComponent {
@@ -1,12 +1,12 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { debounce } from 'lodash';
import { debounce } from "lodash";
import { isMobile } from '../../../is_mobile';
import { scrollTop } from '../../../scroll';
import { isMobile } from "../../../is_mobile";
import { scrollTop } from "../../../scroll";
import ColumnHeader from './column_header';
import ColumnHeader from "./column_header";
export default class Column extends PureComponent {
@@ -19,7 +19,7 @@ export default class Column extends PureComponent {
};
handleHeaderClick = () => {
const scrollable = this.node.querySelector('.scrollable');
const scrollable = this.node.querySelector(".scrollable");
if (!scrollable) {
return;
@@ -29,7 +29,7 @@ export default class Column extends PureComponent {
};
scrollTop () {
const scrollable = this.node.querySelector('.scrollable');
const scrollable = this.node.querySelector(".scrollable");
if (!scrollable) {
return;
@@ -40,7 +40,7 @@ export default class Column extends PureComponent {
handleScroll = debounce(() => {
if (typeof this._interruptScrollAnimation !== 'undefined') {
if (typeof this._interruptScrollAnimation !== "undefined") {
this._interruptScrollAnimation();
}
}, 200);
@@ -54,7 +54,7 @@ export default class Column extends PureComponent {
const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth)));
const columnHeaderId = showHeading && heading.replace(/ /g, '-');
const columnHeaderId = showHeading && heading.replace(/ /g, "-");
const header = showHeading && (
<ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} />
);
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import classNames from 'classnames';
import classNames from "classnames";
import { Icon } from 'mastodon/components/icon';
import { Icon } from "mastodon/components/icon";
export default class ColumnHeader extends PureComponent {
@@ -21,14 +21,14 @@ export default class ColumnHeader extends PureComponent {
render () {
const { icon, type, active, columnHeaderId } = this.props;
let iconElement = '';
let iconElement = "";
if (icon) {
iconElement = <Icon id={icon} fixedWidth className='column-header__icon' />;
}
return (
<h1 className={classNames('column-header', { active })} id={columnHeaderId || null}>
<h1 className={classNames("column-header", { active })} id={columnHeaderId || null}>
<button onClick={this.handleClick}>
{iconElement}
{type}
@@ -1,14 +1,14 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import classNames from 'classnames';
import { NavLink } from 'react-router-dom';
import classNames from "classnames";
import { NavLink } from "react-router-dom";
import { Icon } from 'mastodon/components/icon';
import { Icon } from "mastodon/components/icon";
const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other }) => {
const className = classNames('column-link', { 'column-link--transparent': transparent });
const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null;
const iconElement = typeof icon === 'string' ? <Icon id={icon} fixedWidth className='column-link__icon' /> : icon;
const className = classNames("column-link", { "column-link--transparent": transparent });
const badgeElement = typeof badge !== "undefined" ? <span className='column-link__badge'>{badge}</span> : null;
const iconElement = typeof icon === "string" ? <Icon id={icon} fixedWidth className='column-link__icon' /> : icon;
if (href) {
return (
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePureComponent from "react-immutable-pure-component";
import Column from '../../../components/column';
import ColumnHeader from '../../../components/column_header';
import Column from "../../../components/column";
import ColumnHeader from "../../../components/column_header";
export default class ColumnLoading extends ImmutablePureComponent {
@@ -14,8 +14,8 @@ export default class ColumnLoading extends ImmutablePureComponent {
};
static defaultProps = {
title: '',
icon: '',
title: "",
icon: "",
};
render() {
@@ -1,4 +1,4 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
const ColumnSubheading = ({ text }) => {
return (
@@ -1,13 +1,13 @@
import PropTypes from 'prop-types';
import { Children, cloneElement } from 'react';
import PropTypes from "prop-types";
import { Children, cloneElement } from "react";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { supportsPassiveEvents } from 'detect-passive-events';
import { supportsPassiveEvents } from "detect-passive-events";
import { scrollRight } from '../../../scroll';
import BundleContainer from '../containers/bundle_container';
import { scrollRight } from "../../../scroll";
import BundleContainer from "../containers/bundle_container";
import {
Compose,
Notifications,
@@ -20,27 +20,27 @@ import {
BookmarkedStatuses,
ListTimeline,
Directory,
} from '../util/async-components';
} from "../util/async-components";
import BundleColumnError from './bundle_column_error';
import ColumnLoading from './column_loading';
import ComposePanel from './compose_panel';
import DrawerLoading from './drawer_loading';
import NavigationPanel from './navigation_panel';
import BundleColumnError from "./bundle_column_error";
import ColumnLoading from "./column_loading";
import ComposePanel from "./compose_panel";
import DrawerLoading from "./drawer_loading";
import NavigationPanel from "./navigation_panel";
const componentMap = {
'COMPOSE': Compose,
'HOME': HomeTimeline,
'NOTIFICATIONS': Notifications,
'PUBLIC': PublicTimeline,
'REMOTE': PublicTimeline,
'COMMUNITY': CommunityTimeline,
'HASHTAG': HashtagTimeline,
'DIRECT': DirectTimeline,
'FAVOURITES': FavouritedStatuses,
'BOOKMARKS': BookmarkedStatuses,
'LIST': ListTimeline,
'DIRECTORY': Directory,
"COMPOSE": Compose,
"HOME": HomeTimeline,
"NOTIFICATIONS": Notifications,
"PUBLIC": PublicTimeline,
"REMOTE": PublicTimeline,
"COMMUNITY": CommunityTimeline,
"HASHTAG": HashtagTimeline,
"DIRECT": DirectTimeline,
"FAVOURITES": FavouritedStatuses,
"BOOKMARKS": BookmarkedStatuses,
"LIST": ListTimeline,
"DIRECTORY": Directory,
};
export default class ColumnsArea extends ImmutablePureComponent {
@@ -57,7 +57,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
};
// Corresponds to (max-width: $no-gap-breakpoint + 285px - 1px) in SCSS
mediaQuery = 'matchMedia' in window && window.matchMedia('(max-width: 1174px)');
mediaQuery = "matchMedia" in window && window.matchMedia("(max-width: 1174px)");
state = {
renderComposePanel: !(this.mediaQuery && this.mediaQuery.matches),
@@ -65,41 +65,41 @@ export default class ColumnsArea extends ImmutablePureComponent {
componentDidMount() {
if (!this.props.singleColumn) {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
this.node.addEventListener("wheel", this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
}
if (this.mediaQuery) {
if (this.mediaQuery.addEventListener) {
this.mediaQuery.addEventListener('change', this.handleLayoutChange);
this.mediaQuery.addEventListener("change", this.handleLayoutChange);
} else {
this.mediaQuery.addListener(this.handleLayoutChange);
}
this.setState({ renderComposePanel: !this.mediaQuery.matches });
}
this.isRtlLayout = document.getElementsByTagName('body')[0].classList.contains('rtl');
this.isRtlLayout = document.getElementsByTagName("body")[0].classList.contains("rtl");
}
UNSAFE_componentWillUpdate(nextProps) {
if (this.props.singleColumn !== nextProps.singleColumn && nextProps.singleColumn) {
this.node.removeEventListener('wheel', this.handleWheel);
this.node.removeEventListener("wheel", this.handleWheel);
}
}
componentDidUpdate(prevProps) {
if (this.props.singleColumn !== prevProps.singleColumn && !this.props.singleColumn) {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
this.node.addEventListener("wheel", this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
}
}
componentWillUnmount () {
if (!this.props.singleColumn) {
this.node.removeEventListener('wheel', this.handleWheel);
this.node.removeEventListener("wheel", this.handleWheel);
}
if (this.mediaQuery) {
if (this.mediaQuery.removeEventListener) {
this.mediaQuery.removeEventListener('change', this.handleLayoutChange);
this.mediaQuery.removeEventListener("change", this.handleLayoutChange);
} else {
this.mediaQuery.removeListener(this.handleLayoutChange);
}
@@ -118,7 +118,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
};
handleWheel = () => {
if (typeof this._interruptScrollAnimation !== 'function') {
if (typeof this._interruptScrollAnimation !== "function") {
return;
}
@@ -130,7 +130,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
};
renderLoading = columnId => () => {
return columnId === 'COMPOSE' ? <DrawerLoading /> : <ColumnLoading multiColumn />;
return columnId === "COMPOSE" ? <DrawerLoading /> : <ColumnLoading multiColumn />;
};
renderError = (props) => {
@@ -165,14 +165,14 @@ export default class ColumnsArea extends ImmutablePureComponent {
}
return (
<div className={`columns-area ${ isModalOpen ? 'unscrollable' : '' }`} ref={this.setRef}>
<div className={`columns-area ${ isModalOpen ? "unscrollable" : "" }`} ref={this.setRef}>
{columns.map(column => {
const params = column.get('params', null) === null ? null : column.get('params').toJS();
const params = column.get("params", null) === null ? null : column.get("params").toJS();
const other = params && params.other ? params.other : {};
return (
<BundleContainer key={column.get('uuid')} fetchComponent={componentMap[column.get('id')]} loading={this.renderLoading(column.get('id'))} error={this.renderError}>
{SpecificComponent => <SpecificComponent columnId={column.get('uuid')} params={params} multiColumn {...other} />}
<BundleContainer key={column.get("uuid")} fetchComponent={componentMap[column.get("id")]} loading={this.renderLoading(column.get("id"))} error={this.renderError}>
{SpecificComponent => <SpecificComponent columnId={column.get("uuid")} params={params} multiColumn {...other} />}
</BundleContainer>
);
})}
@@ -1,23 +1,23 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage } from 'react-intl';
import { FormattedMessage } from "react-intl";
import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux';
import ImmutablePropTypes from "react-immutable-proptypes";
import { connect } from "react-redux";
import escapeTextContentForBrowser from 'escape-html';
import escapeTextContentForBrowser from "escape-html";
import { closeModal } from 'mastodon/actions/modal';
import { IconButton } from 'mastodon/components/icon_button';
import InlineAccount from 'mastodon/components/inline_account';
import MediaAttachments from 'mastodon/components/media_attachments';
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
import emojify from 'mastodon/features/emoji/emoji';
import { closeModal } from "mastodon/actions/modal";
import { IconButton } from "mastodon/components/icon_button";
import InlineAccount from "mastodon/components/inline_account";
import MediaAttachments from "mastodon/components/media_attachments";
import { RelativeTimestamp } from "mastodon/components/relative_timestamp";
import emojify from "mastodon/features/emoji/emoji";
const mapStateToProps = (state, { statusId }) => ({
language: state.getIn(['statuses', statusId, 'language']),
versions: state.getIn(['history', statusId, 'items']),
language: state.getIn(["statuses", statusId, "language"]),
versions: state.getIn(["history", statusId, "items"]),
});
const mapDispatchToProps = dispatch => ({
@@ -45,18 +45,18 @@ class CompareHistoryModal extends PureComponent {
const { index, versions, language, onClose } = this.props;
const currentVersion = versions.get(index);
const emojiMap = currentVersion.get('emojis').reduce((obj, emoji) => {
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
const emojiMap = currentVersion.get("emojis").reduce((obj, emoji) => {
obj[`:${emoji.get("shortcode")}:`] = emoji.toJS();
return obj;
}, {});
const content = { __html: emojify(currentVersion.get('content'), emojiMap) };
const spoilerContent = { __html: emojify(escapeTextContentForBrowser(currentVersion.get('spoiler_text')), emojiMap) };
const content = { __html: emojify(currentVersion.get("content"), emojiMap) };
const spoilerContent = { __html: emojify(escapeTextContentForBrowser(currentVersion.get("spoiler_text")), emojiMap) };
const formattedDate = <RelativeTimestamp timestamp={currentVersion.get('created_at')} short={false} />;
const formattedName = <InlineAccount accountId={currentVersion.get('account')} />;
const formattedDate = <RelativeTimestamp timestamp={currentVersion.get("created_at")} short={false} />;
const formattedName = <InlineAccount accountId={currentVersion.get("account")} />;
const label = currentVersion.get('original') ? (
const label = currentVersion.get("original") ? (
<FormattedMessage id='status.history.created' defaultMessage='{name} created {date}' values={{ name: formattedName, date: formattedDate }} />
) : (
<FormattedMessage id='status.history.edited' defaultMessage='{name} edited {date}' values={{ name: formattedName, date: formattedDate }} />
@@ -71,7 +71,7 @@ class CompareHistoryModal extends PureComponent {
<div className='compare-history-modal__container'>
<div className='status__content'>
{currentVersion.get('spoiler_text').length > 0 && (
{currentVersion.get("spoiler_text").length > 0 && (
<>
<div className='translate' dangerouslySetInnerHTML={spoilerContent} lang={language} />
<hr />
@@ -80,16 +80,16 @@ class CompareHistoryModal extends PureComponent {
<div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} lang={language} />
{!!currentVersion.get('poll') && (
{!!currentVersion.get("poll") && (
<div className='poll'>
<ul>
{currentVersion.getIn(['poll', 'options']).map(option => (
<li key={option.get('title')}>
{currentVersion.getIn(["poll", "options"]).map(option => (
<li key={option.get("title")}>
<span className='poll__input disabled' />
<span
className='poll__option__text translate'
dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }}
dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get("title")), emojiMap) }}
lang={language}
/>
</li>
@@ -1,15 +1,15 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { changeComposing, mountCompose, unmountCompose } from 'mastodon/actions/compose';
import ServerBanner from 'mastodon/components/server_banner';
import ComposeFormContainer from 'mastodon/features/compose/containers/compose_form_container';
import NavigationContainer from 'mastodon/features/compose/containers/navigation_container';
import SearchContainer from 'mastodon/features/compose/containers/search_container';
import { changeComposing, mountCompose, unmountCompose } from "mastodon/actions/compose";
import ServerBanner from "mastodon/components/server_banner";
import ComposeFormContainer from "mastodon/features/compose/containers/compose_form_container";
import NavigationContainer from "mastodon/features/compose/containers/navigation_container";
import SearchContainer from "mastodon/features/compose/containers/search_container";
import LinkFooter from './link_footer';
import LinkFooter from "./link_footer";
class ComposePanel extends PureComponent {
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { injectIntl, FormattedMessage } from 'react-intl';
import { injectIntl, FormattedMessage } from "react-intl";
import Button from '../../../components/button';
import Button from "../../../components/button";
class ConfirmationModal extends PureComponent {
@@ -1,30 +1,30 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from "react-intl";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { openModal } from 'mastodon/actions/modal';
import { disabledAccountId, movedToAccountId, domain } from 'mastodon/initial_state';
import { logOut } from 'mastodon/utils/log_out';
import { openModal } from "mastodon/actions/modal";
import { disabledAccountId, movedToAccountId, domain } from "mastodon/initial_state";
import { logOut } from "mastodon/utils/log_out";
const messages = defineMessages({
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
logoutMessage: { id: "confirmations.logout.message", defaultMessage: "Are you sure you want to log out?" },
logoutConfirm: { id: "confirmations.logout.confirm", defaultMessage: "Log out" },
});
const mapStateToProps = (state) => ({
disabledAcct: state.getIn(['accounts', disabledAccountId, 'acct']),
movedToAcct: movedToAccountId ? state.getIn(['accounts', movedToAccountId, 'acct']) : undefined,
disabledAcct: state.getIn(["accounts", disabledAccountId, "acct"]),
movedToAcct: movedToAccountId ? state.getIn(["accounts", movedToAccountId, "acct"]) : undefined,
});
const mapDispatchToProps = (dispatch, { intl }) => ({
onLogout () {
dispatch(openModal({
modalType: 'CONFIRM',
modalType: "CONFIRM",
modalProps: {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
@@ -71,7 +71,7 @@ class DisabledAccountBanner extends PureComponent {
defaultMessage='Your account {disabledAccount} is currently disabled because you moved to {movedToAccount}.'
values={{
disabledAccount: disabledAccountLink,
movedToAccount: <Link to={`/@${movedToAcct}`}>{movedToAcct.includes('@') ? movedToAcct : `${movedToAcct}@${domain}`}</Link>,
movedToAccount: <Link to={`/@${movedToAcct}`}>{movedToAcct.includes("@") ? movedToAcct : `${movedToAcct}@${domain}`}</Link>,
}}
/>
) : (
@@ -1,14 +1,14 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { defineMessages, FormattedMessage, injectIntl } from "react-intl";
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePureComponent from "react-immutable-pure-component";
import api from 'mastodon/api';
import { IconButton } from 'mastodon/components/icon_button';
import api from "mastodon/api";
import { IconButton } from "mastodon/components/icon_button";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
close: { id: "lightbox.close", defaultMessage: "Close" },
});
class EmbedModal extends ImmutablePureComponent {
@@ -66,7 +66,7 @@ class EmbedModal extends ImmutablePureComponent {
<FormattedMessage id='status.embed' defaultMessage='Embed' />
</div>
<div className='report-modal__container embed-modal__container' style={{ display: 'block' }}>
<div className='report-modal__container embed-modal__container' style={{ display: "block" }}>
<p className='hint'>
<FormattedMessage id='embed.instructions' defaultMessage='Embed this status on your website by copying the code below.' />
</p>
@@ -75,7 +75,7 @@ class EmbedModal extends ImmutablePureComponent {
type='text'
className='embed-modal__html'
readOnly
value={oembed && oembed.html || ''}
value={oembed && oembed.html || ""}
onClick={this.handleTextareaClick}
/>
@@ -1,18 +1,18 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { defineMessages, FormattedMessage, injectIntl } from "react-intl";
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import { fetchFilters, createFilter, createFilterStatus } from 'mastodon/actions/filters';
import { fetchStatus } from 'mastodon/actions/statuses';
import { IconButton } from 'mastodon/components/icon_button';
import AddedToFilter from 'mastodon/features/filters/added_to_filter';
import SelectFilter from 'mastodon/features/filters/select_filter';
import { fetchFilters, createFilter, createFilterStatus } from "mastodon/actions/filters";
import { fetchStatus } from "mastodon/actions/statuses";
import { IconButton } from "mastodon/components/icon_button";
import AddedToFilter from "mastodon/features/filters/added_to_filter";
import SelectFilter from "mastodon/features/filters/select_filter";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
close: { id: "lightbox.close", defaultMessage: "Close" },
});
class FilterModal extends ImmutablePureComponent {
@@ -25,7 +25,7 @@ class FilterModal extends ImmutablePureComponent {
};
state = {
step: 'select',
step: "select",
filterId: null,
isSubmitting: false,
isSubmitted: false,
@@ -38,7 +38,7 @@ class FilterModal extends ImmutablePureComponent {
handleSuccess = () => {
const { dispatch, statusId } = this.props;
dispatch(fetchStatus(statusId, true));
this.setState({ isSubmitting: false, isSubmitted: true, step: 'submitted' });
this.setState({ isSubmitting: false, isSubmitted: true, step: "submitted" });
};
handleFail = () => {
@@ -67,8 +67,8 @@ class FilterModal extends ImmutablePureComponent {
dispatch(createFilter({
title,
context: ['home', 'notifications', 'public', 'thread', 'account'],
action: 'warn',
context: ["home", "notifications", "public", "thread", "account"],
action: "warn",
}, this.handleNewFilterSuccess, this.handleFail));
};
@@ -94,27 +94,27 @@ class FilterModal extends ImmutablePureComponent {
let stepComponent;
switch(step) {
case 'select':
stepComponent = (
<SelectFilter
contextType={contextType}
onSelectFilter={this.handleSelectFilter}
onNewFilter={this.handleNewFilter}
/>
);
break;
case 'create':
stepComponent = null;
break;
case 'submitted':
stepComponent = (
<AddedToFilter
contextType={contextType}
filterId={filterId}
statusId={statusId}
onClose={onClose}
/>
);
case "select":
stepComponent = (
<SelectFilter
contextType={contextType}
onSelectFilter={this.handleSelectFilter}
onNewFilter={this.handleNewFilter}
/>
);
break;
case "create":
stepComponent = null;
break;
case "submitted":
stepComponent = (
<AddedToFilter
contextType={contextType}
filterId={filterId}
statusId={statusId}
onClose={onClose}
/>
);
}
return (
@@ -1,54 +1,53 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from "react-intl";
import classNames from 'classnames';
import classNames from "classnames";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import Textarea from 'react-textarea-autosize';
import { length } from 'stringz';
// eslint-disable-next-line import/extensions
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
// eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
import Textarea from "react-textarea-autosize";
import { length } from "stringz";
import tesseractWorkerPath from "tesseract.js/dist/worker.min.js";
import tesseractCorePath from "tesseract.js-core/tesseract-core.wasm.js";
import Button from 'mastodon/components/button';
import { GIFV } from 'mastodon/components/gifv';
import { IconButton } from 'mastodon/components/icon_button';
import Audio from 'mastodon/features/audio';
import CharacterCounter from 'mastodon/features/compose/components/character_counter';
import UploadProgress from 'mastodon/features/compose/components/upload_progress';
import { Tesseract as fetchTesseract } from 'mastodon/features/ui/util/async-components';
import { me , maxMediaDescChars } from 'mastodon/initial_state';
import { assetHost } from 'mastodon/utils/config';
import Button from "mastodon/components/button";
import { GIFV } from "mastodon/components/gifv";
import { IconButton } from "mastodon/components/icon_button";
import Audio from "mastodon/features/audio";
import CharacterCounter from "mastodon/features/compose/components/character_counter";
import UploadProgress from "mastodon/features/compose/components/upload_progress";
import { Tesseract as fetchTesseract } from "mastodon/features/ui/util/async-components";
import { me , maxMediaDescChars } from "mastodon/initial_state";
import { assetHost } from "mastodon/utils/config";
import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from '../../../actions/compose';
import Video, { getPointerPosition } from '../../video';
import { changeUploadCompose, uploadThumbnail, onChangeMediaDescription, onChangeMediaFocus } from "../../../actions/compose";
import Video, { getPointerPosition } from "../../video";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
apply: { id: 'upload_modal.apply', defaultMessage: 'Apply' },
applying: { id: 'upload_modal.applying', defaultMessage: 'Applying…' },
placeholder: { id: 'upload_modal.description_placeholder', defaultMessage: 'A quick brown fox jumps over the lazy dog' },
chooseImage: { id: 'upload_modal.choose_image', defaultMessage: 'Choose image' },
discardMessage: { id: 'confirmations.discard_edit_media.message', defaultMessage: 'You have unsaved changes to the media description or preview, discard them anyway?' },
discardConfirm: { id: 'confirmations.discard_edit_media.confirm', defaultMessage: 'Discard' },
close: { id: "lightbox.close", defaultMessage: "Close" },
apply: { id: "upload_modal.apply", defaultMessage: "Apply" },
applying: { id: "upload_modal.applying", defaultMessage: "Applying…" },
placeholder: { id: "upload_modal.description_placeholder", defaultMessage: "A quick brown fox jumps over the lazy dog" },
chooseImage: { id: "upload_modal.choose_image", defaultMessage: "Choose image" },
discardMessage: { id: "confirmations.discard_edit_media.message", defaultMessage: "You have unsaved changes to the media description or preview, discard them anyway?" },
discardConfirm: { id: "confirmations.discard_edit_media.confirm", defaultMessage: "Discard" },
});
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
account: state.getIn(['accounts', me]),
isUploadingThumbnail: state.getIn(['compose', 'isUploadingThumbnail']),
description: state.getIn(['compose', 'media_modal', 'description']),
lang: state.getIn(['compose', 'language']),
focusX: state.getIn(['compose', 'media_modal', 'focusX']),
focusY: state.getIn(['compose', 'media_modal', 'focusY']),
dirty: state.getIn(['compose', 'media_modal', 'dirty']),
is_changing_upload: state.getIn(['compose', 'is_changing_upload']),
media: state.getIn(["compose", "media_attachments"]).find(item => item.get("id") === id),
account: state.getIn(["accounts", me]),
isUploadingThumbnail: state.getIn(["compose", "isUploadingThumbnail"]),
description: state.getIn(["compose", "media_modal", "description"]),
lang: state.getIn(["compose", "language"]),
focusX: state.getIn(["compose", "media_modal", "focusX"]),
focusY: state.getIn(["compose", "media_modal", "focusY"]),
dirty: state.getIn(["compose", "media_modal", "dirty"]),
is_changing_upload: state.getIn(["compose", "is_changing_upload"]),
});
const mapDispatchToProps = (dispatch, { id }) => ({
@@ -71,9 +70,9 @@ const mapDispatchToProps = (dispatch, { id }) => ({
});
const removeExtraLineBreaks = str => str.replace(/\n\n/g, '******')
.replace(/\n/g, ' ')
.replace(/\*\*\*\*\*\*/g, '\n\n');
const removeExtraLineBreaks = str => str.replace(/\n\n/g, "******")
.replace(/\n/g, " ")
.replace(/\*\*\*\*\*\*/g, "\n\n");
class ImageLoader extends PureComponent {
@@ -89,7 +88,7 @@ class ImageLoader extends PureComponent {
componentDidMount() {
const image = new Image();
image.addEventListener('load', () => this.setState({ loading: false }));
image.addEventListener("load", () => this.setState({ loading: false }));
image.src = this.props.src;
}
@@ -124,25 +123,25 @@ class FocalPointModal extends ImmutablePureComponent {
dirty: false,
progress: 0,
loading: true,
ocrStatus: '',
ocrStatus: "",
};
componentWillUnmount () {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
document.removeEventListener("mousemove", this.handleMouseMove);
document.removeEventListener("mouseup", this.handleMouseUp);
}
handleMouseDown = e => {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
document.addEventListener("mousemove", this.handleMouseMove);
document.addEventListener("mouseup", this.handleMouseUp);
this.updatePosition(e);
this.setState({ dragging: true });
};
handleTouchStart = e => {
document.addEventListener('touchmove', this.handleMouseMove);
document.addEventListener('touchend', this.handleTouchEnd);
document.addEventListener("touchmove", this.handleMouseMove);
document.addEventListener("touchend", this.handleTouchEnd);
this.updatePosition(e);
this.setState({ dragging: true });
@@ -153,15 +152,15 @@ class FocalPointModal extends ImmutablePureComponent {
};
handleMouseUp = () => {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
document.removeEventListener("mousemove", this.handleMouseMove);
document.removeEventListener("mouseup", this.handleMouseUp);
this.setState({ dragging: false });
};
handleTouchEnd = () => {
document.removeEventListener('touchmove', this.handleMouseMove);
document.removeEventListener('touchend', this.handleTouchEnd);
document.removeEventListener("touchmove", this.handleMouseMove);
document.removeEventListener("touchend", this.handleTouchEnd);
this.setState({ dragging: false });
};
@@ -223,20 +222,20 @@ class FocalPointModal extends ImmutablePureComponent {
corePath: tesseractCorePath,
langPath: `${assetHost}/ocr/lang-data/`,
logger: ({ status, progress }) => {
if (status === 'recognizing text') {
this.setState({ ocrStatus: 'detecting', progress });
if (status === "recognizing text") {
this.setState({ ocrStatus: "detecting", progress });
} else {
this.setState({ ocrStatus: 'preparing', progress });
this.setState({ ocrStatus: "preparing", progress });
}
},
cacheMethod: refreshCache ? 'refresh' : 'write',
cacheMethod: refreshCache ? "refresh" : "write",
});
let media_url = media.get('url');
let media_url = media.get("url");
if (window.URL && URL.createObjectURL) {
try {
media_url = URL.createObjectURL(media.get('file'));
media_url = URL.createObjectURL(media.get("file"));
} catch (error) {
console.error(error);
}
@@ -244,8 +243,8 @@ class FocalPointModal extends ImmutablePureComponent {
return (async () => {
await worker.load();
await worker.loadLanguage('eng');
await worker.initialize('eng');
await worker.loadLanguage("eng");
await worker.initialize("eng");
const { data: { text } } = await worker.recognize(media_url);
this.setState({ detecting: false });
this.props.onChangeDescription(removeExtraLineBreaks(text));
@@ -283,27 +282,27 @@ class FocalPointModal extends ImmutablePureComponent {
const x = (focusX / 2) + .5;
const y = (focusY / -2) + .5;
const width = media.getIn(['meta', 'original', 'width']) || null;
const height = media.getIn(['meta', 'original', 'height']) || null;
const focals = ['image', 'gifv'].includes(media.get('type'));
const thumbnailable = ['audio', 'video'].includes(media.get('type'));
const width = media.getIn(["meta", "original", "width"]) || null;
const height = media.getIn(["meta", "original", "height"]) || null;
const focals = ["image", "gifv"].includes(media.get("type"));
const thumbnailable = ["audio", "video"].includes(media.get("type"));
const previewRatio = 16/9;
const previewWidth = 200;
const previewHeight = previewWidth / previewRatio;
let descriptionLabel = null;
let descriptionLabel;
if (media.get('type') === 'audio') {
if (media.get("type") === "audio") {
descriptionLabel = <FormattedMessage id='upload_form.audio_description' defaultMessage='Describe for people who are hard of hearing' />;
} else if (media.get('type') === 'video') {
} else if (media.get("type") === "video") {
descriptionLabel = <FormattedMessage id='upload_form.video_description' defaultMessage='Describe for people who are deaf, hard of hearing, blind or have low vision' />;
} else {
descriptionLabel = <FormattedMessage id='upload_form.description' defaultMessage='Describe for people who are blind or have low vision' />;
}
let ocrMessage = '';
if (ocrStatus === 'detecting') {
let ocrMessage;
if (ocrStatus === "detecting") {
ocrMessage = <FormattedMessage id='upload_modal.analyzing_picture' defaultMessage='Analyzing picture…' />;
} else {
ocrMessage = <FormattedMessage id='upload_modal.preparing_ocr' defaultMessage='Preparing OCR…' />;
@@ -324,10 +323,10 @@ class FocalPointModal extends ImmutablePureComponent {
<>
<label className='setting-text-label' htmlFor='upload-modal__thumbnail'><FormattedMessage id='upload_form.thumbnail' defaultMessage='Change thumbnail' /></label>
<Button disabled={isUploadingThumbnail || !media.get('unattached')} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
<Button disabled={isUploadingThumbnail || !media.get("unattached")} text={intl.formatMessage(messages.chooseImage)} onClick={this.handleFileInputClick} />
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.chooseImage)}</span>
<span style={{ display: "none" }}>{intl.formatMessage(messages.chooseImage)}</span>
<input
id='upload-modal__thumbnail'
@@ -335,7 +334,7 @@ class FocalPointModal extends ImmutablePureComponent {
type='file'
accept='image/png,image/jpeg'
onChange={this.handleThumbnailChange}
style={{ display: 'none' }}
style={{ display: "none" }}
disabled={isUploadingThumbnail || is_changing_upload}
/>
</label>
@@ -352,7 +351,7 @@ class FocalPointModal extends ImmutablePureComponent {
<Textarea
id='upload-modal__description'
className='setting-text light'
value={detecting ? '…' : description}
value={detecting ? "…" : description}
lang={lang}
onChange={this.handleChange}
onKeyDown={this.handleKeyDown}
@@ -368,13 +367,13 @@ class FocalPointModal extends ImmutablePureComponent {
<div className='setting-text__toolbar'>
<button
type='button'
disabled={detecting || media.get('type') !== 'image' || is_changing_upload}
disabled={detecting || media.get("type") !== "image" || is_changing_upload}
className='link-button'
onClick={this.handleTextDetection}
>
<FormattedMessage id='upload_modal.detect_text' defaultMessage='Detect text from picture' />
</button>
<CharacterCounter max={maxMediaDescChars} text={detecting ? '' : description} />
<CharacterCounter max={maxMediaDescChars} text={detecting ? "" : description} />
</div>
<Button
@@ -386,13 +385,13 @@ class FocalPointModal extends ImmutablePureComponent {
<div className='focal-point-modal__content'>
{focals && (
<div className={classNames('focal-point', { dragging })} ref={this.setRef} onMouseDown={this.handleMouseDown} onTouchStart={this.handleTouchStart}>
{media.get('type') === 'image' && <ImageLoader src={media.get('url')} width={width} height={height} alt='' />}
{media.get('type') === 'gifv' && <GIFV src={media.get('url')} key={media.get('url')} width={width} height={height} />}
<div className={classNames("focal-point", { dragging })} ref={this.setRef} onMouseDown={this.handleMouseDown} onTouchStart={this.handleTouchStart}>
{media.get("type") === "image" && <ImageLoader src={media.get("url")} width={width} height={height} alt='' />}
{media.get("type") === "gifv" && <GIFV src={media.get("url")} key={media.get("url")} width={width} height={height} />}
<div className='focal-point__preview'>
<strong><FormattedMessage id='upload_modal.preview_label' defaultMessage='Preview ({ratio})' values={{ ratio: '16:9' }} /></strong>
<div style={{ width: previewWidth, height: previewHeight, backgroundImage: `url(${media.get('preview_url')})`, backgroundSize: 'cover', backgroundPosition: `${x * 100}% ${y * 100}%` }} />
<strong><FormattedMessage id='upload_modal.preview_label' defaultMessage='Preview ({ratio})' values={{ ratio: "16:9" }} /></strong>
<div style={{ width: previewWidth, height: previewHeight, backgroundImage: `url(${media.get("preview_url")})`, backgroundSize: "cover", backgroundPosition: `${x * 100}% ${y * 100}%` }} />
</div>
<div className='focal-point__reticle' style={{ top: `${y * 100}%`, left: `${x * 100}%` }} />
@@ -400,27 +399,27 @@ class FocalPointModal extends ImmutablePureComponent {
</div>
)}
{media.get('type') === 'video' && (
{media.get("type") === "video" && (
<Video
preview={media.get('preview_url')}
frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
blurhash={media.get('blurhash')}
src={media.get('url')}
preview={media.get("preview_url")}
frameRate={media.getIn(["meta", "original", "frame_rate"])}
blurhash={media.get("blurhash")}
src={media.get("url")}
detailed
inline
editable
/>
)}
{media.get('type') === 'audio' && (
{media.get("type") === "audio" && (
<Audio
src={media.get('url')}
duration={media.getIn(['meta', 'original', 'duration'], 0)}
src={media.get("url")}
duration={media.getIn(["meta", "original", "duration"], 0)}
height={150}
poster={media.get('preview_url') || account.get('avatar_static')}
backgroundColor={media.getIn(['meta', 'colors', 'background'])}
foregroundColor={media.getIn(['meta', 'colors', 'foreground'])}
accentColor={media.getIn(['meta', 'colors', 'accent'])}
poster={media.get("preview_url") || account.get("avatar_static")}
backgroundColor={media.getIn(["meta", "colors", "background"])}
foregroundColor={media.getIn(["meta", "colors", "foreground"])}
accentColor={media.getIn(["meta", "colors", "accent"])}
editable
/>
)}
@@ -1,21 +1,21 @@
import PropTypes from 'prop-types';
import { Component } from 'react';
import PropTypes from "prop-types";
import { Component } from "react";
import { injectIntl, defineMessages } from 'react-intl';
import { injectIntl, defineMessages } from "react-intl";
import { List as ImmutableList } from 'immutable';
import { connect } from 'react-redux';
import { List as ImmutableList } from "immutable";
import { connect } from "react-redux";
import { fetchFollowRequests } from 'mastodon/actions/accounts';
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import ColumnLink from 'mastodon/features/ui/components/column_link';
import { fetchFollowRequests } from "mastodon/actions/accounts";
import { IconWithBadge } from "mastodon/components/icon_with_badge";
import ColumnLink from "mastodon/features/ui/components/column_link";
const messages = defineMessages({
text: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
text: { id: "navigation_bar.follow_requests", defaultMessage: "Follow requests" },
});
const mapStateToProps = state => ({
count: state.getIn(['user_lists', 'follow_requests', 'items'], ImmutableList()).size,
count: state.getIn(["user_lists", "follow_requests", "items"], ImmutableList()).size,
});
class FollowRequestsColumnLink extends Component {
@@ -1,42 +1,42 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from "react-intl";
import { Link, withRouter } from 'react-router-dom';
import { Link, withRouter } from "react-router-dom";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { openModal } from 'mastodon/actions/modal';
import { fetchServer } from 'mastodon/actions/server';
import { Avatar } from 'mastodon/components/avatar';
import { Icon } from 'mastodon/components/icon';
import { WordmarkLogo, SymbolLogo } from 'mastodon/components/logo';
import { registrationsOpen, me, sso_redirect } from 'mastodon/initial_state';
import { openModal } from "mastodon/actions/modal";
import { fetchServer } from "mastodon/actions/server";
import { Avatar } from "mastodon/components/avatar";
import { Icon } from "mastodon/components/icon";
import { WordmarkLogo, SymbolLogo } from "mastodon/components/logo";
import { registrationsOpen, me, sso_redirect } from "mastodon/initial_state";
const Account = connect(state => ({
account: state.getIn(['accounts', me]),
account: state.getIn(["accounts", me]),
}))(({ account }) => (
<Link to={`/@${account.get('acct')}`} title={account.get('acct')}>
<Link to={`/@${account.get("acct")}`} title={account.get("acct")}>
<Avatar account={account} size={35} />
</Link>
));
const messages = defineMessages({
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
search: { id: "navigation_bar.search", defaultMessage: "Search" },
});
const mapStateToProps = (state) => ({
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
signupUrl: state.getIn(["server", "server", "registrations", "url"], null) || "/auth/sign_up",
});
const mapDispatchToProps = (dispatch) => ({
openClosedRegistrationsModal() {
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
dispatch(openModal({ modalType: "CLOSED_REGISTRATIONS" }));
},
dispatchServer() {
dispatch(fetchServer());
}
},
});
class Header extends PureComponent {
@@ -67,8 +67,8 @@ class Header extends PureComponent {
if (signedIn) {
content = (
<>
{location.pathname !== '/search' && <Link to='/search' className='button button-secondary' aria-label={intl.formatMessage(messages.search)}><Icon id='search' /></Link>}
{location.pathname !== '/publish' && <Link to='/publish' className='button button-secondary'><FormattedMessage id='compose_form.publish_form' defaultMessage='New post' /></Link>}
{location.pathname !== "/search" && <Link to='/search' className='button button-secondary' aria-label={intl.formatMessage(messages.search)}><Icon id='search' /></Link>}
{location.pathname !== "/publish" && <Link to='/publish' className='button button-secondary'><FormattedMessage id='compose_form.publish_form' defaultMessage='New post' /></Link>}
<Account />
</>
);
@@ -1,11 +1,11 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import classNames from 'classnames';
import classNames from "classnames";
import { LoadingBar } from 'react-redux-loading-bar';
import { LoadingBar } from "react-redux-loading-bar";
import ZoomableImage from './zoomable_image';
import ZoomableImage from "./zoomable_image";
export default class ImageLoader extends PureComponent {
@@ -21,8 +21,8 @@ export default class ImageLoader extends PureComponent {
};
static defaultProps = {
alt: '',
lang: '',
alt: "",
lang: "",
width: null,
height: null,
};
@@ -40,7 +40,7 @@ export default class ImageLoader extends PureComponent {
if (!this.canvas) {
return null;
}
this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
this._canvasContext = this._canvasContext || this.canvas.getContext("2d");
return this._canvasContext;
}
@@ -75,8 +75,8 @@ export default class ImageLoader extends PureComponent {
loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => {
const image = new Image();
const removeEventListeners = () => {
image.removeEventListener('error', handleError);
image.removeEventListener('load', handleLoad);
image.removeEventListener("error", handleError);
image.removeEventListener("load", handleLoad);
};
const handleError = () => {
removeEventListeners();
@@ -87,8 +87,8 @@ export default class ImageLoader extends PureComponent {
this.canvasContext.drawImage(image, 0, 0, width, height);
resolve();
};
image.addEventListener('error', handleError);
image.addEventListener('load', handleLoad);
image.addEventListener("error", handleError);
image.addEventListener("load", handleLoad);
image.src = previewSrc;
this.removers.push(removeEventListeners);
});
@@ -101,8 +101,8 @@ export default class ImageLoader extends PureComponent {
loadOriginalImage = ({ src }) => new Promise((resolve, reject) => {
const image = new Image();
const removeEventListeners = () => {
image.removeEventListener('error', handleError);
image.removeEventListener('load', handleLoad);
image.removeEventListener("error", handleError);
image.removeEventListener("load", handleLoad);
};
const handleError = () => {
removeEventListeners();
@@ -112,8 +112,8 @@ export default class ImageLoader extends PureComponent {
removeEventListeners();
resolve();
};
image.addEventListener('error', handleError);
image.addEventListener('load', handleLoad);
image.addEventListener("error", handleError);
image.addEventListener("load", handleLoad);
image.src = src;
this.removers.push(removeEventListeners);
});
@@ -125,21 +125,23 @@ export default class ImageLoader extends PureComponent {
hasSize () {
const { width, height } = this.props;
return typeof width === 'number' && typeof height === 'number';
return typeof width === "number" && typeof height === "number";
}
setCanvasRef = c => {
this.canvas = c;
if (c) this.setState({ width: c.offsetWidth });
if (c) {
this.setState({ width: c.offsetWidth });
}
};
render () {
const { alt, lang, src, width, height, onClick } = this.props;
const { loading } = this.state;
const className = classNames('image-loader', {
'image-loader--loading': loading,
'image-loader--amorphous': !this.hasSize(),
const className = classNames("image-loader", {
"image-loader--loading": loading,
"image-loader--amorphous": !this.hasSize(),
});
return (
@@ -1,16 +1,16 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from "react-intl";
import classNames from 'classnames';
import classNames from "classnames";
import { IconButton } from 'mastodon/components/icon_button';
import { IconButton } from "mastodon/components/icon_button";
import ImageLoader from './image_loader';
import ImageLoader from "./image_loader";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
close: { id: "lightbox.close", defaultMessage: "Close" },
});
class ImageModal extends PureComponent {
@@ -36,8 +36,8 @@ class ImageModal extends PureComponent {
const { intl, src, alt, onClose } = this.props;
const { navigationHidden } = this.state;
const navigationClassName = classNames('media-modal__navigation', {
'media-modal__navigation--hidden': navigationHidden,
const navigationClassName = classNames("media-modal__navigation", {
"media-modal__navigation--hidden": navigationHidden,
});
return (
@@ -1,26 +1,26 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage, defineMessages, injectIntl } from 'react-intl';
import { FormattedMessage, defineMessages, injectIntl } from "react-intl";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { openModal } from 'mastodon/actions/modal';
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from 'mastodon/initial_state';
import { PERMISSION_INVITE_USERS } from 'mastodon/permissions';
import { logOut } from 'mastodon/utils/log_out';
import { openModal } from "mastodon/actions/modal";
import { domain, version, source_url, statusPageUrl, profile_directory as profileDirectory } from "mastodon/initial_state";
import { PERMISSION_INVITE_USERS } from "mastodon/permissions";
import { logOut } from "mastodon/utils/log_out";
const messages = defineMessages({
logoutMessage: { id: 'confirmations.logout.message', defaultMessage: 'Are you sure you want to log out?' },
logoutConfirm: { id: 'confirmations.logout.confirm', defaultMessage: 'Log out' },
logoutMessage: { id: "confirmations.logout.message", defaultMessage: "Are you sure you want to log out?" },
logoutConfirm: { id: "confirmations.logout.confirm", defaultMessage: "Log out" },
});
const mapDispatchToProps = (dispatch, { intl }) => ({
onLogout () {
dispatch(openModal({
modalType: 'CONFIRM',
modalType: "CONFIRM",
modalProps: {
message: intl.formatMessage(messages.logoutMessage),
confirm: intl.formatMessage(messages.logoutConfirm),
@@ -59,18 +59,18 @@ class LinkFooter extends PureComponent {
const canInvite = signedIn && ((permissions & PERMISSION_INVITE_USERS) === PERMISSION_INVITE_USERS);
const canProfileDirectory = profileDirectory;
const DividingCircle = <span aria-hidden>{' · '}</span>;
const DividingCircle = <span aria-hidden>{" · "}</span>;
return (
<div className='link-footer'>
<p>
<strong>{domain}</strong>:
{' '}
<Link to='/about' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
{" "}
<Link to='/about' target={multiColumn ? "_blank" : undefined}><FormattedMessage id='footer.about' defaultMessage='About' /></Link>
{statusPageUrl && (
<>
{DividingCircle}
<a href={statusPageUrl} target='_blank' rel='noopener'><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
<a href={statusPageUrl} target='_blank' rel="noopener noreferrer"><FormattedMessage id='footer.status' defaultMessage='Status' /></a>
</>
)}
{canInvite && (
@@ -86,21 +86,21 @@ class LinkFooter extends PureComponent {
</>
)}
{DividingCircle}
<Link to='/privacy-policy' target={multiColumn ? '_blank' : undefined}><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
<Link to='/privacy-policy' target={multiColumn ? "_blank" : undefined}><FormattedMessage id='footer.privacy_policy' defaultMessage='Privacy policy' /></Link>
</p>
<p>
<strong>Mastodon</strong>:
{' '}
<a href='https://joinmastodon.org' target='_blank'><FormattedMessage id='footer.about' defaultMessage='About' /></a>
{" "}
<a href='https://joinmastodon.org' target='_blank' rel="noreferrer"><FormattedMessage id='footer.about' defaultMessage='About' /></a>
{DividingCircle}
<a href='https://joinmastodon.org/apps' target='_blank'><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
<a href='https://joinmastodon.org/apps' target='_blank' rel="noreferrer"><FormattedMessage id='footer.get_app' defaultMessage='Get the app' /></a>
{DividingCircle}
<Link to='/keyboard-shortcuts'><FormattedMessage id='footer.keyboard_shortcuts' defaultMessage='Keyboard shortcuts' /></Link>
{DividingCircle}
<a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='footer.source_code' defaultMessage='View source code' /></a>
{DividingCircle}
<span class='version'>v{version}</span>
<span className='version'>v{version}</span>
</p>
</div>
);
@@ -1,22 +1,22 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { withRouter } from 'react-router-dom';
import { withRouter } from "react-router-dom";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import { createSelector } from "reselect";
import { fetchLists } from 'mastodon/actions/lists';
import { fetchLists } from "mastodon/actions/lists";
import ColumnLink from './column_link';
import ColumnLink from "./column_link";
const getOrderedLists = createSelector([state => state.get('lists')], lists => {
const getOrderedLists = createSelector([state => state.get("lists")], lists => {
if (!lists) {
return lists;
}
return lists.toList().filter(item => !!item).sort((a, b) => a.get('title').localeCompare(b.get('title'))).take(4);
return lists.toList().filter(item => !!item).sort((a, b) => a.get("title").localeCompare(b.get("title"))).take(4);
});
const mapStateToProps = state => ({
@@ -47,7 +47,7 @@ class ListPanel extends ImmutablePureComponent {
<hr />
{lists.map(list => (
<ColumnLink icon='list-ul' key={list.get('id')} strict text={list.get('title')} to={`/lists/${list.get('id')}`} transparent />
<ColumnLink icon='list-ul' key={list.get("id")} strict text={list.get("title")} to={`/lists/${list.get("id")}`} transparent />
))}
</div>
);
@@ -1,28 +1,28 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from "react-intl";
import classNames from 'classnames';
import classNames from "classnames";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import ReactSwipeableViews from 'react-swipeable-views';
import ReactSwipeableViews from "react-swipeable-views";
import { getAverageFromBlurhash } from 'mastodon/blurhash';
import { GIFV } from 'mastodon/components/gifv';
import { Icon } from 'mastodon/components/icon';
import { IconButton } from 'mastodon/components/icon_button';
import Footer from 'mastodon/features/picture_in_picture/components/footer';
import Video from 'mastodon/features/video';
import { disableSwiping } from 'mastodon/initial_state';
import { getAverageFromBlurhash } from "mastodon/blurhash";
import { GIFV } from "mastodon/components/gifv";
import { Icon } from "mastodon/components/icon";
import { IconButton } from "mastodon/components/icon_button";
import Footer from "mastodon/features/picture_in_picture/components/footer";
import Video from "mastodon/features/video";
import { disableSwiping } from "mastodon/initial_state";
import ImageLoader from './image_loader';
import ImageLoader from "./image_loader";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
previous: { id: 'lightbox.previous', defaultMessage: 'Previous' },
next: { id: 'lightbox.next', defaultMessage: 'Next' },
close: { id: "lightbox.close", defaultMessage: "Close" },
previous: { id: "lightbox.previous", defaultMessage: "Previous" },
next: { id: "lightbox.next", defaultMessage: "Next" },
});
class MediaModal extends ImmutablePureComponent {
@@ -71,7 +71,7 @@ class MediaModal extends ImmutablePureComponent {
};
handleChangeIndex = (e) => {
const index = Number(e.currentTarget.getAttribute('data-index'));
const index = Number(e.currentTarget.getAttribute("data-index"));
this.setState({
index: index % this.props.media.size,
@@ -81,21 +81,21 @@ class MediaModal extends ImmutablePureComponent {
handleKeyDown = (e) => {
switch(e.key) {
case 'ArrowLeft':
this.handlePrevClick();
e.preventDefault();
e.stopPropagation();
break;
case 'ArrowRight':
this.handleNextClick();
e.preventDefault();
e.stopPropagation();
break;
case "ArrowLeft":
this.handlePrevClick();
e.preventDefault();
e.stopPropagation();
break;
case "ArrowRight":
this.handleNextClick();
e.preventDefault();
e.stopPropagation();
break;
}
};
componentDidMount () {
window.addEventListener('keydown', this.handleKeyDown, false);
window.addEventListener("keydown", this.handleKeyDown, false);
this._sendBackgroundColor();
}
@@ -109,7 +109,7 @@ class MediaModal extends ImmutablePureComponent {
_sendBackgroundColor () {
const { media, onChangeBackgroundColor } = this.props;
const index = this.getIndex();
const blurhash = media.getIn([index, 'blurhash']);
const blurhash = media.getIn([index, "blurhash"]);
if (blurhash) {
const backgroundColor = getAverageFromBlurhash(blurhash);
@@ -118,7 +118,7 @@ class MediaModal extends ImmutablePureComponent {
}
componentWillUnmount () {
window.removeEventListener('keydown', this.handleKeyDown);
window.removeEventListener("keydown", this.handleKeyDown);
this.props.onChangeBackgroundColor(null);
}
@@ -143,36 +143,36 @@ class MediaModal extends ImmutablePureComponent {
const rightNav = media.size > 1 && <button tabIndex={0} className='media-modal__nav media-modal__nav--right' onClick={this.handleNextClick} aria-label={intl.formatMessage(messages.next)}><Icon id='chevron-right' fixedWidth /></button>;
const content = media.map((image) => {
const width = image.getIn(['meta', 'original', 'width']) || null;
const height = image.getIn(['meta', 'original', 'height']) || null;
const description = image.getIn(['translation', 'description']) || image.get('description');
const width = image.getIn(["meta", "original", "width"]) || null;
const height = image.getIn(["meta", "original", "height"]) || null;
const description = image.getIn(["translation", "description"]) || image.get("description");
if (image.get('type') === 'image') {
if (image.get("type") === "image") {
return (
<ImageLoader
previewSrc={image.get('preview_url')}
src={image.get('url')}
previewSrc={image.get("preview_url")}
src={image.get("url")}
width={width}
height={height}
alt={description}
lang={lang}
key={image.get('url')}
key={image.get("url")}
onClick={this.toggleNavigation}
zoomButtonHidden={this.state.zoomButtonHidden}
/>
);
} else if (image.get('type') === 'video') {
} else if (image.get("type") === "video") {
const { currentTime, autoPlay, volume } = this.props;
return (
<Video
preview={image.get('preview_url')}
blurhash={image.get('blurhash')}
src={image.get('url')}
width={image.get('width')}
height={image.get('height')}
frameRate={image.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${image.getIn(['meta', 'original', 'width'])} / ${image.getIn(['meta', 'original', 'height'])}`}
preview={image.get("preview_url")}
blurhash={image.get("blurhash")}
src={image.get("url")}
width={image.get("width")}
height={image.get("height")}
frameRate={image.getIn(["meta", "original", "frame_rate"])}
aspectRatio={`${image.getIn(["meta", "original", "width"])} / ${image.getIn(["meta", "original", "height"])}`}
currentTime={currentTime || 0}
autoPlay={autoPlay || false}
volume={volume || 1}
@@ -180,16 +180,16 @@ class MediaModal extends ImmutablePureComponent {
detailed
alt={description}
lang={lang}
key={image.get('url')}
key={image.get("url")}
/>
);
} else if (image.get('type') === 'gifv') {
} else if (image.get("type") === "gifv") {
return (
<GIFV
src={image.get('url')}
src={image.get("url")}
width={width}
height={height}
key={image.get('url')}
key={image.get("url")}
alt={description}
lang={lang}
onClick={this.toggleNavigation}
@@ -205,23 +205,23 @@ class MediaModal extends ImmutablePureComponent {
// browsers when it's address bar is visible.
// https://developers.google.com/web/updates/2016/12/url-bar-resizing
const swipeableViewsStyle = {
width: '100%',
height: '100%',
width: "100%",
height: "100%",
};
const containerStyle = {
alignItems: 'center', // center vertically
alignItems: "center", // center vertically
};
const navigationClassName = classNames('media-modal__navigation', {
'media-modal__navigation--hidden': navigationHidden,
const navigationClassName = classNames("media-modal__navigation", {
"media-modal__navigation--hidden": navigationHidden,
});
let pagination;
if (media.size > 1) {
pagination = media.map((item, i) => (
<button key={i} className={classNames('media-modal__page-dot', { active: i === index })} data-index={i} onClick={this.handleChangeIndex}>
<button key={i} className={classNames("media-modal__page-dot", { active: i === index })} data-index={i} onClick={this.handleChangeIndex}>
{i + 1}
</button>
));
@@ -1,4 +1,4 @@
import { LoadingIndicator } from '../../../components/loading_indicator';
import { LoadingIndicator } from "../../../components/loading_indicator";
// Keep the markup in sync with <BundleModalError />
// (make sure they have the same dimensions)
@@ -1,9 +1,9 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { Helmet } from 'react-helmet';
import { Helmet } from "react-helmet";
import Base from 'mastodon/components/modal_root';
import Base from "mastodon/components/modal_root";
import {
MuteModal,
BlockModal,
@@ -16,42 +16,42 @@ import {
InteractionModal,
SubscribedLanguagesModal,
ClosedRegistrationsModal,
} from 'mastodon/features/ui/util/async-components';
import { getScrollbarWidth } from 'mastodon/utils/scrollbar';
} from "mastodon/features/ui/util/async-components";
import { getScrollbarWidth } from "mastodon/utils/scrollbar";
import BundleContainer from '../containers/bundle_container';
import BundleContainer from "../containers/bundle_container";
import ActionsModal from './actions_modal';
import AudioModal from './audio_modal';
import BoostModal from './boost_modal';
import BundleModalError from './bundle_modal_error';
import ConfirmationModal from './confirmation_modal';
import FocalPointModal from './focal_point_modal';
import ImageModal from './image_modal';
import MediaModal from './media_modal';
import ModalLoading from './modal_loading';
import VideoModal from './video_modal';
import ActionsModal from "./actions_modal";
import AudioModal from "./audio_modal";
import BoostModal from "./boost_modal";
import BundleModalError from "./bundle_modal_error";
import ConfirmationModal from "./confirmation_modal";
import FocalPointModal from "./focal_point_modal";
import ImageModal from "./image_modal";
import MediaModal from "./media_modal";
import ModalLoading from "./modal_loading";
import VideoModal from "./video_modal";
export const MODAL_COMPONENTS = {
'MEDIA': () => Promise.resolve({ default: MediaModal }),
'VIDEO': () => Promise.resolve({ default: VideoModal }),
'AUDIO': () => Promise.resolve({ default: AudioModal }),
'IMAGE': () => Promise.resolve({ default: ImageModal }),
'BOOST': () => Promise.resolve({ default: BoostModal }),
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
'MUTE': MuteModal,
'BLOCK': BlockModal,
'REPORT': ReportModal,
'ACTIONS': () => Promise.resolve({ default: ActionsModal }),
'EMBED': EmbedModal,
'LIST_EDITOR': ListEditor,
'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
'LIST_ADDER': ListAdder,
'COMPARE_HISTORY': CompareHistoryModal,
'FILTER': FilterModal,
'SUBSCRIBED_LANGUAGES': SubscribedLanguagesModal,
'INTERACTION': InteractionModal,
'CLOSED_REGISTRATIONS': ClosedRegistrationsModal,
"MEDIA": () => Promise.resolve({ default: MediaModal }),
"VIDEO": () => Promise.resolve({ default: VideoModal }),
"AUDIO": () => Promise.resolve({ default: AudioModal }),
"IMAGE": () => Promise.resolve({ default: ImageModal }),
"BOOST": () => Promise.resolve({ default: BoostModal }),
"CONFIRM": () => Promise.resolve({ default: ConfirmationModal }),
"MUTE": MuteModal,
"BLOCK": BlockModal,
"REPORT": ReportModal,
"ACTIONS": () => Promise.resolve({ default: ActionsModal }),
"EMBED": EmbedModal,
"LIST_EDITOR": ListEditor,
"FOCAL_POINT": () => Promise.resolve({ default: FocalPointModal }),
"LIST_ADDER": ListAdder,
"COMPARE_HISTORY": CompareHistoryModal,
"FILTER": FilterModal,
"SUBSCRIBED_LANGUAGES": SubscribedLanguagesModal,
"INTERACTION": InteractionModal,
"CLOSED_REGISTRATIONS": ClosedRegistrationsModal,
};
export default class ModalRoot extends PureComponent {
@@ -73,11 +73,11 @@ export default class ModalRoot extends PureComponent {
componentDidUpdate (prevProps, prevState, { visible }) {
if (visible) {
document.body.classList.add('with-modals--active');
document.body.classList.add("with-modals--active");
document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
} else {
document.body.classList.remove('with-modals--active');
document.documentElement.style.marginRight = '0';
document.body.classList.remove("with-modals--active");
document.documentElement.style.marginRight = "0";
}
}
@@ -86,7 +86,7 @@ export default class ModalRoot extends PureComponent {
};
renderLoading = modalId => () => {
return ['MEDIA', 'VIDEO', 'BOOST', 'CONFIRM', 'ACTIONS'].indexOf(modalId) === -1 ? <ModalLoading /> : null;
return ["MEDIA", "VIDEO", "BOOST", "CONFIRM", "ACTIONS"].indexOf(modalId) === -1 ? <ModalLoading /> : null;
};
renderError = (props) => {
@@ -116,7 +116,7 @@ export default class ModalRoot extends PureComponent {
<>
<BundleContainer fetchComponent={MODAL_COMPONENTS[type]} loading={this.renderLoading(type)} error={this.renderError} renderDelay={200}>
{(SpecificComponent) => {
const ref = typeof SpecificComponent !== 'function' ? this.setModalRef : undefined;
const ref = typeof SpecificComponent !== "function" ? this.setModalRef : undefined;
return <SpecificComponent {...props} onChangeBackgroundColor={this.setBackgroundColor} onClose={this.handleClose} ref={ref} />;
}}
</BundleContainer>
@@ -1,36 +1,36 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl, FormattedMessage } from "react-intl";
import { connect } from 'react-redux';
import { connect } from "react-redux";
import Toggle from 'react-toggle';
import Toggle from "react-toggle";
import { muteAccount } from '../../../actions/accounts';
import { closeModal } from '../../../actions/modal';
import { toggleHideNotifications, changeMuteDuration } from '../../../actions/mutes';
import Button from '../../../components/button';
import { muteAccount } from "../../../actions/accounts";
import { closeModal } from "../../../actions/modal";
import { toggleHideNotifications, changeMuteDuration } from "../../../actions/mutes";
import Button from "../../../components/button";
const messages = defineMessages({
minutes: { id: 'intervals.full.minutes', defaultMessage: '{number, plural, one {# minute} other {# minutes}}' },
hours: { id: 'intervals.full.hours', defaultMessage: '{number, plural, one {# hour} other {# hours}}' },
days: { id: 'intervals.full.days', defaultMessage: '{number, plural, one {# day} other {# days}}' },
indefinite: { id: 'mute_modal.indefinite', defaultMessage: 'Indefinite' },
minutes: { id: "intervals.full.minutes", defaultMessage: "{number, plural, one {# minute} other {# minutes}}" },
hours: { id: "intervals.full.hours", defaultMessage: "{number, plural, one {# hour} other {# hours}}" },
days: { id: "intervals.full.days", defaultMessage: "{number, plural, one {# day} other {# days}}" },
indefinite: { id: "mute_modal.indefinite", defaultMessage: "Indefinite" },
});
const mapStateToProps = state => {
return {
account: state.getIn(['mutes', 'new', 'account']),
notifications: state.getIn(['mutes', 'new', 'notifications']),
muteDuration: state.getIn(['mutes', 'new', 'duration']),
account: state.getIn(["mutes", "new", "account"]),
notifications: state.getIn(["mutes", "new", "notifications"]),
muteDuration: state.getIn(["mutes", "new", "duration"]),
};
};
const mapDispatchToProps = dispatch => {
return {
onConfirm(account, notifications, muteDuration) {
dispatch(muteAccount(account.get('id'), notifications, muteDuration));
dispatch(muteAccount(account.get("id"), notifications, muteDuration));
},
onClose() {
@@ -98,7 +98,7 @@ class MuteModal extends PureComponent {
<FormattedMessage
id='confirmations.mute.message'
defaultMessage='Are you sure you want to mute {name}?'
values={{ name: <strong>@{account.get('acct')}</strong> }}
values={{ name: <strong>@{account.get("acct")}</strong> }}
/>
</p>
<p className='mute-modal__explanation'>
@@ -1,37 +1,37 @@
import PropTypes from 'prop-types';
import { Component } from 'react';
import PropTypes from "prop-types";
import { Component } from "react";
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from "react-intl";
import { Link } from 'react-router-dom';
import { Link } from "react-router-dom";
import { WordmarkLogo } from 'mastodon/components/logo';
import NavigationPortal from 'mastodon/components/navigation_portal';
import { timelinePreview, trendsEnabled } from 'mastodon/initial_state';
import { transientSingleColumn } from 'mastodon/is_mobile';
import { WordmarkLogo } from "mastodon/components/logo";
import NavigationPortal from "mastodon/components/navigation_portal";
import { timelinePreview, trendsEnabled } from "mastodon/initial_state";
import { transientSingleColumn } from "mastodon/is_mobile";
import ColumnLink from './column_link';
import DisabledAccountBanner from './disabled_account_banner';
import FollowRequestsColumnLink from './follow_requests_column_link';
import ListPanel from './list_panel';
import NotificationsCounterIcon from './notifications_counter_icon';
import SignInBanner from './sign_in_banner';
import ColumnLink from "./column_link";
import DisabledAccountBanner from "./disabled_account_banner";
import FollowRequestsColumnLink from "./follow_requests_column_link";
import ListPanel from "./list_panel";
import NotificationsCounterIcon from "./notifications_counter_icon";
import SignInBanner from "./sign_in_banner";
const messages = defineMessages({
home: { id: 'tabs_bar.home', defaultMessage: 'Home' },
notifications: { id: 'tabs_bar.notifications', defaultMessage: 'Notifications' },
explore: { id: 'explore.title', defaultMessage: 'Explore' },
firehose: { id: 'column.firehose', defaultMessage: 'Live feeds' },
direct: { id: 'navigation_bar.direct', defaultMessage: 'Private mentions' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favorites' },
bookmarks: { id: 'navigation_bar.bookmarks', defaultMessage: 'Bookmarks' },
lists: { id: 'navigation_bar.lists', defaultMessage: 'Lists' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
followsAndFollowers: { id: 'navigation_bar.follows_and_followers', defaultMessage: 'Follows and followers' },
about: { id: 'navigation_bar.about', defaultMessage: 'About' },
search: { id: 'navigation_bar.search', defaultMessage: 'Search' },
advancedInterface: { id: 'navigation_bar.advanced_interface', defaultMessage: 'Open in advanced web interface' },
openedInClassicInterface: { id: 'navigation_bar.opened_in_classic_interface', defaultMessage: 'Posts, accounts, and other specific pages are opened by default in the classic web interface.' },
home: { id: "tabs_bar.home", defaultMessage: "Home" },
notifications: { id: "tabs_bar.notifications", defaultMessage: "Notifications" },
explore: { id: "explore.title", defaultMessage: "Explore" },
firehose: { id: "column.firehose", defaultMessage: "Live feeds" },
direct: { id: "navigation_bar.direct", defaultMessage: "Private mentions" },
favourites: { id: "navigation_bar.favourites", defaultMessage: "Favorites" },
bookmarks: { id: "navigation_bar.bookmarks", defaultMessage: "Bookmarks" },
lists: { id: "navigation_bar.lists", defaultMessage: "Lists" },
preferences: { id: "navigation_bar.preferences", defaultMessage: "Preferences" },
followsAndFollowers: { id: "navigation_bar.follows_and_followers", defaultMessage: "Follows and followers" },
about: { id: "navigation_bar.about", defaultMessage: "About" },
search: { id: "navigation_bar.search", defaultMessage: "Search" },
advancedInterface: { id: "navigation_bar.advanced_interface", defaultMessage: "Open in advanced web interface" },
openedInClassicInterface: { id: "navigation_bar.opened_in_classic_interface", defaultMessage: "Posts, accounts, and other specific pages are opened by default in the classic web interface." },
});
class NavigationPanel extends Component {
@@ -46,7 +46,7 @@ class NavigationPanel extends Component {
};
isFirehoseActive = (match, location) => {
return match || location.pathname.startsWith('/public');
return match || location.pathname.startsWith("/public");
};
render () {
@@ -59,10 +59,10 @@ class NavigationPanel extends Component {
<Link to='/' className='column-link column-link--logo'><WordmarkLogo /></Link>
{transientSingleColumn ? (
<div class='switch-to-advanced'>
<div className='switch-to-advanced'>
{intl.formatMessage(messages.openedInClassicInterface)}
{" "}
<a href={`/deck${location.pathname}`} class='switch-to-advanced__toggle'>
<a href={`/deck${location.pathname}`} className='switch-to-advanced__toggle'>
{intl.formatMessage(messages.advancedInterface)}
</a>
</div>
@@ -1,10 +1,10 @@
import { connect } from 'react-redux';
import { connect } from "react-redux";
import { IconWithBadge } from 'mastodon/components/icon_with_badge';
import { IconWithBadge } from "mastodon/components/icon_with_badge";
const mapStateToProps = state => ({
count: state.getIn(['notifications', 'unread']),
id: 'bell',
count: state.getIn(["notifications", "unread"]),
id: "bell",
});
export default connect(mapStateToProps)(IconWithBadge);
@@ -1,25 +1,25 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import { defineMessages, FormattedMessage, injectIntl } from 'react-intl';
import { defineMessages, FormattedMessage, injectIntl } from "react-intl";
import { OrderedSet } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import { OrderedSet } from "immutable";
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import { submitReport } from 'mastodon/actions/reports';
import { fetchServer } from 'mastodon/actions/server';
import { expandAccountTimeline } from 'mastodon/actions/timelines';
import { IconButton } from 'mastodon/components/icon_button';
import Category from 'mastodon/features/report/category';
import Comment from 'mastodon/features/report/comment';
import Rules from 'mastodon/features/report/rules';
import Statuses from 'mastodon/features/report/statuses';
import Thanks from 'mastodon/features/report/thanks';
import { makeGetAccount } from 'mastodon/selectors';
import { submitReport } from "mastodon/actions/reports";
import { fetchServer } from "mastodon/actions/server";
import { expandAccountTimeline } from "mastodon/actions/timelines";
import { IconButton } from "mastodon/components/icon_button";
import Category from "mastodon/features/report/category";
import Comment from "mastodon/features/report/comment";
import Rules from "mastodon/features/report/rules";
import Statuses from "mastodon/features/report/statuses";
import Thanks from "mastodon/features/report/thanks";
import { makeGetAccount } from "mastodon/selectors";
const messages = defineMessages({
close: { id: 'lightbox.close', defaultMessage: 'Close' },
close: { id: "lightbox.close", defaultMessage: "Close" },
});
const makeMapStateToProps = () => {
@@ -43,10 +43,10 @@ class ReportModal extends ImmutablePureComponent {
};
state = {
step: 'category',
step: "category",
selectedStatusIds: OrderedSet(this.props.statusId ? [this.props.statusId] : []),
selectedDomains: OrderedSet(),
comment: '',
comment: "",
category: null,
selectedRuleIds: OrderedSet(),
isSubmitting: false,
@@ -71,7 +71,7 @@ class ReportModal extends ImmutablePureComponent {
};
handleSuccess = () => {
this.setState({ isSubmitting: false, isSubmitted: true, step: 'thanks' });
this.setState({ isSubmitting: false, isSubmitted: true, step: "thanks" });
};
handleFail = () => {
@@ -146,71 +146,71 @@ class ReportModal extends ImmutablePureComponent {
isSubmitted,
} = this.state;
const domain = account.get('acct').split('@')[1];
const domain = account.get("acct").split("@")[1];
const isRemote = !!domain;
let stepComponent;
switch(step) {
case 'category':
stepComponent = (
<Category
onNextStep={this.handleNextStep}
startedFrom={this.props.statusId ? 'status' : 'account'}
category={category}
onChangeCategory={this.handleChangeCategory}
/>
);
break;
case 'rules':
stepComponent = (
<Rules
onNextStep={this.handleNextStep}
selectedRuleIds={selectedRuleIds}
onToggle={this.handleRuleToggle}
/>
);
break;
case 'statuses':
stepComponent = (
<Statuses
onNextStep={this.handleNextStep}
accountId={accountId}
selectedStatusIds={selectedStatusIds}
onToggle={this.handleStatusToggle}
/>
);
break;
case 'comment':
stepComponent = (
<Comment
onSubmit={this.handleSubmit}
isSubmitting={isSubmitting}
isRemote={isRemote}
comment={comment}
domain={domain}
onChangeComment={this.handleChangeComment}
statusIds={selectedStatusIds}
selectedDomains={selectedDomains}
onToggleDomain={this.handleDomainToggle}
/>
);
break;
case 'thanks':
stepComponent = (
<Thanks
submitted={isSubmitted}
account={account}
onClose={onClose}
/>
);
case "category":
stepComponent = (
<Category
onNextStep={this.handleNextStep}
startedFrom={this.props.statusId ? "status" : "account"}
category={category}
onChangeCategory={this.handleChangeCategory}
/>
);
break;
case "rules":
stepComponent = (
<Rules
onNextStep={this.handleNextStep}
selectedRuleIds={selectedRuleIds}
onToggle={this.handleRuleToggle}
/>
);
break;
case "statuses":
stepComponent = (
<Statuses
onNextStep={this.handleNextStep}
accountId={accountId}
selectedStatusIds={selectedStatusIds}
onToggle={this.handleStatusToggle}
/>
);
break;
case "comment":
stepComponent = (
<Comment
onSubmit={this.handleSubmit}
isSubmitting={isSubmitting}
isRemote={isRemote}
comment={comment}
domain={domain}
onChangeComment={this.handleChangeComment}
statusIds={selectedStatusIds}
selectedDomains={selectedDomains}
onToggleDomain={this.handleDomainToggle}
/>
);
break;
case "thanks":
stepComponent = (
<Thanks
submitted={isSubmitted}
account={account}
onClose={onClose}
/>
);
}
return (
<div className='modal-root__modal report-dialog-modal'>
<div className='report-modal__target'>
<IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} />
<FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get('acct')}</strong> }} />
<FormattedMessage id='report.target' defaultMessage='Report {target}' values={{ target: <strong>{account.get("acct")}</strong> }} />
</div>
<div className='report-dialog-modal__container'>
@@ -1,23 +1,23 @@
import { useCallback } from 'react';
import { useCallback } from "react";
import { FormattedMessage } from 'react-intl';
import { FormattedMessage } from "react-intl";
import { openModal } from 'mastodon/actions/modal';
import { registrationsOpen, sso_redirect } from 'mastodon/initial_state';
import { useAppDispatch, useAppSelector } from 'mastodon/store';
import { openModal } from "mastodon/actions/modal";
import { registrationsOpen, sso_redirect } from "mastodon/initial_state";
import { useAppDispatch, useAppSelector } from "mastodon/store";
const SignInBanner = () => {
const dispatch = useAppDispatch();
const openClosedRegistrationsModal = useCallback(
() => dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' })),
() => dispatch(openModal({ modalType: "CLOSED_REGISTRATIONS" })),
[dispatch],
);
let signupButton;
const signupUrl = useAppSelector((state) => state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up');
const signupUrl = useAppSelector((state) => state.getIn(["server", "server", "registrations", "url"], null) || "/auth/sign_up");
if (sso_redirect) {
return (
@@ -1,11 +1,11 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { FormattedMessage } from 'react-intl';
import { FormattedMessage } from "react-intl";
import spring from 'react-motion/lib/spring';
import spring from "react-motion/lib/spring";
import Motion from '../util/optional_motion';
import Motion from "../util/optional_motion";
export default class UploadArea extends PureComponent {
@@ -18,21 +18,21 @@ export default class UploadArea extends PureComponent {
const keyCode = e.keyCode;
if (this.props.active) {
switch(keyCode) {
case 27:
e.preventDefault();
e.stopPropagation();
this.props.onClose();
break;
case 27:
e.preventDefault();
e.stopPropagation();
this.props.onClose();
break;
}
}
};
componentDidMount () {
window.addEventListener('keyup', this.handleKeyUp, false);
window.addEventListener("keyup", this.handleKeyUp, false);
}
componentWillUnmount () {
window.removeEventListener('keyup', this.handleKeyUp);
window.removeEventListener("keyup", this.handleKeyUp);
}
render () {
@@ -41,7 +41,7 @@ export default class UploadArea extends PureComponent {
return (
<Motion defaultStyle={{ backgroundOpacity: 0, backgroundScale: 0.95 }} style={{ backgroundOpacity: spring(active ? 1 : 0, { stiffness: 150, damping: 15 }), backgroundScale: spring(active ? 1 : 0.95, { stiffness: 200, damping: 3 }) }}>
{({ backgroundOpacity, backgroundScale }) => (
<div className='upload-area' style={{ visibility: active ? 'visible' : 'hidden', opacity: backgroundOpacity }}>
<div className='upload-area' style={{ visibility: active ? "visible" : "hidden", opacity: backgroundOpacity }}>
<div className='upload-area__drop'>
<div className='upload-area__background' style={{ transform: `scale(${backgroundScale})` }} />
<div className='upload-area__content'><FormattedMessage id='upload_area.title' defaultMessage='Drag & drop to upload' /></div>
@@ -1,15 +1,15 @@
import PropTypes from 'prop-types';
import PropTypes from "prop-types";
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import ImmutablePropTypes from "react-immutable-proptypes";
import ImmutablePureComponent from "react-immutable-pure-component";
import { connect } from "react-redux";
import { getAverageFromBlurhash } from 'mastodon/blurhash';
import Footer from 'mastodon/features/picture_in_picture/components/footer';
import Video from 'mastodon/features/video';
import { getAverageFromBlurhash } from "mastodon/blurhash";
import Footer from "mastodon/features/picture_in_picture/components/footer";
import Video from "mastodon/features/video";
const mapStateToProps = (state, { statusId }) => ({
status: state.getIn(['statuses', statusId]),
status: state.getIn(["statuses", statusId]),
});
class VideoModal extends ImmutablePureComponent {
@@ -30,7 +30,7 @@ class VideoModal extends ImmutablePureComponent {
componentDidMount () {
const { media, onChangeBackgroundColor } = this.props;
const backgroundColor = getAverageFromBlurhash(media.get('blurhash'));
const backgroundColor = getAverageFromBlurhash(media.get("blurhash"));
if (backgroundColor) {
onChangeBackgroundColor(backgroundColor);
@@ -40,18 +40,18 @@ class VideoModal extends ImmutablePureComponent {
render () {
const { media, status, onClose } = this.props;
const options = this.props.options || {};
const language = status.getIn(['translation', 'language']) || status.get('language');
const description = media.getIn(['translation', 'description']) || media.get('description');
const language = status.getIn(["translation", "language"]) || status.get("language");
const description = media.getIn(["translation", "description"]) || media.get("description");
return (
<div className='modal-root__modal video-modal'>
<div className='video-modal__container'>
<Video
preview={media.get('preview_url')}
frameRate={media.getIn(['meta', 'original', 'frame_rate'])}
aspectRatio={`${media.getIn(['meta', 'original', 'width'])} / ${media.getIn(['meta', 'original', 'height'])}`}
blurhash={media.get('blurhash')}
src={media.get('url')}
preview={media.get("preview_url")}
frameRate={media.getIn(["meta", "original", "frame_rate"])}
aspectRatio={`${media.getIn(["meta", "original", "width"])} / ${media.getIn(["meta", "original", "height"])}`}
blurhash={media.get("blurhash")}
src={media.get("url")}
currentTime={options.startTime}
autoPlay={options.autoPlay}
volume={options.defaultVolume}
@@ -64,7 +64,7 @@ class VideoModal extends ImmutablePureComponent {
</div>
<div className='media-modal__overlay'>
{status && <Footer statusId={status.get('id')} withOpenButton onClose={onClose} />}
{status && <Footer statusId={status.get("id")} withOpenButton onClose={onClose} />}
</div>
</div>
);
@@ -1,13 +1,13 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import PropTypes from "prop-types";
import { PureComponent } from "react";
import { defineMessages, injectIntl } from 'react-intl';
import { defineMessages, injectIntl } from "react-intl";
import { IconButton } from 'mastodon/components/icon_button';
import { IconButton } from "mastodon/components/icon_button";
const messages = defineMessages({
compress: { id: 'lightbox.compress', defaultMessage: 'Compress image view box' },
expand: { id: 'lightbox.expand', defaultMessage: 'Expand image view box' },
compress: { id: "lightbox.compress", defaultMessage: "Compress image view box" },
expand: { id: "lightbox.expand", defaultMessage: "Expand image view box" },
});
const MIN_SCALE = 1;
@@ -38,21 +38,21 @@ const normalizeWheel = event => {
pY = 0; // pixelX, pixelY
// Legacy
if ('detail' in event) {
if ("detail" in event) {
sY = event.detail;
}
if ('wheelDelta' in event) {
if ("wheelDelta" in event) {
sY = -event.wheelDelta / 120;
}
if ('wheelDeltaY' in event) {
if ("wheelDeltaY" in event) {
sY = -event.wheelDeltaY / 120;
}
if ('wheelDeltaX' in event) {
if ("wheelDeltaX" in event) {
sX = -event.wheelDeltaX / 120;
}
// side scrolling on FF with DOMMouseScroll
if ('axis' in event && event.axis === event.HORIZONTAL_AXIS) {
if ("axis" in event && event.axis === event.HORIZONTAL_AXIS) {
sX = sY;
sY = 0;
}
@@ -60,10 +60,10 @@ const normalizeWheel = event => {
pX = sX * PIXEL_STEP;
pY = sY * PIXEL_STEP;
if ('deltaY' in event) {
if ("deltaY" in event) {
pY = event.deltaY;
}
if ('deltaX' in event) {
if ("deltaX" in event) {
pX = event.deltaX;
}
@@ -107,8 +107,8 @@ class ZoomableImage extends PureComponent {
};
static defaultProps = {
alt: '',
lang: '',
alt: "",
lang: "",
width: null,
height: null,
};
@@ -129,7 +129,7 @@ class ZoomableImage extends PureComponent {
translateX: null,
translateY: null,
},
zoomState: 'expand', // 'expand' 'compress'
zoomState: "expand", // 'expand' 'compress'
navigationHidden: false,
dragPosition: { top: 0, left: 0, x: 0, y: 0 },
dragged: false,
@@ -145,27 +145,27 @@ class ZoomableImage extends PureComponent {
componentDidMount () {
let handler = this.handleTouchStart;
this.container.addEventListener('touchstart', handler);
this.removers.push(() => this.container.removeEventListener('touchstart', handler));
this.container.addEventListener("touchstart", handler);
this.removers.push(() => this.container.removeEventListener("touchstart", handler));
handler = this.handleTouchMove;
// on Chrome 56+, touch event listeners will default to passive
// https://www.chromestatus.com/features/5093566007214080
this.container.addEventListener('touchmove', handler, { passive: false });
this.removers.push(() => this.container.removeEventListener('touchend', handler));
this.container.addEventListener("touchmove", handler, { passive: false });
this.removers.push(() => this.container.removeEventListener("touchend", handler));
handler = this.mouseDownHandler;
this.container.addEventListener('mousedown', handler);
this.removers.push(() => this.container.removeEventListener('mousedown', handler));
this.container.addEventListener("mousedown", handler);
this.removers.push(() => this.container.removeEventListener("mousedown", handler));
handler = this.mouseWheelHandler;
this.container.addEventListener('wheel', handler);
this.removers.push(() => this.container.removeEventListener('wheel', handler));
this.container.addEventListener("wheel", handler);
this.removers.push(() => this.container.removeEventListener("wheel", handler));
// Old Chrome
this.container.addEventListener('mousewheel', handler);
this.removers.push(() => this.container.removeEventListener('mousewheel', handler));
this.container.addEventListener("mousewheel", handler);
this.removers.push(() => this.container.removeEventListener("mousewheel", handler));
// Old Firefox
this.container.addEventListener('DOMMouseScroll', handler);
this.removers.push(() => this.container.removeEventListener('DOMMouseScroll', handler));
this.container.addEventListener("DOMMouseScroll", handler);
this.removers.push(() => this.container.removeEventListener("DOMMouseScroll", handler));
this.initZoomMatrix();
}
@@ -175,10 +175,10 @@ class ZoomableImage extends PureComponent {
}
componentDidUpdate () {
this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? 'compress' : 'expand' });
this.setState({ zoomState: this.state.scale >= this.state.zoomMatrix.rate ? "compress" : "expand" });
if (this.state.scale === MIN_SCALE) {
this.container.style.removeProperty('cursor');
this.container.style.removeProperty("cursor");
}
}
@@ -205,7 +205,7 @@ class ZoomableImage extends PureComponent {
const event = normalizeWheel(e);
if (this.state.zoomMatrix.type === 'width') {
if (this.state.zoomMatrix.type === "width") {
// full width, scroll vertical
this.container.scrollTop = Math.max(this.container.scrollTop + event.pixelY, this.state.lockScroll.y);
} else {
@@ -218,8 +218,8 @@ class ZoomableImage extends PureComponent {
};
mouseDownHandler = e => {
this.container.style.cursor = 'grabbing';
this.container.style.userSelect = 'none';
this.container.style.cursor = "grabbing";
this.container.style.userSelect = "none";
this.setState({ dragPosition: {
left: this.container.scrollLeft,
@@ -229,8 +229,8 @@ class ZoomableImage extends PureComponent {
y: e.clientY,
} });
this.image.addEventListener('mousemove', this.mouseMoveHandler);
this.image.addEventListener('mouseup', this.mouseUpHandler);
this.image.addEventListener("mousemove", this.mouseMoveHandler);
this.image.addEventListener("mouseup", this.mouseUpHandler);
};
mouseMoveHandler = e => {
@@ -244,15 +244,17 @@ class ZoomableImage extends PureComponent {
};
mouseUpHandler = () => {
this.container.style.cursor = 'grab';
this.container.style.removeProperty('user-select');
this.container.style.cursor = "grab";
this.container.style.removeProperty("user-select");
this.image.removeEventListener('mousemove', this.mouseMoveHandler);
this.image.removeEventListener('mouseup', this.mouseUpHandler);
this.image.removeEventListener("mousemove", this.mouseMoveHandler);
this.image.removeEventListener("mouseup", this.mouseUpHandler);
};
handleTouchStart = e => {
if (e.touches.length !== 2) return;
if (e.touches.length !== 2) {
return;
}
this.lastDistance = getDistance(...e.touches);
};
@@ -264,7 +266,9 @@ class ZoomableImage extends PureComponent {
e.stopPropagation();
return;
}
if (e.touches.length !== 2) return;
if (e.touches.length !== 2) {
return;
}
e.preventDefault();
e.stopPropagation();
@@ -313,9 +317,13 @@ class ZoomableImage extends PureComponent {
e.stopPropagation();
const dragged = this.state.dragged;
this.setState({ dragged: false });
if (dragged) return;
if (dragged) {
return;
}
const handler = this.props.onClick;
if (handler) handler();
if (handler) {
handler();
}
this.setState({ navigationHidden: !this.state.navigationHidden });
};
@@ -329,13 +337,13 @@ class ZoomableImage extends PureComponent {
const { offsetWidth, offsetHeight } = this.image;
const clientHeightFixed = clientHeight - NAV_BAR_HEIGHT;
const type = width / height < clientWidth / clientHeightFixed ? 'width' : 'height';
const fullScreen = type === 'width' ? width > clientWidth : height > clientHeightFixed;
const rate = type === 'width' ? Math.min(clientWidth, width) / offsetWidth : Math.min(clientHeightFixed, height) / offsetHeight;
const scrollTop = type === 'width' ? (clientHeight - offsetHeight) / 2 - NAV_BAR_HEIGHT : (clientHeightFixed - offsetHeight) / 2;
const type = width / height < clientWidth / clientHeightFixed ? "width" : "height";
const fullScreen = type === "width" ? width > clientWidth : height > clientHeightFixed;
const rate = type === "width" ? Math.min(clientWidth, width) / offsetWidth : Math.min(clientHeightFixed, height) / offsetHeight;
const scrollTop = type === "width" ? (clientHeight - offsetHeight) / 2 - NAV_BAR_HEIGHT : (clientHeightFixed - offsetHeight) / 2;
const scrollLeft = (clientWidth - offsetWidth) / 2;
const translateX = type === 'width' ? (width - offsetWidth) / (2 * rate) : 0;
const translateY = type === 'height' ? (height - offsetHeight) / (2 * rate) : 0;
const translateX = type === "width" ? (width - offsetWidth) / (2 * rate) : 0;
const translateY = type === "height" ? (height - offsetHeight) / (2 * rate) : 0;
this.setState({
zoomMatrix: {
@@ -393,8 +401,8 @@ class ZoomableImage extends PureComponent {
});
}
this.container.style.cursor = 'grab';
this.container.style.removeProperty('user-select');
this.container.style.cursor = "grab";
this.container.style.removeProperty("user-select");
};
setContainerRef = c => {
@@ -408,9 +416,9 @@ class ZoomableImage extends PureComponent {
render () {
const { alt, lang, src, width, height, intl } = this.props;
const { scale, lockTranslate } = this.state;
const overflow = scale === MIN_SCALE ? 'hidden' : 'scroll';
const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? 'media-modal__zoom-button--hidden' : '';
const zoomButtonTitle = this.state.zoomState === 'compress' ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand);
const overflow = scale === MIN_SCALE ? "hidden" : "scroll";
const zoomButtonShouldHide = this.state.navigationHidden || this.props.zoomButtonHidden || this.state.zoomMatrix.rate <= MIN_SCALE ? "media-modal__zoom-button--hidden" : "";
const zoomButtonTitle = this.state.zoomState === "compress" ? intl.formatMessage(messages.compress) : intl.formatMessage(messages.expand);
return (
<>
@@ -421,7 +429,7 @@ class ZoomableImage extends PureComponent {
onClick={this.handleZoomClick}
size={40}
style={{
fontSize: '30px', /* Fontawesome's fa-compress fa-expand is larger than fa-close */
fontSize: "30px", /* Fontawesome's fa-compress fa-expand is larger than fa-close */
}}
/>
<div
@@ -440,7 +448,7 @@ class ZoomableImage extends PureComponent {
height={height}
style={{
transform: `scale(${scale}) translate(-${lockTranslate.x}px, -${lockTranslate.y}px)`,
transformOrigin: '0 0',
transformOrigin: "0 0",
}}
draggable={false}
onClick={this.handleClick}