[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:
@@ -1,30 +1,30 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from "react-intl";
|
||||
|
||||
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 { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
import { me } from "flavours/glitch/initial_state";
|
||||
|
||||
import { Avatar } from './avatar';
|
||||
import { DisplayName } from './display_name';
|
||||
import { IconButton } from './icon_button';
|
||||
import Permalink from './permalink';
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
import { Avatar } from "./avatar";
|
||||
import { DisplayName } from "./display_name";
|
||||
import { IconButton } from "./icon_button";
|
||||
import Permalink from "./permalink";
|
||||
import { RelativeTimestamp } from "./relative_timestamp";
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
follow: { id: 'account.follow', defaultMessage: 'Follow' },
|
||||
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
|
||||
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' },
|
||||
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
|
||||
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
|
||||
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
|
||||
unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
|
||||
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
|
||||
follow: { id: "account.follow", defaultMessage: "Follow" },
|
||||
unfollow: { id: "account.unfollow", defaultMessage: "Unfollow" },
|
||||
requested: { id: "account.requested", defaultMessage: "Awaiting approval. Click to cancel follow request" },
|
||||
unblock: { id: "account.unblock", defaultMessage: "Unblock @{name}" },
|
||||
unmute: { id: "account.unmute", defaultMessage: "Unmute @{name}" },
|
||||
mute_notifications: { id: "account.mute_notifications", defaultMessage: "Mute notifications from @{name}" },
|
||||
unmute_notifications: { id: "account.unmute_notifications", defaultMessage: "Unmute notifications from @{name}" },
|
||||
mute: { id: "account.mute", defaultMessage: "Mute @{name}" },
|
||||
block: { id: "account.block", defaultMessage: "Block @{name}" },
|
||||
});
|
||||
|
||||
class Account extends ImmutablePureComponent {
|
||||
@@ -102,8 +102,8 @@ class Account extends ImmutablePureComponent {
|
||||
if (hidden) {
|
||||
return (
|
||||
<>
|
||||
{account.get('display_name')}
|
||||
{account.get('username')}
|
||||
{account.get("display_name")}
|
||||
{account.get("username")}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -114,48 +114,48 @@ class Account extends ImmutablePureComponent {
|
||||
if (actionIcon) {
|
||||
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />;
|
||||
}
|
||||
} else if (account.get('id') !== me && !small && account.get('relationship', null) !== null) {
|
||||
const following = account.getIn(['relationship', 'following']);
|
||||
const requested = account.getIn(['relationship', 'requested']);
|
||||
const blocking = account.getIn(['relationship', 'blocking']);
|
||||
const muting = account.getIn(['relationship', 'muting']);
|
||||
} else if (account.get("id") !== me && !small && account.get("relationship", null) !== null) {
|
||||
const following = account.getIn(["relationship", "following"]);
|
||||
const requested = account.getIn(["relationship", "requested"]);
|
||||
const blocking = account.getIn(["relationship", "blocking"]);
|
||||
const muting = account.getIn(["relationship", "muting"]);
|
||||
|
||||
if (requested) {
|
||||
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />;
|
||||
} else if (blocking) {
|
||||
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />;
|
||||
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get("username") })} onClick={this.handleBlock} />;
|
||||
} else if (muting) {
|
||||
let hidingNotificationsButton;
|
||||
if (account.getIn(['relationship', 'muting_notifications'])) {
|
||||
hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get('username') })} onClick={this.handleUnmuteNotifications} />;
|
||||
if (account.getIn(["relationship", "muting_notifications"])) {
|
||||
hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get("username") })} onClick={this.handleUnmuteNotifications} />;
|
||||
} else {
|
||||
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />;
|
||||
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get("username") })} onClick={this.handleMuteNotifications} />;
|
||||
}
|
||||
buttons = (
|
||||
<>
|
||||
<IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
|
||||
<IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get("username") })} onClick={this.handleMute} />
|
||||
{hidingNotificationsButton}
|
||||
</>
|
||||
);
|
||||
} else if (defaultAction === 'mute') {
|
||||
buttons = <IconButton icon='volume-off' title={intl.formatMessage(messages.mute, { name: account.get('username') })} onClick={this.handleMute} />;
|
||||
} else if (defaultAction === 'block') {
|
||||
buttons = <IconButton icon='lock' title={intl.formatMessage(messages.block, { name: account.get('username') })} onClick={this.handleBlock} />;
|
||||
} else if (!account.get('moved') || following) {
|
||||
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
|
||||
} else if (defaultAction === "mute") {
|
||||
buttons = <IconButton icon='volume-off' title={intl.formatMessage(messages.mute, { name: account.get("username") })} onClick={this.handleMute} />;
|
||||
} else if (defaultAction === "block") {
|
||||
buttons = <IconButton icon='lock' title={intl.formatMessage(messages.block, { name: account.get("username") })} onClick={this.handleBlock} />;
|
||||
} else if (!account.get("moved") || following) {
|
||||
buttons = <IconButton icon={following ? "user-times" : "user-plus"} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />;
|
||||
}
|
||||
}
|
||||
|
||||
let mute_expires_at;
|
||||
if (account.get('mute_expires_at')) {
|
||||
mute_expires_at = <div><RelativeTimestamp timestamp={account.get('mute_expires_at')} futureDate /></div>;
|
||||
if (account.get("mute_expires_at")) {
|
||||
mute_expires_at = <div><RelativeTimestamp timestamp={account.get("mute_expires_at")} futureDate /></div>;
|
||||
}
|
||||
|
||||
return small ? (
|
||||
<Permalink
|
||||
className='account small'
|
||||
href={account.get('url')}
|
||||
to={`/@${account.get('acct')}`}
|
||||
href={account.get("url")}
|
||||
to={`/@${account.get("acct")}`}
|
||||
>
|
||||
<div className='account__avatar-wrapper'>
|
||||
<Avatar
|
||||
@@ -171,7 +171,7 @@ class Account extends ImmutablePureComponent {
|
||||
) : (
|
||||
<div className='account'>
|
||||
<div className='account__wrapper'>
|
||||
<Permalink key={account.get('id')} className='account__display-name' title={account.get('acct')} href={account.get('url')} to={`/@${account.get('acct')}`}>
|
||||
<Permalink key={account.get("id")} className='account__display-name' title={account.get("acct")} href={account.get("url")} to={`/@${account.get("acct")}`}>
|
||||
<div className='account__avatar-wrapper'><Avatar account={account} size={size} /></div>
|
||||
{mute_expires_at}
|
||||
<DisplayName account={account} />
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedNumber } from 'react-intl';
|
||||
import { FormattedNumber } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||
import { Sparklines, SparklinesCurve } from "react-sparklines";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import api from "flavours/glitch/api";
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
|
||||
const percIncrease = (a, b) => {
|
||||
let percent;
|
||||
@@ -48,7 +48,7 @@ export default class Counter extends PureComponent {
|
||||
componentDidMount () {
|
||||
const { measure, start_at, end_at, params } = this.props;
|
||||
|
||||
api().post('/api/v1/admin/measures', { keys: [measure], start_at, end_at, [measure]: params }).then(res => {
|
||||
api().post("/api/v1/admin/measures", { keys: [measure], start_at, end_at, [measure]: params }).then(res => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data,
|
||||
@@ -78,7 +78,7 @@ export default class Counter extends PureComponent {
|
||||
content = (
|
||||
<>
|
||||
<span className='sparkline__value__total'>{measure.human_value || <FormattedNumber value={measure.total} />}</span>
|
||||
{measure.previous_total && (<span className={classNames('sparkline__value__change', { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && '+'}<FormattedNumber value={percentChange} style='percent' /></span>)}
|
||||
{measure.previous_total && (<span className={classNames("sparkline__value__change", { positive: percentChange > 0, negative: percentChange < 0 })}>{percentChange > 0 && "+"}<FormattedNumber value={percentChange} style='percent' /></span>)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedNumber } from 'react-intl';
|
||||
import { FormattedNumber } from "react-intl";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import { roundTo10 } from 'flavours/glitch/utils/numbers';
|
||||
import api from "flavours/glitch/api";
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
import { roundTo10 } from "flavours/glitch/utils/numbers";
|
||||
|
||||
export default class Dimension extends PureComponent {
|
||||
|
||||
@@ -26,7 +26,7 @@ export default class Dimension extends PureComponent {
|
||||
componentDidMount () {
|
||||
const { start_at, end_at, dimension, limit, params } = this.props;
|
||||
|
||||
api().post('/api/v1/admin/dimensions', { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => {
|
||||
api().post("/api/v1/admin/dimensions", { keys: [dimension], start_at, end_at, limit, [dimension]: params }).then(res => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data,
|
||||
@@ -74,7 +74,7 @@ export default class Dimension extends PureComponent {
|
||||
</td>
|
||||
|
||||
<td className='dimension__item__value'>
|
||||
{typeof item.human_value !== 'undefined' ? item.human_value : <FormattedNumber value={item.value} />}
|
||||
{typeof item.human_value !== "undefined" ? item.human_value : <FormattedNumber value={item.value} />}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedNumber, FormattedMessage } from 'react-intl';
|
||||
import { FormattedNumber, FormattedMessage } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import api from "flavours/glitch/api";
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
|
||||
export default class ImpactReport extends PureComponent {
|
||||
|
||||
@@ -27,8 +27,8 @@ export default class ImpactReport extends PureComponent {
|
||||
include_subdomains: true,
|
||||
};
|
||||
|
||||
api().post('/api/v1/admin/measures', {
|
||||
keys: ['instance_accounts', 'instance_follows', 'instance_followers'],
|
||||
api().post("/api/v1/admin/measures", {
|
||||
keys: ["instance_accounts", "instance_follows", "instance_followers"],
|
||||
start_at: null,
|
||||
end_at: null,
|
||||
instance_accounts: params,
|
||||
@@ -63,7 +63,7 @@ export default class ImpactReport extends PureComponent {
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className={classNames('dimension__item', { negative: !loading && data[1].total > 0 })}>
|
||||
<tr className={classNames("dimension__item", { negative: !loading && data[1].total > 0 })}>
|
||||
<td className='dimension__item__key'>
|
||||
<FormattedMessage id='admin.impact_report.instance_follows' defaultMessage='Followers their users would lose' />
|
||||
</td>
|
||||
@@ -73,7 +73,7 @@ export default class ImpactReport extends PureComponent {
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr className={classNames('dimension__item', { negative: !loading && data[2].total > 0 })}>
|
||||
<tr className={classNames("dimension__item", { negative: !loading && data[2].total > 0 })}>
|
||||
<td className='dimension__item__key'>
|
||||
<FormattedMessage id='admin.impact_report.instance_followers' defaultMessage='Followers our users would lose' />
|
||||
</td>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import { injectIntl, defineMessages } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import api from "flavours/glitch/api";
|
||||
|
||||
const messages = defineMessages({
|
||||
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
|
||||
other: { id: 'report.categories.other', defaultMessage: 'Other' },
|
||||
spam: { id: 'report.categories.spam', defaultMessage: 'Spam' },
|
||||
violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
|
||||
legal: { id: "report.categories.legal", defaultMessage: "Legal" },
|
||||
other: { id: "report.categories.other", defaultMessage: "Other" },
|
||||
spam: { id: "report.categories.spam", defaultMessage: "Spam" },
|
||||
violation: { id: "report.categories.violation", defaultMessage: "Content violates one or more server rules" },
|
||||
});
|
||||
|
||||
class Category extends PureComponent {
|
||||
@@ -37,11 +37,11 @@ class Category extends PureComponent {
|
||||
const { id, text, disabled, selected, children } = this.props;
|
||||
|
||||
return (
|
||||
<div tabIndex={0} role='button' className={classNames('report-reason-selector__category', { selected, disabled })} onClick={this.handleClick}>
|
||||
<div tabIndex={0} role='button' className={classNames("report-reason-selector__category", { selected, disabled })} onClick={this.handleClick}>
|
||||
{selected && <input type='hidden' name='report[category]' value={id} />}
|
||||
|
||||
<div className='report-reason-selector__category__label'>
|
||||
<span className={classNames('poll__input', { active: selected, disabled })} />
|
||||
<span className={classNames("poll__input", { active: selected, disabled })} />
|
||||
{text}
|
||||
</div>
|
||||
|
||||
@@ -78,8 +78,8 @@ class Rule extends PureComponent {
|
||||
const { id, text, disabled, selected } = this.props;
|
||||
|
||||
return (
|
||||
<div tabIndex={0} role='button' className={classNames('report-reason-selector__rule', { selected, disabled })} onClick={this.handleClick}>
|
||||
<span className={classNames('poll__input', { checkbox: true, active: selected, disabled })} />
|
||||
<div tabIndex={0} role='button' className={classNames("report-reason-selector__rule", { selected, disabled })} onClick={this.handleClick}>
|
||||
<span className={classNames("poll__input", { checkbox: true, active: selected, disabled })} />
|
||||
{selected && <input type='hidden' name='report[rule_ids][]' value={id} />}
|
||||
{text}
|
||||
</div>
|
||||
@@ -105,7 +105,7 @@ class ReportReasonSelector extends PureComponent {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
api().get('/api/v1/instance').then(res => {
|
||||
api().get("/api/v1/instance").then(res => {
|
||||
this.setState({
|
||||
rules: res.data.rules,
|
||||
});
|
||||
@@ -150,10 +150,10 @@ class ReportReasonSelector extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className='report-reason-selector'>
|
||||
<Category id='other' text={intl.formatMessage(messages.other)} selected={category === 'other'} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='legal' text={intl.formatMessage(messages.legal)} selected={category === 'legal'} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='spam' text={intl.formatMessage(messages.spam)} selected={category === 'spam'} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='violation' text={intl.formatMessage(messages.violation)} selected={category === 'violation'} onSelect={this.handleSelect} disabled={disabled}>
|
||||
<Category id='other' text={intl.formatMessage(messages.other)} selected={category === "other"} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='legal' text={intl.formatMessage(messages.legal)} selected={category === "legal"} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='spam' text={intl.formatMessage(messages.spam)} selected={category === "spam"} onSelect={this.handleSelect} disabled={disabled} />
|
||||
<Category id='violation' text={intl.formatMessage(messages.violation)} selected={category === "violation"} onSelect={this.handleSelect} disabled={disabled}>
|
||||
{rules.map(rule => <Rule key={rule.id} id={rule.id} text={rule.text} selected={rule_ids.includes(rule.id)} onToggle={this.handleToggle} disabled={disabled} />)}
|
||||
</Category>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedMessage, FormattedNumber, FormattedDate } from 'react-intl';
|
||||
import { FormattedMessage, FormattedNumber, FormattedDate } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import { roundTo10 } from 'flavours/glitch/utils/numbers';
|
||||
import api from "flavours/glitch/api";
|
||||
import { roundTo10 } from "flavours/glitch/utils/numbers";
|
||||
|
||||
const dateForCohort = cohort => {
|
||||
const timeZone = 'UTC';
|
||||
const timeZone = "UTC";
|
||||
switch(cohort.frequency) {
|
||||
case 'day':
|
||||
return <FormattedDate value={cohort.period} month='long' day='2-digit' timeZone={timeZone} />;
|
||||
default:
|
||||
return <FormattedDate value={cohort.period} month='long' year='numeric' timeZone={timeZone} />;
|
||||
case "day":
|
||||
return <FormattedDate value={cohort.period} month='long' day='2-digit' timeZone={timeZone} />;
|
||||
default:
|
||||
return <FormattedDate value={cohort.period} month='long' year='numeric' timeZone={timeZone} />;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class Retention extends PureComponent {
|
||||
componentDidMount () {
|
||||
const { start_at, end_at, frequency } = this.props;
|
||||
|
||||
api().post('/api/v1/admin/retention', { start_at, end_at, frequency }).then(res => {
|
||||
api().post("/api/v1/admin/retention", { start_at, end_at, frequency }).then(res => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data,
|
||||
@@ -96,7 +96,7 @@ export default class Retention extends PureComponent {
|
||||
|
||||
return (
|
||||
<td key={retention.date}>
|
||||
<div className={classNames('retention__table__box', 'retention__table__average', `retention__table__box--${roundTo10(average * 100)}`)}>
|
||||
<div className={classNames("retention__table__box", "retention__table__average", `retention__table__box--${roundTo10(average * 100)}`)}>
|
||||
<FormattedNumber value={average} style='percent' />
|
||||
</div>
|
||||
</td>
|
||||
@@ -122,7 +122,7 @@ export default class Retention extends PureComponent {
|
||||
|
||||
{cohort.data.slice(1).map(retention => (
|
||||
<td key={retention.date}>
|
||||
<div className={classNames('retention__table__box', `retention__table__box--${roundTo10(retention.rate * 100)}`)}>
|
||||
<div className={classNames("retention__table__box", `retention__table__box--${roundTo10(retention.rate * 100)}`)}>
|
||||
<FormattedNumber value={retention.rate} style='percent' />
|
||||
</div>
|
||||
</td>
|
||||
@@ -134,13 +134,13 @@ export default class Retention extends PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
let title = null;
|
||||
let title;
|
||||
switch(frequency) {
|
||||
case 'day':
|
||||
title = <FormattedMessage id='admin.dashboard.daily_retention' defaultMessage='User retention rate by day after sign-up' />;
|
||||
break;
|
||||
default:
|
||||
title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />;
|
||||
case "day":
|
||||
title = <FormattedMessage id='admin.dashboard.daily_retention' defaultMessage='User retention rate by day after sign-up' />;
|
||||
break;
|
||||
default:
|
||||
title = <FormattedMessage id='admin.dashboard.monthly_retention' defaultMessage='User retention rate by month after sign-up' />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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 classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import api from 'flavours/glitch/api';
|
||||
import Hashtag from 'flavours/glitch/components/hashtag';
|
||||
import api from "flavours/glitch/api";
|
||||
import Hashtag from "flavours/glitch/components/hashtag";
|
||||
|
||||
export default class Trends extends PureComponent {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class Trends extends PureComponent {
|
||||
componentDidMount () {
|
||||
const { limit } = this.props;
|
||||
|
||||
api().get('/api/v1/admin/trends/tags', { params: { limit } }).then(res => {
|
||||
api().get("/api/v1/admin/trends/tags", { params: { limit } }).then(res => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
data: res.data,
|
||||
@@ -57,7 +57,7 @@ export default class Trends extends PureComponent {
|
||||
people={hashtag.history[0].accounts * 1 + hashtag.history[1].accounts * 1}
|
||||
uses={hashtag.history[0].uses * 1 + hashtag.history[1].uses * 1}
|
||||
history={hashtag.history.reverse().map(day => day.uses)}
|
||||
className={classNames(hashtag.requires_review && 'trends__item--requires-review', !hashtag.trendable && !hashtag.requires_review && 'trends__item--disabled')}
|
||||
className={classNames(hashtag.requires_review && "trends__item--requires-review", !hashtag.trendable && !hashtag.requires_review && "trends__item--disabled")}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useState } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import { TransitionMotion, spring } from 'react-motion';
|
||||
import { TransitionMotion, spring } from "react-motion";
|
||||
|
||||
import { reduceMotion } from '../initial_state';
|
||||
import { reduceMotion } from "../initial_state";
|
||||
|
||||
import { ShortNumber } from './short_number';
|
||||
import { ShortNumber } from "./short_number";
|
||||
|
||||
const obfuscatedCount = (count: number) => {
|
||||
if (count < 0) {
|
||||
@@ -13,13 +13,13 @@ const obfuscatedCount = (count: number) => {
|
||||
} else if (count <= 1) {
|
||||
return count;
|
||||
} else {
|
||||
return '1+';
|
||||
return "1+";
|
||||
}
|
||||
};
|
||||
|
||||
interface Props {
|
||||
value: number;
|
||||
obfuscate?: boolean;
|
||||
value: number,
|
||||
obfuscate?: boolean,
|
||||
}
|
||||
export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
|
||||
const [previousValue, setPreviousValue] = useState(value);
|
||||
@@ -64,7 +64,7 @@ export const AnimatedNumber: React.FC<Props> = ({ value, obfuscate }) => {
|
||||
<span
|
||||
key={key}
|
||||
style={{
|
||||
position: direction * style.y > 0 ? 'absolute' : 'static',
|
||||
position: direction * style.y > 0 ? "absolute" : "static",
|
||||
transform: `translateY(${style.y * 100}%)`,
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { 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 ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
|
||||
const filename = url => url.split("/").pop().split("#")[0].split("?")[0];
|
||||
|
||||
export default class AttachmentList extends ImmutablePureComponent {
|
||||
|
||||
@@ -22,7 +22,7 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||
const { media, compact } = this.props;
|
||||
|
||||
return (
|
||||
<div className={classNames('attachment-list', { compact })}>
|
||||
<div className={classNames("attachment-list", { compact })}>
|
||||
{!compact && (
|
||||
<div className='attachment-list__icon'>
|
||||
<Icon id='link' />
|
||||
@@ -31,13 +31,13 @@ export default class AttachmentList extends ImmutablePureComponent {
|
||||
|
||||
<ul className='attachment-list__list'>
|
||||
{media.map(attachment => {
|
||||
const displayUrl = attachment.get('remote_url') || attachment.get('url');
|
||||
const displayUrl = attachment.get("remote_url") || attachment.get("url");
|
||||
|
||||
return (
|
||||
<li key={attachment.get('id')}>
|
||||
<li key={attachment.get("id")}>
|
||||
<a href={displayUrl} target='_blank' rel='noopener noreferrer'>
|
||||
{compact && <Icon id='link' />}
|
||||
{compact && ' ' }
|
||||
{compact && " " }
|
||||
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import unicodeMapping from 'flavours/glitch/features/emoji/emoji_unicode_mapping_light';
|
||||
import { assetHost } from 'flavours/glitch/utils/config';
|
||||
import unicodeMapping from "flavours/glitch/features/emoji/emoji_unicode_mapping_light";
|
||||
import { assetHost } from "flavours/glitch/utils/config";
|
||||
|
||||
export default class AutosuggestEmoji extends PureComponent {
|
||||
|
||||
@@ -17,7 +17,7 @@ export default class AutosuggestEmoji extends PureComponent {
|
||||
if (emoji.custom) {
|
||||
url = emoji.imageUrl;
|
||||
} else {
|
||||
const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, '')];
|
||||
const mapping = unicodeMapping[emoji.native] || unicodeMapping[emoji.native.replace(/\uFE0F$/, "")];
|
||||
|
||||
if (!mapping) {
|
||||
return null;
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||
import { ShortNumber } from "flavours/glitch/components/short_number";
|
||||
|
||||
interface Props {
|
||||
tag: {
|
||||
name: string;
|
||||
url?: string;
|
||||
name: string,
|
||||
url?: string,
|
||||
history?: {
|
||||
uses: number;
|
||||
accounts: string;
|
||||
day: string;
|
||||
}[];
|
||||
following?: boolean;
|
||||
type: 'hashtag';
|
||||
};
|
||||
uses: number,
|
||||
accounts: string,
|
||||
day: string,
|
||||
}[],
|
||||
following?: boolean,
|
||||
type: "hashtag",
|
||||
},
|
||||
}
|
||||
|
||||
export const AutosuggestHashtag: React.FC<Props> = ({ tag }) => {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
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 AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestAccountContainer from "flavours/glitch/features/compose/containers/autosuggest_account_container";
|
||||
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
import { AutosuggestHashtag } from './autosuggest_hashtag';
|
||||
import AutosuggestEmoji from "./autosuggest_emoji";
|
||||
import { AutosuggestHashtag } from "./autosuggest_hashtag";
|
||||
|
||||
const textAtCursorMatchesToken = (str, caretPosition, searchTokens) => {
|
||||
let word;
|
||||
@@ -59,7 +59,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
|
||||
static defaultProps = {
|
||||
autoFocus: true,
|
||||
searchTokens: ['@', ':', '#'],
|
||||
searchTokens: ["@", ":", "#"],
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -100,39 +100,39 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
if (suggestions.size === 0 || suggestionsHidden) {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
this.setState({ suggestionsHidden: true });
|
||||
}
|
||||
case "Escape":
|
||||
if (suggestions.size === 0 || suggestionsHidden) {
|
||||
document.querySelector(".ui").parentElement.focus();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
this.setState({ suggestionsHidden: true });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
|
||||
}
|
||||
break;
|
||||
case "ArrowDown":
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
|
||||
}
|
||||
break;
|
||||
case "ArrowUp":
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Enter':
|
||||
case 'Tab':
|
||||
break;
|
||||
case "Enter":
|
||||
case "Tab":
|
||||
// Select suggestion
|
||||
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.defaultPrevented || !this.props.onKeyDown) {
|
||||
@@ -151,7 +151,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute("data-index"));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.input.focus();
|
||||
@@ -171,19 +171,19 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
const { selectedSuggestion } = this.state;
|
||||
let inner, key;
|
||||
|
||||
if (suggestion.type === 'emoji') {
|
||||
if (suggestion.type === "emoji") {
|
||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||
key = suggestion.id;
|
||||
} else if (suggestion.type ==='hashtag') {
|
||||
} else if (suggestion.type ==="hashtag") {
|
||||
inner = <AutosuggestHashtag tag={suggestion} />;
|
||||
key = suggestion.name;
|
||||
} else if (suggestion.type === 'account') {
|
||||
} else if (suggestion.type === "account") {
|
||||
inner = <AutosuggestAccountContainer id={suggestion.id} />;
|
||||
key = suggestion.id;
|
||||
}
|
||||
|
||||
return (
|
||||
<div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
<div role='button' tabIndex={0} key={key} data-index={i} className={classNames("autosuggest-textarea__suggestions__item", { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
{inner}
|
||||
</div>
|
||||
);
|
||||
@@ -196,7 +196,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
return (
|
||||
<div className='autosuggest-input'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
<span style={{ display: "none" }}>{placeholder}</span>
|
||||
|
||||
<input
|
||||
type='text'
|
||||
@@ -220,7 +220,7 @@ export default class AutosuggestInput extends ImmutablePureComponent {
|
||||
/>
|
||||
</label>
|
||||
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? "" : "autosuggest-textarea__suggestions--visible"}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
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 Textarea from 'react-textarea-autosize';
|
||||
import Textarea from "react-textarea-autosize";
|
||||
|
||||
import AutosuggestAccountContainer from 'flavours/glitch/features/compose/containers/autosuggest_account_container';
|
||||
import AutosuggestAccountContainer from "flavours/glitch/features/compose/containers/autosuggest_account_container";
|
||||
|
||||
import AutosuggestEmoji from './autosuggest_emoji';
|
||||
import { AutosuggestHashtag } from './autosuggest_hashtag';
|
||||
import AutosuggestEmoji from "./autosuggest_emoji";
|
||||
import { AutosuggestHashtag } from "./autosuggest_hashtag";
|
||||
|
||||
const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||
let word;
|
||||
@@ -24,7 +24,7 @@ const textAtCursorMatchesToken = (str, caretPosition) => {
|
||||
word = str.slice(left, right + caretPosition);
|
||||
}
|
||||
|
||||
if (!word || word.trim().length < 3 || ['@', ':', '#'].indexOf(word[0]) === -1) {
|
||||
if (!word || word.trim().length < 3 || ["@", ":", "#"].indexOf(word[0]) === -1) {
|
||||
return [null, null];
|
||||
}
|
||||
|
||||
@@ -97,39 +97,39 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
switch(e.key) {
|
||||
case 'Escape':
|
||||
if (suggestions.size === 0 || suggestionsHidden) {
|
||||
document.querySelector('.ui').parentElement.focus();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
this.setState({ suggestionsHidden: true });
|
||||
}
|
||||
case "Escape":
|
||||
if (suggestions.size === 0 || suggestionsHidden) {
|
||||
document.querySelector(".ui").parentElement.focus();
|
||||
} else {
|
||||
e.preventDefault();
|
||||
this.setState({ suggestionsHidden: true });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowDown':
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
|
||||
}
|
||||
break;
|
||||
case "ArrowDown":
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
|
||||
}
|
||||
break;
|
||||
case "ArrowUp":
|
||||
if (suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) });
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Enter':
|
||||
case 'Tab':
|
||||
break;
|
||||
case "Enter":
|
||||
case "Tab":
|
||||
// Select suggestion
|
||||
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion));
|
||||
}
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
if (e.defaultPrevented || !this.props.onKeyDown) {
|
||||
@@ -151,7 +151,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
onSuggestionClick = (e) => {
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index'));
|
||||
const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute("data-index"));
|
||||
e.preventDefault();
|
||||
this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion);
|
||||
this.textarea.focus();
|
||||
@@ -178,19 +178,19 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
const { selectedSuggestion } = this.state;
|
||||
let inner, key;
|
||||
|
||||
if (suggestion.type === 'emoji') {
|
||||
if (suggestion.type === "emoji") {
|
||||
inner = <AutosuggestEmoji emoji={suggestion} />;
|
||||
key = suggestion.id;
|
||||
} else if (suggestion.type === 'hashtag') {
|
||||
} else if (suggestion.type === "hashtag") {
|
||||
inner = <AutosuggestHashtag tag={suggestion} />;
|
||||
key = suggestion.name;
|
||||
} else if (suggestion.type === 'account') {
|
||||
} else if (suggestion.type === "account") {
|
||||
inner = <AutosuggestAccountContainer id={suggestion.id} />;
|
||||
key = suggestion.id;
|
||||
}
|
||||
|
||||
return (
|
||||
<div role='button' tabIndex={0} key={key} data-index={i} className={classNames('autosuggest-textarea__suggestions__item', { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
<div role='button' tabIndex={0} key={key} data-index={i} className={classNames("autosuggest-textarea__suggestions__item", { selected: i === selectedSuggestion })} onMouseDown={this.onSuggestionClick}>
|
||||
{inner}
|
||||
</div>
|
||||
);
|
||||
@@ -204,7 +204,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
<div className='compose-form__autosuggest-wrapper' key='autosuggest-wrapper'>
|
||||
<div className='autosuggest-textarea'>
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{placeholder}</span>
|
||||
<span style={{ display: "none" }}>{placeholder}</span>
|
||||
|
||||
<Textarea
|
||||
ref={this.setTextarea}
|
||||
@@ -229,7 +229,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent {
|
||||
</div>,
|
||||
|
||||
<div className='autosuggest-textarea__suggestions-wrapper' key='suggestions-wrapper'>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? '' : 'autosuggest-textarea__suggestions--visible'}`}>
|
||||
<div className={`autosuggest-textarea__suggestions ${suggestionsHidden || suggestions.isEmpty() ? "" : "autosuggest-textarea__suggestions--visible"}`}>
|
||||
{suggestions.map(this.renderSuggestion)}
|
||||
</div>
|
||||
</div>,
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { useHovering } from 'flavours/glitch/hooks/useHovering';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import type { Account } from 'flavours/glitch/types/resources';
|
||||
import { useHovering } from "flavours/glitch/hooks/useHovering";
|
||||
import { autoPlayGif } from "flavours/glitch/initial_state";
|
||||
import { type Account } from "flavours/glitch/types/resources";
|
||||
|
||||
interface Props {
|
||||
account: Account | undefined;
|
||||
className?: string;
|
||||
size: number;
|
||||
style?: React.CSSProperties;
|
||||
inline?: boolean;
|
||||
account: Account | undefined,
|
||||
className?: string,
|
||||
size: number,
|
||||
style?: React.CSSProperties,
|
||||
inline?: boolean,
|
||||
}
|
||||
|
||||
export const Avatar: React.FC<Props> = ({
|
||||
@@ -33,23 +33,23 @@ export const Avatar: React.FC<Props> = ({
|
||||
|
||||
if (account) {
|
||||
style.backgroundImage = `url(${account.get(
|
||||
hovering ? 'avatar' : 'avatar_static',
|
||||
hovering ? "avatar" : "avatar_static",
|
||||
)})`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames(
|
||||
'account__avatar',
|
||||
{ 'account__avatar-inline': inline },
|
||||
"account__avatar",
|
||||
{ "account__avatar-inline": inline },
|
||||
className,
|
||||
)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
style={style}
|
||||
data-avatar-of={account && `@${account.get('acct')}`}
|
||||
data-avatar-of={account && `@${account.get("acct")}`}
|
||||
role='img'
|
||||
aria-label={account?.get('acct')}
|
||||
aria-label={account?.get("acct")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import { autoPlayGif } from "flavours/glitch/initial_state";
|
||||
|
||||
export default class AvatarComposite extends PureComponent {
|
||||
|
||||
@@ -22,10 +22,10 @@ export default class AvatarComposite extends PureComponent {
|
||||
|
||||
let width = 50;
|
||||
let height = 100;
|
||||
let top = 'auto';
|
||||
let left = 'auto';
|
||||
let bottom = 'auto';
|
||||
let right = 'auto';
|
||||
let top = "auto";
|
||||
let left = "auto";
|
||||
let bottom = "auto";
|
||||
let right = "auto";
|
||||
|
||||
if (size === 1) {
|
||||
width = 100;
|
||||
@@ -37,35 +37,35 @@ export default class AvatarComposite extends PureComponent {
|
||||
|
||||
if (size === 2) {
|
||||
if (index === 0) {
|
||||
right = '1px';
|
||||
right = "1px";
|
||||
} else {
|
||||
left = '1px';
|
||||
left = "1px";
|
||||
}
|
||||
} else if (size === 3) {
|
||||
if (index === 0) {
|
||||
right = '1px';
|
||||
right = "1px";
|
||||
} else if (index > 0) {
|
||||
left = '1px';
|
||||
left = "1px";
|
||||
}
|
||||
|
||||
if (index === 1) {
|
||||
bottom = '1px';
|
||||
bottom = "1px";
|
||||
} else if (index > 1) {
|
||||
top = '1px';
|
||||
top = "1px";
|
||||
}
|
||||
} else if (size === 4) {
|
||||
if (index === 0 || index === 2) {
|
||||
right = '1px';
|
||||
right = "1px";
|
||||
}
|
||||
|
||||
if (index === 1 || index === 3) {
|
||||
left = '1px';
|
||||
left = "1px";
|
||||
}
|
||||
|
||||
if (index < 2) {
|
||||
bottom = '1px';
|
||||
bottom = "1px";
|
||||
} else {
|
||||
top = '1px';
|
||||
top = "1px";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,12 +76,12 @@ export default class AvatarComposite extends PureComponent {
|
||||
bottom: bottom,
|
||||
width: `${width}%`,
|
||||
height: `${height}%`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
||||
backgroundSize: "cover",
|
||||
backgroundImage: `url(${account.get(animate ? "avatar" : "avatar_static")})`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div key={account.get('id')} style={style} data-avatar-of={`@${account.get('acct')}`} />
|
||||
<div key={account.get("id")} style={style} data-avatar-of={`@${account.get("acct")}`} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import { autoPlayGif } from "flavours/glitch/initial_state";
|
||||
|
||||
export default class AvatarOverlay extends PureComponent {
|
||||
|
||||
@@ -21,17 +21,17 @@ export default class AvatarOverlay extends PureComponent {
|
||||
const { account, friend, animate } = this.props;
|
||||
|
||||
const baseStyle = {
|
||||
backgroundImage: `url(${account.get(animate ? 'avatar' : 'avatar_static')})`,
|
||||
backgroundImage: `url(${account.get(animate ? "avatar" : "avatar_static")})`,
|
||||
};
|
||||
|
||||
const overlayStyle = {
|
||||
backgroundImage: `url(${friend.get(animate ? 'avatar' : 'avatar_static')})`,
|
||||
backgroundImage: `url(${friend.get(animate ? "avatar" : "avatar_static")})`,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className='account__avatar-overlay'>
|
||||
<div className='account__avatar-overlay-base' style={baseStyle} data-avatar-of={`@${account.get('acct')}`} />
|
||||
<div className='account__avatar-overlay-overlay' style={overlayStyle} data-avatar-of={`@${friend.get('acct')}`} />
|
||||
<div className='account__avatar-overlay-base' style={baseStyle} data-avatar-of={`@${account.get("acct")}`} />
|
||||
<div className='account__avatar-overlay-overlay' style={overlayStyle} data-avatar-of={`@${friend.get("acct")}`} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useRef, useEffect } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useRef, useEffect } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import { decode } from 'blurhash';
|
||||
import { decode } from "blurhash";
|
||||
|
||||
interface Props extends React.HTMLAttributes<HTMLCanvasElement> {
|
||||
hash: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
dummy?: boolean; // Whether dummy mode is enabled. If enabled, nothing is rendered and canvas left untouched
|
||||
children?: never;
|
||||
hash: string,
|
||||
width?: number,
|
||||
height?: number,
|
||||
dummy?: boolean, // Whether dummy mode is enabled. If enabled, nothing is rendered and canvas left untouched
|
||||
children?: never,
|
||||
}
|
||||
const Blurhash: React.FC<Props> = ({
|
||||
hash,
|
||||
@@ -26,16 +26,18 @@ const Blurhash: React.FC<Props> = ({
|
||||
// eslint-disable-next-line no-self-assign
|
||||
canvas.width = canvas.width; // resets canvas
|
||||
|
||||
if (dummy || !hash) return;
|
||||
if (dummy || !hash) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const pixels = decode(hash, width, height);
|
||||
const ctx = canvas.getContext('2d');
|
||||
const ctx = canvas.getContext("2d");
|
||||
const imageData = new ImageData(pixels, width, height);
|
||||
|
||||
ctx?.putImageData(imageData, 0, 0);
|
||||
} catch (err) {
|
||||
console.error('Blurhash decoding failure', { err, hash });
|
||||
console.error("Blurhash decoding failure", { err, hash });
|
||||
}
|
||||
}, [dummy, hash, width, height]);
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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";
|
||||
|
||||
export default class Button extends PureComponent {
|
||||
|
||||
@@ -32,16 +32,18 @@ export default class Button extends PureComponent {
|
||||
|
||||
render () {
|
||||
let attrs = {
|
||||
className: classNames('button', this.props.className, {
|
||||
'button-secondary': this.props.secondary,
|
||||
'button--block': this.props.block,
|
||||
className: classNames("button", this.props.className, {
|
||||
"button-secondary": this.props.secondary,
|
||||
"button--block": this.props.block,
|
||||
}),
|
||||
disabled: this.props.disabled,
|
||||
onClick: this.handleClick,
|
||||
ref: this.setRef,
|
||||
};
|
||||
|
||||
if (this.props.title) attrs.title = this.props.title;
|
||||
if (this.props.title) {
|
||||
attrs.title = this.props.title;
|
||||
}
|
||||
|
||||
return (
|
||||
<button {...attrs}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
interface Props {
|
||||
size: number;
|
||||
strokeWidth: number;
|
||||
size: number,
|
||||
strokeWidth: number,
|
||||
}
|
||||
|
||||
export const CircularProgress: React.FC<Props> = ({ size, strokeWidth }) => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import { supportsPassiveEvents } from "detect-passive-events";
|
||||
|
||||
import { scrollTop } from '../scroll';
|
||||
import { scrollTop } from "../scroll";
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
|
||||
|
||||
@@ -23,12 +23,12 @@ export default class Column extends PureComponent {
|
||||
if (this.props.bindToDocument) {
|
||||
scrollable = document.scrollingElement;
|
||||
} else {
|
||||
scrollable = this.node.querySelector('.scrollable');
|
||||
scrollable = this.node.querySelector(".scrollable");
|
||||
|
||||
// Some columns have nested `.scrollable` containers, with the outer one
|
||||
// being a wrapper while the actual scrollable content is deeper.
|
||||
if (scrollable.classList.contains('scrollable--flex')) {
|
||||
scrollable = scrollable?.querySelector('.scrollable') || scrollable;
|
||||
if (scrollable.classList.contains("scrollable--flex")) {
|
||||
scrollable = scrollable?.querySelector(".scrollable") || scrollable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class Column extends PureComponent {
|
||||
}
|
||||
|
||||
handleWheel = () => {
|
||||
if (typeof this._interruptScrollAnimation !== 'function') {
|
||||
if (typeof this._interruptScrollAnimation !== "function") {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -53,17 +53,17 @@ export default class Column extends PureComponent {
|
||||
|
||||
componentDidMount () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
document.addEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
this.node.addEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
document.removeEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
this.node.removeEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ export default class Column extends PureComponent {
|
||||
const { children, extraClasses, name, label } = this.props;
|
||||
|
||||
return (
|
||||
<div role='region' aria-label={label} data-column={name} className={`column ${extraClasses || ''}`} ref={this.setRef}>
|
||||
<div role='region' aria-label={label} data-column={name} className={`column ${extraClasses || ""}`} ref={this.setRef}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
|
||||
export default class ColumnBackButton extends PureComponent {
|
||||
@@ -23,7 +23,7 @@ export default class ColumnBackButton extends PureComponent {
|
||||
if (router.history.location?.state?.fromMastodon) {
|
||||
router.history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
router.history.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -43,7 +43,7 @@ export default class ColumnBackButton extends PureComponent {
|
||||
// The portal container and the component may be rendered to the DOM in
|
||||
// the same React render pass, so the container might not be available at
|
||||
// the time `render()` is called.
|
||||
const container = document.getElementById('tabs-bar__portal');
|
||||
const container = document.getElementById("tabs-bar__portal");
|
||||
if (container === null) {
|
||||
// The container wasn't available, force a re-render so that the
|
||||
// component can eventually be inserted in the container and not scroll
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
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 { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
export default class ColumnBackButtonSlim extends PureComponent {
|
||||
|
||||
@@ -19,7 +19,7 @@ export default class ColumnBackButtonSlim extends PureComponent {
|
||||
if (router.route.location.key) {
|
||||
router.history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
router.history.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
|
||||
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl, defineMessages } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
const messages = defineMessages({
|
||||
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
|
||||
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
|
||||
moveLeft: { id: 'column_header.moveLeft_settings', defaultMessage: 'Move column to the left' },
|
||||
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
|
||||
show: { id: "column_header.show_settings", defaultMessage: "Show settings" },
|
||||
hide: { id: "column_header.hide_settings", defaultMessage: "Hide settings" },
|
||||
moveLeft: { id: "column_header.moveLeft_settings", defaultMessage: "Move column to the left" },
|
||||
moveRight: { id: "column_header.moveRight_settings", defaultMessage: "Move column to the right" },
|
||||
});
|
||||
|
||||
class ColumnHeader extends PureComponent {
|
||||
@@ -68,7 +68,7 @@ class ColumnHeader extends PureComponent {
|
||||
if (router.history.location?.state?.fromMastodon) {
|
||||
router.history.goBack();
|
||||
} else {
|
||||
router.history.push('/');
|
||||
router.history.push("/");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,7 +78,7 @@ class ColumnHeader extends PureComponent {
|
||||
|
||||
handlePin = () => {
|
||||
if (!this.props.pinned) {
|
||||
this.context.router.history.replace('/');
|
||||
this.context.router.history.replace("/");
|
||||
}
|
||||
|
||||
this.props.onPin();
|
||||
@@ -89,21 +89,21 @@ class ColumnHeader extends PureComponent {
|
||||
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
|
||||
const { collapsed, animating } = this.state;
|
||||
|
||||
const wrapperClassName = classNames('column-header__wrapper', {
|
||||
'active': active,
|
||||
const wrapperClassName = classNames("column-header__wrapper", {
|
||||
"active": active,
|
||||
});
|
||||
|
||||
const buttonClassName = classNames('column-header', {
|
||||
'active': active,
|
||||
const buttonClassName = classNames("column-header", {
|
||||
"active": active,
|
||||
});
|
||||
|
||||
const collapsibleClassName = classNames('column-header__collapsible', {
|
||||
'collapsed': collapsed,
|
||||
'animating': animating,
|
||||
const collapsibleClassName = classNames("column-header__collapsible", {
|
||||
"collapsed": collapsed,
|
||||
"animating": animating,
|
||||
});
|
||||
|
||||
const collapsibleButtonClassName = classNames('column-header__button', {
|
||||
'active': !collapsed,
|
||||
const collapsibleButtonClassName = classNames("column-header__button", {
|
||||
"active": !collapsed,
|
||||
});
|
||||
|
||||
let extraContent, pinButton, moveButtons, backButton, collapseButton;
|
||||
@@ -200,7 +200,7 @@ class ColumnHeader extends PureComponent {
|
||||
// The portal container and the component may be rendered to the DOM in
|
||||
// the same React render pass, so the container might not be available at
|
||||
// the time `render()` is called.
|
||||
const container = document.getElementById('tabs-bar__portal');
|
||||
const container = document.getElementById("tabs-bar__portal");
|
||||
if (container === null) {
|
||||
// The container wasn't available, force a re-render so that the
|
||||
// component can eventually be inserted in the container and not scroll
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
export const StatusesCounter = (
|
||||
displayNumber: React.ReactNode,
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import { useCallback, useState } from 'react';
|
||||
import { type PropsWithChildren } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from "react-intl";
|
||||
|
||||
import { bannerSettings } from 'flavours/glitch/settings';
|
||||
import { bannerSettings } from "flavours/glitch/settings";
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { IconButton } from "./icon_button";
|
||||
|
||||
const messages = defineMessages({
|
||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||
dismiss: { id: "dismissable_banner.dismiss", defaultMessage: "Dismiss" },
|
||||
});
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
id: string,
|
||||
}
|
||||
|
||||
export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import type { List } from 'immutable';
|
||||
import { type List } from "immutable";
|
||||
|
||||
import type { Account } from 'flavours/glitch/types/resources';
|
||||
import { type Account } from "flavours/glitch/types/resources";
|
||||
|
||||
import { autoPlayGif } from '../initial_state';
|
||||
import { autoPlayGif } from "../initial_state";
|
||||
|
||||
import { Skeleton } from './skeleton';
|
||||
import { Skeleton } from "./skeleton";
|
||||
|
||||
interface Props {
|
||||
account?: Account;
|
||||
others?: List<Account>;
|
||||
localDomain?: string;
|
||||
inline?: boolean;
|
||||
account?: Account,
|
||||
others?: List<Account>,
|
||||
localDomain?: string,
|
||||
inline?: boolean,
|
||||
}
|
||||
|
||||
export class DisplayName extends React.PureComponent<Props> {
|
||||
@@ -26,11 +26,13 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
const emojis =
|
||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
||||
currentTarget.querySelectorAll<HTMLImageElement>("img.custom-emoji");
|
||||
|
||||
emojis.forEach((emoji) => {
|
||||
const originalSrc = emoji.getAttribute('data-original');
|
||||
if (originalSrc != null) emoji.src = originalSrc;
|
||||
const originalSrc = emoji.getAttribute("data-original");
|
||||
if (originalSrc != null) {
|
||||
emoji.src = originalSrc;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -42,11 +44,13 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||
}
|
||||
|
||||
const emojis =
|
||||
currentTarget.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
||||
currentTarget.querySelectorAll<HTMLImageElement>("img.custom-emoji");
|
||||
|
||||
emojis.forEach((emoji) => {
|
||||
const staticSrc = emoji.getAttribute('data-static');
|
||||
if (staticSrc != null) emoji.src = staticSrc;
|
||||
const staticSrc = emoji.getAttribute("data-static");
|
||||
if (staticSrc != null) {
|
||||
emoji.src = staticSrc;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -67,22 +71,22 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||
displayName = others
|
||||
.take(2)
|
||||
.map((a) => (
|
||||
<bdi key={a.get('id')}>
|
||||
<bdi key={a.get("id")}>
|
||||
<strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{ __html: a.get('display_name_html') }}
|
||||
dangerouslySetInnerHTML={{ __html: a.get("display_name_html") }}
|
||||
/>
|
||||
</bdi>
|
||||
))
|
||||
.reduce((prev, cur) => [prev, ', ', cur]);
|
||||
.reduce((prev, cur) => [prev, ", ", cur]);
|
||||
|
||||
if (others.size - 2 > 0) {
|
||||
suffix = `+${others.size - 2}`;
|
||||
}
|
||||
} else if (account) {
|
||||
let acct = account.get('acct');
|
||||
let acct = account.get("acct");
|
||||
|
||||
if (!acct.includes('@') && localDomain) {
|
||||
if (!acct.includes("@") && localDomain) {
|
||||
acct = `${acct}@${localDomain}`;
|
||||
}
|
||||
|
||||
@@ -91,7 +95,7 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||
<strong
|
||||
className='display-name__html'
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: account.get('display_name_html'),
|
||||
__html: account.get("display_name_html"),
|
||||
}}
|
||||
/>
|
||||
</bdi>
|
||||
@@ -114,12 +118,12 @@ export class DisplayName extends React.PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<span
|
||||
className={classNames('display-name', { inline })}
|
||||
className={classNames("display-name", { inline })}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
>
|
||||
{displayName}
|
||||
{inline ? ' ' : null}
|
||||
{inline ? " " : null}
|
||||
{suffix}
|
||||
</span>
|
||||
);
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { useCallback } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useCallback } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import { defineMessages, useIntl } from 'react-intl';
|
||||
import { defineMessages, useIntl } from "react-intl";
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { IconButton } from "./icon_button";
|
||||
|
||||
const messages = defineMessages({
|
||||
unblockDomain: {
|
||||
id: 'account.unblock_domain',
|
||||
defaultMessage: 'Unblock domain {domain}',
|
||||
id: "account.unblock_domain",
|
||||
defaultMessage: "Unblock domain {domain}",
|
||||
},
|
||||
});
|
||||
|
||||
interface Props {
|
||||
domain: string;
|
||||
onUnblockDomain: (domain: string) => void;
|
||||
domain: string,
|
||||
onUnblockDomain: (domain: string) => void,
|
||||
}
|
||||
|
||||
export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => {
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent, cloneElement, Children } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent, cloneElement, Children } from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import Overlay from 'react-overlays/Overlay';
|
||||
import { supportsPassiveEvents } from "detect-passive-events";
|
||||
import Overlay from "react-overlays/Overlay";
|
||||
|
||||
import { CircularProgress } from "./circular_progress";
|
||||
import { IconButton } from './icon_button';
|
||||
import { IconButton } from "./icon_button";
|
||||
|
||||
const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
|
||||
let id = 0;
|
||||
@@ -44,9 +44,9 @@ class DropdownMenu extends PureComponent {
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
document.addEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener('keydown', this.handleKeyDown, { capture: true });
|
||||
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
document.addEventListener("click", this.handleDocumentClick, { capture: true });
|
||||
document.addEventListener("keydown", this.handleKeyDown, { capture: true });
|
||||
document.addEventListener("touchend", this.handleDocumentClick, listenerOptions);
|
||||
|
||||
if (this.focusedItem && this.props.openedViaKeyboard) {
|
||||
this.focusedItem.focus({ preventScroll: true });
|
||||
@@ -54,9 +54,9 @@ class DropdownMenu extends PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
document.removeEventListener('click', this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener('keydown', this.handleKeyDown, { capture: true });
|
||||
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
|
||||
document.removeEventListener("click", this.handleDocumentClick, { capture: true });
|
||||
document.removeEventListener("keydown", this.handleKeyDown, { capture: true });
|
||||
document.removeEventListener("touchend", this.handleDocumentClick, listenerOptions);
|
||||
}
|
||||
|
||||
setRef = c => {
|
||||
@@ -68,33 +68,33 @@ class DropdownMenu extends PureComponent {
|
||||
};
|
||||
|
||||
handleKeyDown = e => {
|
||||
const items = Array.from(this.node.querySelectorAll('a, button'));
|
||||
const items = Array.from(this.node.querySelectorAll("a, button"));
|
||||
const index = items.indexOf(document.activeElement);
|
||||
let element = null;
|
||||
|
||||
switch(e.key) {
|
||||
case 'ArrowDown':
|
||||
element = items[index+1] || items[0];
|
||||
break;
|
||||
case 'ArrowUp':
|
||||
element = items[index-1] || items[items.length-1];
|
||||
break;
|
||||
case 'Tab':
|
||||
if (e.shiftKey) {
|
||||
element = items[index-1] || items[items.length-1];
|
||||
} else {
|
||||
case "ArrowDown":
|
||||
element = items[index+1] || items[0];
|
||||
}
|
||||
break;
|
||||
case 'Home':
|
||||
element = items[0];
|
||||
break;
|
||||
case 'End':
|
||||
element = items[items.length-1];
|
||||
break;
|
||||
case 'Escape':
|
||||
this.props.onClose();
|
||||
break;
|
||||
break;
|
||||
case "ArrowUp":
|
||||
element = items[index-1] || items[items.length-1];
|
||||
break;
|
||||
case "Tab":
|
||||
if (e.shiftKey) {
|
||||
element = items[index-1] || items[items.length-1];
|
||||
} else {
|
||||
element = items[index+1] || items[0];
|
||||
}
|
||||
break;
|
||||
case "Home":
|
||||
element = items[0];
|
||||
break;
|
||||
case "End":
|
||||
element = items[items.length-1];
|
||||
break;
|
||||
case "Escape":
|
||||
this.props.onClose();
|
||||
break;
|
||||
}
|
||||
|
||||
if (element) {
|
||||
@@ -105,7 +105,7 @@ class DropdownMenu extends PureComponent {
|
||||
};
|
||||
|
||||
handleItemKeyPress = e => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
this.handleClick(e);
|
||||
}
|
||||
};
|
||||
@@ -120,10 +120,10 @@ class DropdownMenu extends PureComponent {
|
||||
return <li key={`sep-${i}`} className='dropdown-menu__separator' />;
|
||||
}
|
||||
|
||||
const { text, href = '#', target = '_blank', method, dangerous } = option;
|
||||
const { text, href = "#", target = "_blank", method, dangerous } = option;
|
||||
|
||||
return (
|
||||
<li className={classNames('dropdown-menu__item', { 'dropdown-menu__item--dangerous': dangerous })} key={`${text}-${i}`}>
|
||||
<li className={classNames("dropdown-menu__item", { "dropdown-menu__item--dangerous": dangerous })} key={`${text}-${i}`}>
|
||||
<a href={href} target={target} data-method={method} rel='noopener noreferrer' role='button' tabIndex={0} ref={i === 0 ? this.setFocusRef : null} onClick={this.handleClick} onKeyPress={this.handleItemKeyPress} data-index={i}>
|
||||
{text}
|
||||
</a>
|
||||
@@ -137,7 +137,7 @@ class DropdownMenu extends PureComponent {
|
||||
let renderItem = this.props.renderItem || this.renderItem;
|
||||
|
||||
return (
|
||||
<div className={classNames('dropdown-menu__container', { 'dropdown-menu__container--loading': loading })} ref={this.setRef}>
|
||||
<div className={classNames("dropdown-menu__container", { "dropdown-menu__container--loading": loading })} ref={this.setRef}>
|
||||
{loading && (
|
||||
<CircularProgress size={30} strokeWidth={3.5} />
|
||||
)}
|
||||
@@ -149,7 +149,7 @@ class DropdownMenu extends PureComponent {
|
||||
)}
|
||||
|
||||
{!loading && (
|
||||
<ul className={classNames('dropdown-menu__container__list', { 'dropdown-menu__container__list--scrollable': scrollable })}>
|
||||
<ul className={classNames("dropdown-menu__container__list", { "dropdown-menu__container__list--scrollable": scrollable })}>
|
||||
{items.map((option, i) => renderItem(option, i, { onClick: this.handleClick, onKeyPress: this.handleItemKeyPress }))}
|
||||
</ul>
|
||||
)}
|
||||
@@ -186,7 +186,7 @@ export default class Dropdown extends PureComponent {
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
title: 'Menu',
|
||||
title: "Menu",
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -197,7 +197,7 @@ export default class Dropdown extends PureComponent {
|
||||
if (this.state.id === this.props.openDropdownId) {
|
||||
this.handleClose();
|
||||
} else {
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, type !== 'click');
|
||||
this.props.onOpen(this.state.id, this.handleItemClick, type !== "click");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -217,35 +217,35 @@ export default class Dropdown extends PureComponent {
|
||||
|
||||
handleButtonKeyDown = (e) => {
|
||||
switch(e.key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
this.handleMouseDown();
|
||||
break;
|
||||
case " ":
|
||||
case "Enter":
|
||||
this.handleMouseDown();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress = (e) => {
|
||||
switch(e.key) {
|
||||
case ' ':
|
||||
case 'Enter':
|
||||
this.handleClick(e);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
break;
|
||||
case " ":
|
||||
case "Enter":
|
||||
this.handleClick(e);
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
handleItemClick = e => {
|
||||
const { onItemClick } = this.props;
|
||||
const i = Number(e.currentTarget.getAttribute('data-index'));
|
||||
const i = Number(e.currentTarget.getAttribute("data-index"));
|
||||
const item = this.props.items[i];
|
||||
|
||||
this.handleClose();
|
||||
|
||||
if (typeof onItemClick === 'function') {
|
||||
if (typeof onItemClick === "function") {
|
||||
e.preventDefault();
|
||||
onItemClick(item, i);
|
||||
} else if (item && typeof item.action === 'function') {
|
||||
} else if (item && typeof item.action === "function") {
|
||||
e.preventDefault();
|
||||
item.action();
|
||||
} else if (item && item.to) {
|
||||
@@ -314,7 +314,7 @@ export default class Dropdown extends PureComponent {
|
||||
<span ref={this.setTargetRef}>
|
||||
{button}
|
||||
</span>
|
||||
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
|
||||
<Overlay show={open} offset={[5, 5]} placement={"bottom"} flip target={this.findTarget} popperConfig={{ strategy: "fixed" }}>
|
||||
{({ props, arrowProps, placement }) => (
|
||||
<div {...props}>
|
||||
<div className={`dropdown-animation dropdown-menu ${placement}`}>
|
||||
|
||||
+6
-6
@@ -1,8 +1,8 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { openDropdownMenu, closeDropdownMenu } from 'flavours/glitch/actions/dropdown_menu';
|
||||
import { fetchHistory } from 'flavours/glitch/actions/history';
|
||||
import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
||||
import { openDropdownMenu, closeDropdownMenu } from "flavours/glitch/actions/dropdown_menu";
|
||||
import { fetchHistory } from "flavours/glitch/actions/history";
|
||||
import DropdownMenu from "flavours/glitch/components/dropdown_menu";
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -12,8 +12,8 @@ import DropdownMenu from 'flavours/glitch/components/dropdown_menu';
|
||||
const mapStateToProps = (state, { statusId }) => ({
|
||||
openDropdownId: state.dropdownMenu.openId,
|
||||
openedViaKeyboard: state.dropdownMenu.keyboard,
|
||||
items: state.getIn(['history', statusId, 'items']),
|
||||
loading: state.getIn(['history', statusId, 'loading']),
|
||||
items: state.getIn(["history", statusId, "items"]),
|
||||
loading: state.getIn(["history", statusId, "loading"]),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { statusId }) => ({
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl } from "react-intl";
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { openModal } from 'flavours/glitch/actions/modal';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import InlineAccount from 'flavours/glitch/components/inline_account';
|
||||
import { RelativeTimestamp } from 'flavours/glitch/components/relative_timestamp';
|
||||
import { openModal } from "flavours/glitch/actions/modal";
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
import InlineAccount from "flavours/glitch/components/inline_account";
|
||||
import { RelativeTimestamp } from "flavours/glitch/components/relative_timestamp";
|
||||
|
||||
import DropdownMenu from './containers/dropdown_menu_container';
|
||||
import DropdownMenu from "./containers/dropdown_menu_container";
|
||||
|
||||
const mapDispatchToProps = (dispatch, { statusId }) => ({
|
||||
|
||||
onItemClick (index) {
|
||||
dispatch(openModal({
|
||||
modalType: 'COMPARE_HISTORY',
|
||||
modalType: "COMPARE_HISTORY",
|
||||
modalProps: { index, statusId },
|
||||
}));
|
||||
},
|
||||
@@ -44,17 +44,17 @@ class EditedTimestamp extends PureComponent {
|
||||
};
|
||||
|
||||
renderItem = (item, index, { onClick, onKeyPress }) => {
|
||||
const formattedDate = <RelativeTimestamp timestamp={item.get('created_at')} short={false} />;
|
||||
const formattedName = <InlineAccount accountId={item.get('account')} />;
|
||||
const formattedDate = <RelativeTimestamp timestamp={item.get("created_at")} short={false} />;
|
||||
const formattedName = <InlineAccount accountId={item.get("account")} />;
|
||||
|
||||
const label = item.get('original') ? (
|
||||
const label = item.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 }} />
|
||||
);
|
||||
|
||||
return (
|
||||
<li className='dropdown-menu__item edited-timestamp__history__item' key={item.get('created_at')}>
|
||||
<li className='dropdown-menu__item edited-timestamp__history__item' key={item.get("created_at")}>
|
||||
<button data-index={index} onClick={onClick} onKeyPress={onKeyPress}>{label}</button>
|
||||
</li>
|
||||
);
|
||||
@@ -66,7 +66,7 @@ class EditedTimestamp extends PureComponent {
|
||||
return (
|
||||
<DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}>
|
||||
<button className='dropdown-menu__text-button'>
|
||||
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hourCycle: 'h23', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' />
|
||||
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hourCycle: "h23", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }) }} /> <Icon id='caret-down' />
|
||||
</button>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
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 { Helmet } from 'react-helmet';
|
||||
import { Helmet } from "react-helmet";
|
||||
|
||||
import StackTrace from 'stacktrace-js';
|
||||
import StackTrace from "stacktrace-js";
|
||||
|
||||
import { version, source_url } from 'flavours/glitch/initial_state';
|
||||
import { version, source_url } from "flavours/glitch/initial_state";
|
||||
|
||||
export default class ErrorBoundary extends PureComponent {
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class ErrorBoundary extends PureComponent {
|
||||
|
||||
StackTrace.fromError(error).then((stackframes) => {
|
||||
this.setState({
|
||||
mappedStackTrace: stackframes.map((sf) => sf.toString()).join('\n'),
|
||||
mappedStackTrace: stackframes.map((sf) => sf.toString()).join("\n"),
|
||||
});
|
||||
}).catch(() => {
|
||||
this.setState({
|
||||
@@ -45,23 +45,23 @@ export default class ErrorBoundary extends PureComponent {
|
||||
|
||||
handleCopyStackTrace = () => {
|
||||
const { errorMessage, stackTrace, mappedStackTrace } = this.state;
|
||||
const textarea = document.createElement('textarea');
|
||||
const textarea = document.createElement("textarea");
|
||||
|
||||
let contents = [errorMessage, stackTrace];
|
||||
if (mappedStackTrace) {
|
||||
contents.push(mappedStackTrace);
|
||||
}
|
||||
|
||||
textarea.textContent = contents.join('\n\n\n');
|
||||
textarea.style.position = 'fixed';
|
||||
textarea.textContent = contents.join("\n\n\n");
|
||||
textarea.style.position = "fixed";
|
||||
|
||||
document.body.appendChild(textarea);
|
||||
|
||||
try {
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
document.execCommand("copy");
|
||||
} catch (e) {
|
||||
|
||||
console.error(e);
|
||||
} finally {
|
||||
document.body.removeChild(textarea);
|
||||
}
|
||||
@@ -77,7 +77,7 @@ export default class ErrorBoundary extends PureComponent {
|
||||
return this.props.children;
|
||||
}
|
||||
|
||||
const likelyBrowserAddonIssue = errorMessage && errorMessage.includes('NotFoundError');
|
||||
const likelyBrowserAddonIssue = errorMessage && errorMessage.includes("NotFoundError");
|
||||
|
||||
return (
|
||||
<div className='error-boundary'>
|
||||
@@ -98,7 +98,7 @@ export default class ErrorBoundary extends PureComponent {
|
||||
)}
|
||||
</p>
|
||||
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? 'copied' : ''}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||
<p className='error-boundary__footer'>Mastodon v{version} · <a href={source_url} rel='noopener noreferrer' target='_blank'><FormattedMessage id='errors.unexpected_crash.report_issue' defaultMessage='Report issue' /></a> · <button onClick={this.handleCopyStackTrace} className={copied ? "copied" : ""}><FormattedMessage id='errors.unexpected_crash.copy_stacktrace' defaultMessage='Copy stacktrace to clipboard' /></button></p>
|
||||
</div>
|
||||
|
||||
<Helmet>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useState } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
interface Props {
|
||||
src: string;
|
||||
key: string;
|
||||
alt?: string;
|
||||
lang?: string;
|
||||
width: number;
|
||||
height: number;
|
||||
onClick?: () => void;
|
||||
src: string,
|
||||
key: string,
|
||||
alt?: string,
|
||||
lang?: string,
|
||||
width: number,
|
||||
height: number,
|
||||
onClick?: () => void,
|
||||
}
|
||||
|
||||
export const GIFV: React.FC<Props> = ({
|
||||
@@ -37,7 +37,7 @@ export const GIFV: React.FC<Props> = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='gifv' style={{ position: 'relative' }}>
|
||||
<div className='gifv' style={{ position: "relative" }}>
|
||||
{loading && (
|
||||
<canvas
|
||||
width={width}
|
||||
@@ -64,7 +64,7 @@ export const GIFV: React.FC<Props> = ({
|
||||
playsInline
|
||||
onClick={handleClick}
|
||||
onLoadedData={handleLoadedData}
|
||||
style={{ position: loading ? 'absolute' : 'static', top: 0, left: 0 }}
|
||||
style={{ position: loading ? "absolute" : "static", top: 0, left: 0 }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
// @ts-check
|
||||
import PropTypes from 'prop-types';
|
||||
import { Component } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { Component } from "react";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||
import { Sparklines, SparklinesCurve } from "react-sparklines";
|
||||
|
||||
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import { ShortNumber } from "flavours/glitch/components/short_number";
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
|
||||
import Permalink from './permalink';
|
||||
import Permalink from "./permalink";
|
||||
|
||||
class SilentErrorBoundary extends Component {
|
||||
|
||||
@@ -58,12 +58,12 @@ export const accountsCountRenderer = (displayNumber, pluralReady) => (
|
||||
// @ts-expect-error
|
||||
export const ImmutableHashtag = ({ hashtag }) => (
|
||||
<Hashtag
|
||||
name={hashtag.get('name')}
|
||||
href={hashtag.get('url')}
|
||||
to={`/tags/${hashtag.get('name')}`}
|
||||
people={hashtag.getIn(['history', 0, 'accounts']) * 1 + hashtag.getIn(['history', 1, 'accounts']) * 1}
|
||||
name={hashtag.get("name")}
|
||||
href={hashtag.get("url")}
|
||||
to={`/tags/${hashtag.get("name")}`}
|
||||
people={hashtag.getIn(["history", 0, "accounts"]) * 1 + hashtag.getIn(["history", 1, "accounts"]) * 1}
|
||||
// @ts-expect-error
|
||||
history={hashtag.get('history')?.reverse().map((day) => day.get('uses')).toArray()}
|
||||
history={hashtag.get("history")?.reverse().map((day) => day.get("uses")).toArray()}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -73,7 +73,7 @@ ImmutableHashtag.propTypes = {
|
||||
|
||||
// @ts-expect-error
|
||||
const Hashtag = ({ name, href, to, people, uses, history, className, description, withGraph }) => (
|
||||
<div className={classNames('trends__item', className)}>
|
||||
<div className={classNames("trends__item", className)}>
|
||||
<div className='trends__item__name'>
|
||||
<Permalink href={href} to={to}>
|
||||
{name ? <>#<span>{name}</span></> : <Skeleton width={50} />}
|
||||
@@ -82,21 +82,21 @@ const Hashtag = ({ name, href, to, people, uses, history, className, description
|
||||
{description ? (
|
||||
<span>{description}</span>
|
||||
) : (
|
||||
!isNaN(people) && (typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />)
|
||||
!isNaN(people) && (typeof people !== "undefined" ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} />)
|
||||
)}
|
||||
</div>
|
||||
|
||||
{typeof uses !== 'undefined' && (
|
||||
{typeof uses !== "undefined" && (
|
||||
<div className='trends__item__current'>
|
||||
<ShortNumber value={uses} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{withGraph && typeof history !== 'undefined' && (
|
||||
{withGraph && typeof history !== "undefined" && (
|
||||
<div className='trends__item__sparkline'>
|
||||
<SilentErrorBoundary>
|
||||
<Sparklines width={50} height={28} data={history ? history : Array.from(Array(7)).map(() => 0)}>
|
||||
<SparklinesCurve style={{ fill: 'none' }} />
|
||||
<SparklinesCurve style={{ fill: "none" }} />
|
||||
</Sparklines>
|
||||
</SilentErrorBoundary>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
interface Props extends React.HTMLAttributes<HTMLImageElement> {
|
||||
id: string;
|
||||
className?: string;
|
||||
fixedWidth?: boolean;
|
||||
children?: never;
|
||||
id: string,
|
||||
className?: string,
|
||||
fixedWidth?: boolean,
|
||||
children?: never,
|
||||
}
|
||||
|
||||
export const Icon: React.FC<Props> = ({
|
||||
@@ -16,7 +16,7 @@ export const Icon: React.FC<Props> = ({
|
||||
...other
|
||||
}) => (
|
||||
<i
|
||||
className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })}
|
||||
className={classNames("fa", `fa-${id}`, className, { "fa-fw": fixedWidth })}
|
||||
{...other}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { AnimatedNumber } from './animated_number';
|
||||
import { Icon } from './icon';
|
||||
import { AnimatedNumber } from "./animated_number";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
interface Props {
|
||||
className?: string;
|
||||
title: string;
|
||||
icon: string;
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
|
||||
size: number;
|
||||
active: boolean;
|
||||
expanded?: boolean;
|
||||
style?: React.CSSProperties;
|
||||
activeStyle?: React.CSSProperties;
|
||||
disabled: boolean;
|
||||
inverted?: boolean;
|
||||
animate: boolean;
|
||||
overlay: boolean;
|
||||
tabIndex: number;
|
||||
label?: string;
|
||||
counter?: number;
|
||||
obfuscateCount?: boolean;
|
||||
href?: string;
|
||||
ariaHidden: boolean;
|
||||
className?: string,
|
||||
title: string,
|
||||
icon: string,
|
||||
onClick?: React.MouseEventHandler<HTMLButtonElement>,
|
||||
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>,
|
||||
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>,
|
||||
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>,
|
||||
size: number,
|
||||
active: boolean,
|
||||
expanded?: boolean,
|
||||
style?: React.CSSProperties,
|
||||
activeStyle?: React.CSSProperties,
|
||||
disabled: boolean,
|
||||
inverted?: boolean,
|
||||
animate: boolean,
|
||||
overlay: boolean,
|
||||
tabIndex: number,
|
||||
label?: string,
|
||||
counter?: number,
|
||||
obfuscateCount?: boolean,
|
||||
href?: string,
|
||||
ariaHidden: boolean,
|
||||
}
|
||||
interface States {
|
||||
activate: boolean;
|
||||
deactivate: boolean;
|
||||
activate: boolean,
|
||||
deactivate: boolean,
|
||||
}
|
||||
export class IconButton extends React.PureComponent<Props, States> {
|
||||
static defaultProps = {
|
||||
@@ -50,7 +50,9 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
};
|
||||
|
||||
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
||||
if (!nextProps.animate) return;
|
||||
if (!nextProps.animate) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.active && !nextProps.active) {
|
||||
this.setState({ activate: false, deactivate: true });
|
||||
@@ -67,12 +69,6 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyPress: React.KeyboardEventHandler<HTMLButtonElement> = (e) => {
|
||||
if (this.props.onKeyPress && !this.props.disabled) {
|
||||
this.props.onKeyPress(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleMouseDown: React.MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
if (!this.props.disabled && this.props.onMouseDown) {
|
||||
this.props.onMouseDown(e);
|
||||
@@ -87,7 +83,7 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
|
||||
render() {
|
||||
// Hack required for some icons which have an overriden size
|
||||
let containerSize = '1.28571429em';
|
||||
let containerSize = "1.28571429em";
|
||||
if (this.props.style?.fontSize) {
|
||||
containerSize = `${this.props.size * 1.28571429}px`;
|
||||
}
|
||||
@@ -102,7 +98,7 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
if (!this.props.label) {
|
||||
style.width = containerSize;
|
||||
} else {
|
||||
style.textAlign = 'left';
|
||||
style.textAlign = "left";
|
||||
}
|
||||
|
||||
const {
|
||||
@@ -123,24 +119,24 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
|
||||
const { activate, deactivate } = this.state;
|
||||
|
||||
const classes = classNames(className, 'icon-button', {
|
||||
const classes = classNames(className, "icon-button", {
|
||||
active,
|
||||
disabled,
|
||||
inverted,
|
||||
activate,
|
||||
deactivate,
|
||||
overlayed: overlay,
|
||||
'icon-button--with-counter': typeof counter !== 'undefined',
|
||||
"icon-button--with-counter": typeof counter !== "undefined",
|
||||
});
|
||||
|
||||
if (typeof counter !== 'undefined') {
|
||||
style.width = 'auto';
|
||||
if (typeof counter !== "undefined") {
|
||||
style.width = "auto";
|
||||
}
|
||||
|
||||
let contents = (
|
||||
<>
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />{' '}
|
||||
{typeof counter !== 'undefined' && (
|
||||
<Icon id={icon} fixedWidth aria-hidden='true' />{" "}
|
||||
{typeof counter !== "undefined" && (
|
||||
<span className='icon-button__counter'>
|
||||
<AnimatedNumber value={counter} obfuscate={obfuscateCount} />
|
||||
</span>
|
||||
@@ -168,7 +164,6 @@ export class IconButton extends React.PureComponent<Props, States> {
|
||||
onClick={this.handleClick}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
onKeyDown={this.handleKeyDown}
|
||||
onKeyPress={this.handleKeyPress}
|
||||
style={style}
|
||||
tabIndex={tabIndex}
|
||||
disabled={disabled}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import { Icon } from './icon';
|
||||
import { Icon } from "./icon";
|
||||
|
||||
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
|
||||
const formatNumber = (num: number): number | string => (num > 40 ? "40+" : num);
|
||||
|
||||
interface Props {
|
||||
id: string;
|
||||
count: number;
|
||||
issueBadge: boolean;
|
||||
className: string;
|
||||
id: string,
|
||||
count: number,
|
||||
issueBadge: boolean,
|
||||
className: string,
|
||||
}
|
||||
export const IconWithBadge: React.FC<Props> = ({
|
||||
id,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
import { makeGetAccount } from 'flavours/glitch/selectors';
|
||||
import { Avatar } from "flavours/glitch/components/avatar";
|
||||
import { makeGetAccount } from "flavours/glitch/selectors";
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
const getAccount = makeGetAccount();
|
||||
@@ -27,7 +27,7 @@ class InlineAccount extends PureComponent {
|
||||
|
||||
return (
|
||||
<span className='inline-account'>
|
||||
<Avatar size={13} account={account} /> <strong>{account.get('username')}</strong>
|
||||
<Avatar size={13} account={account} /> <strong>{account.get("username")}</strong>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { cloneElement, Component } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { cloneElement, Component } from "react";
|
||||
|
||||
import getRectFromEntry from '../features/ui/util/get_rect_from_entry';
|
||||
import scheduleIdleTask from '../features/ui/util/schedule_idle_task';
|
||||
import getRectFromEntry from "../features/ui/util/get_rect_from_entry";
|
||||
import scheduleIdleTask from "../features/ui/util/schedule_idle_task";
|
||||
// Diff these props in the "unrendered" state
|
||||
const updateOnPropsForUnrendered = ['id', 'index', 'listLength', 'cachedHeight'];
|
||||
const updateOnPropsForUnrendered = ["id", "index", "listLength", "cachedHeight"];
|
||||
|
||||
export default class IntersectionObserverArticle extends Component {
|
||||
|
||||
@@ -111,7 +111,7 @@ export default class IntersectionObserverArticle extends Component {
|
||||
if (!isIntersecting && (isHidden || cachedHeight)) {
|
||||
style.height = `${this.height || cachedHeight || 150}px`;
|
||||
style.opacity = 0;
|
||||
style.overflow = 'hidden';
|
||||
style.overflow = "hidden";
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
// ~ 😘 kibi!
|
||||
|
||||
// Package imports.
|
||||
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";
|
||||
|
||||
// Utils.
|
||||
import { assignHandlers } from 'flavours/glitch/utils/react_helpers';
|
||||
import { assignHandlers } from "flavours/glitch/utils/react_helpers";
|
||||
// Handlers.
|
||||
const handlers = {
|
||||
|
||||
@@ -45,7 +45,7 @@ export default class Link extends PureComponent {
|
||||
title,
|
||||
...rest
|
||||
} = this.props;
|
||||
const computedClass = classNames('link', className, `role-${role}`);
|
||||
const computedClass = classNames("link", className, `role-${role}`);
|
||||
|
||||
// We assume that our `onClick` is a routing function and give it
|
||||
// the qualities of a link even if no `href` is provided. However,
|
||||
@@ -57,10 +57,10 @@ export default class Link extends PureComponent {
|
||||
conditionalProps.onClick = click;
|
||||
} else if (onClick) {
|
||||
conditionalProps.onClick = click;
|
||||
conditionalProps.role = 'link';
|
||||
conditionalProps.role = "link";
|
||||
conditionalProps.tabIndex = 0;
|
||||
} else {
|
||||
conditionalProps.role = 'presentation';
|
||||
conditionalProps.role = "presentation";
|
||||
}
|
||||
|
||||
// If we were provided a `role` it overwrites any that we may have
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useCallback } from "react";
|
||||
|
||||
import { useIntl, defineMessages } from 'react-intl';
|
||||
import { useIntl, defineMessages } from "react-intl";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
const messages = defineMessages({
|
||||
load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
|
||||
load_more: { id: "status.load_more", defaultMessage: "Load more" },
|
||||
});
|
||||
|
||||
interface Props {
|
||||
disabled: boolean;
|
||||
maxId: string;
|
||||
onClick: (maxId: string) => void;
|
||||
disabled: boolean,
|
||||
maxId: string,
|
||||
onClick: (maxId: string) => void,
|
||||
}
|
||||
|
||||
export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
interface Props {
|
||||
onClick: (event: React.MouseEvent) => void;
|
||||
disabled?: boolean;
|
||||
visible?: boolean;
|
||||
onClick: (event: React.MouseEvent) => void,
|
||||
disabled?: boolean,
|
||||
visible?: boolean,
|
||||
}
|
||||
export const LoadMore: React.FC<Props> = ({
|
||||
onClick,
|
||||
@@ -15,7 +15,7 @@ export const LoadMore: React.FC<Props> = ({
|
||||
type='button'
|
||||
className='load-more'
|
||||
disabled={disabled || !visible}
|
||||
style={{ visibility: visible ? 'visible' : 'hidden' }}
|
||||
style={{ visibility: visible ? "visible" : "hidden" }}
|
||||
onClick={onClick}
|
||||
>
|
||||
<FormattedMessage id='status.load_more' defaultMessage='Load more' />
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
interface Props {
|
||||
onClick: (event: React.MouseEvent) => void;
|
||||
count: number;
|
||||
onClick: (event: React.MouseEvent) => void,
|
||||
count: number,
|
||||
}
|
||||
|
||||
export const LoadPending: React.FC<Props> = ({ onClick, count }) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CircularProgress } from './circular_progress';
|
||||
import { CircularProgress } from "./circular_progress";
|
||||
|
||||
export const LoadingIndicator: React.FC = () => (
|
||||
<div className='loading-indicator'>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
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 noop from 'lodash/noop';
|
||||
import noop from "lodash/noop";
|
||||
|
||||
import Bundle from 'flavours/glitch/features/ui/components/bundle';
|
||||
import { MediaGallery, Video, Audio } from 'flavours/glitch/features/ui/util/async-components';
|
||||
import Bundle from "flavours/glitch/features/ui/components/bundle";
|
||||
import { MediaGallery, Video, Audio } from "flavours/glitch/features/ui/util/async-components";
|
||||
|
||||
export default class MediaAttachments extends ImmutablePureComponent {
|
||||
|
||||
@@ -24,7 +24,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
updateOnProps = [
|
||||
'status',
|
||||
"status",
|
||||
];
|
||||
|
||||
renderLoadingMediaGallery = () => {
|
||||
@@ -53,53 +53,53 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
||||
|
||||
render () {
|
||||
const { status, width, height, revealed } = this.props;
|
||||
const mediaAttachments = status.get('media_attachments');
|
||||
const language = status.getIn(['language', 'translation']) || status.get('language') || this.props.lang;
|
||||
const mediaAttachments = status.get("media_attachments");
|
||||
const language = status.getIn(["language", "translation"]) || status.get("language") || this.props.lang;
|
||||
|
||||
if (mediaAttachments.size === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (mediaAttachments.getIn([0, 'type']) === 'audio') {
|
||||
if (mediaAttachments.getIn([0, "type"]) === "audio") {
|
||||
const audio = mediaAttachments.get(0);
|
||||
const description = audio.getIn(['translation', 'description']) || audio.get('description');
|
||||
const description = audio.getIn(["translation", "description"]) || audio.get("description");
|
||||
|
||||
return (
|
||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||
{Component => (
|
||||
<Component
|
||||
src={audio.get('url')}
|
||||
src={audio.get("url")}
|
||||
alt={description}
|
||||
lang={language}
|
||||
width={width}
|
||||
height={height}
|
||||
poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])}
|
||||
backgroundColor={audio.getIn(['meta', 'colors', 'background'])}
|
||||
foregroundColor={audio.getIn(['meta', 'colors', 'foreground'])}
|
||||
accentColor={audio.getIn(['meta', 'colors', 'accent'])}
|
||||
duration={audio.getIn(['meta', 'original', 'duration'], 0)}
|
||||
poster={audio.get("preview_url") || status.getIn(["account", "avatar_static"])}
|
||||
backgroundColor={audio.getIn(["meta", "colors", "background"])}
|
||||
foregroundColor={audio.getIn(["meta", "colors", "foreground"])}
|
||||
accentColor={audio.getIn(["meta", "colors", "accent"])}
|
||||
duration={audio.getIn(["meta", "original", "duration"], 0)}
|
||||
/>
|
||||
)}
|
||||
</Bundle>
|
||||
);
|
||||
} else if (mediaAttachments.getIn([0, 'type']) === 'video') {
|
||||
} else if (mediaAttachments.getIn([0, "type"]) === "video") {
|
||||
const video = mediaAttachments.get(0);
|
||||
const description = video.getIn(['translation', 'description']) || video.get('description');
|
||||
const description = video.getIn(["translation", "description"]) || video.get("description");
|
||||
|
||||
return (
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
{Component => (
|
||||
<Component
|
||||
preview={video.get('preview_url')}
|
||||
frameRate={video.getIn(['meta', 'original', 'frame_rate'])}
|
||||
blurhash={video.get('blurhash')}
|
||||
src={video.get('url')}
|
||||
preview={video.get("preview_url")}
|
||||
frameRate={video.getIn(["meta", "original", "frame_rate"])}
|
||||
blurhash={video.get("blurhash")}
|
||||
src={video.get("url")}
|
||||
alt={description}
|
||||
lang={language}
|
||||
width={width}
|
||||
height={height}
|
||||
inline
|
||||
sensitive={status.get('sensitive')}
|
||||
sensitive={status.get("sensitive")}
|
||||
revealed={revealed}
|
||||
onOpenVideo={noop}
|
||||
/>
|
||||
@@ -113,7 +113,7 @@ export default class MediaAttachments extends ImmutablePureComponent {
|
||||
<Component
|
||||
media={mediaAttachments}
|
||||
lang={language}
|
||||
sensitive={status.get('sensitive')}
|
||||
sensitive={status.get("sensitive")}
|
||||
defaultWidth={width}
|
||||
revealed={revealed}
|
||||
height={height}
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
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 classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { is } from 'immutable';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { is } from "immutable";
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
import { debounce } from "lodash";
|
||||
|
||||
import { Blurhash } from 'flavours/glitch/components/blurhash';
|
||||
import { autoPlayGif, displayMedia } from 'flavours/glitch/initial_state';
|
||||
import { Blurhash } from "flavours/glitch/components/blurhash";
|
||||
import { autoPlayGif, displayMedia } from "flavours/glitch/initial_state";
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { IconButton } from "./icon_button";
|
||||
|
||||
const messages = defineMessages({
|
||||
hidden: {
|
||||
defaultMessage: 'Media hidden',
|
||||
id: 'status.media_hidden',
|
||||
defaultMessage: "Media hidden",
|
||||
id: "status.media_hidden",
|
||||
},
|
||||
sensitive: {
|
||||
defaultMessage: 'Sensitive',
|
||||
id: 'media_gallery.sensitive',
|
||||
defaultMessage: "Sensitive",
|
||||
id: "media_gallery.sensitive",
|
||||
},
|
||||
toggle: {
|
||||
defaultMessage: 'Click to view',
|
||||
id: 'status.sensitive_toggle',
|
||||
defaultMessage: "Click to view",
|
||||
id: "status.sensitive_toggle",
|
||||
},
|
||||
toggle_visible: {
|
||||
defaultMessage: '{number, plural, one {Hide image} other {Hide images}}',
|
||||
id: 'media_gallery.toggle_visible',
|
||||
defaultMessage: "{number, plural, one {Hide image} other {Hide images}}",
|
||||
id: "media_gallery.toggle_visible",
|
||||
},
|
||||
warning: {
|
||||
defaultMessage: 'Sensitive content',
|
||||
id: 'status.sensitive_warning',
|
||||
defaultMessage: "Sensitive content",
|
||||
id: "status.sensitive_warning",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ class Item extends PureComponent {
|
||||
|
||||
hoverToPlay () {
|
||||
const { attachment } = this.props;
|
||||
return !this.getAutoPlay() && attachment.get('type') === 'gifv';
|
||||
return !this.getAutoPlay() && attachment.get("type") === "gifv";
|
||||
}
|
||||
|
||||
handleClick = (e) => {
|
||||
@@ -117,7 +117,7 @@ class Item extends PureComponent {
|
||||
displayWidth,
|
||||
visible,
|
||||
useBlurhash,
|
||||
contentStyles
|
||||
contentStyles,
|
||||
} = this.props;
|
||||
|
||||
let badges = [], thumbnail;
|
||||
@@ -133,51 +133,51 @@ class Item extends PureComponent {
|
||||
height = 50;
|
||||
}
|
||||
|
||||
if (attachment.get('description')?.length > 0) {
|
||||
if (attachment.get("description")?.length > 0) {
|
||||
badges.push(<span key='alt' className='media-gallery__gifv__label'>ALT</span>);
|
||||
}
|
||||
|
||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||
const description = attachment.getIn(["translation", "description"]) || attachment.get("description");
|
||||
|
||||
if (attachment.get('type') === 'unknown') {
|
||||
if (attachment.get("type") === "unknown") {
|
||||
return (
|
||||
<div className={classNames('media-gallery__item', { standalone, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100 })} key={attachment.get('id')}>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener noreferrer'>
|
||||
<div className={classNames("media-gallery__item", { standalone, "media-gallery__item--tall": height === 100, "media-gallery__item--wide": width === 100 })} key={attachment.get("id")}>
|
||||
<a className='media-gallery__item-thumbnail' href={attachment.get("remote_url") || attachment.get("url")} style={{ cursor: "pointer" }} title={description} lang={lang} target='_blank' rel='noopener noreferrer'>
|
||||
<Blurhash
|
||||
hash={attachment.get('blurhash')}
|
||||
hash={attachment.get("blurhash")}
|
||||
className='media-gallery__preview'
|
||||
dummy={!useBlurhash}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
} else if (attachment.get('type') === 'image') {
|
||||
const previewUrl = attachment.get('preview_url');
|
||||
const previewWidth = attachment.getIn(['meta', 'small', 'width']);
|
||||
} else if (attachment.get("type") === "image") {
|
||||
const previewUrl = attachment.get("preview_url");
|
||||
const previewWidth = attachment.getIn(["meta", "small", "width"]);
|
||||
|
||||
const originalUrl = attachment.get('url');
|
||||
const originalWidth = attachment.getIn(['meta', 'original', 'width']);
|
||||
const originalUrl = attachment.get("url");
|
||||
const originalWidth = attachment.getIn(["meta", "original", "width"]);
|
||||
|
||||
const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
|
||||
const hasSize = typeof originalWidth === "number" && typeof previewWidth === "number";
|
||||
|
||||
const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
|
||||
const sizes = hasSize && (displayWidth > 0) ? `${displayWidth * (width / 100)}px` : null;
|
||||
|
||||
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
|
||||
const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
|
||||
const focusX = attachment.getIn(["meta", "focus", "x"]) || 0;
|
||||
const focusY = attachment.getIn(["meta", "focus", "y"]) || 0;
|
||||
const x = ((focusX / 2) + .5) * 100;
|
||||
const y = ((focusY / -2) + .5) * 100;
|
||||
|
||||
thumbnail = (
|
||||
<a
|
||||
className='media-gallery__item-thumbnail'
|
||||
href={attachment.get('remote_url') || originalUrl}
|
||||
href={attachment.get("remote_url") || originalUrl}
|
||||
onClick={this.handleClick}
|
||||
target='_blank'
|
||||
rel='noopener noreferrer'
|
||||
>
|
||||
<img
|
||||
className={letterbox ? 'letterbox' : null}
|
||||
className={letterbox ? "letterbox" : null}
|
||||
src={previewUrl}
|
||||
srcSet={srcSet}
|
||||
sizes={sizes}
|
||||
@@ -189,20 +189,20 @@ class Item extends PureComponent {
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
} else if (attachment.get('type') === 'gifv') {
|
||||
} else if (attachment.get("type") === "gifv") {
|
||||
const autoPlay = this.getAutoPlay();
|
||||
|
||||
badges.push(<span key='gif' className='media-gallery__gifv__label'>GIF</span>);
|
||||
|
||||
thumbnail = (
|
||||
<div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
|
||||
<div className={classNames("media-gallery__gifv", { autoplay: autoPlay })}>
|
||||
<video
|
||||
className={`media-gallery__item-gifv-thumbnail${letterbox ? ' letterbox' : ''}`}
|
||||
className={`media-gallery__item-gifv-thumbnail${letterbox ? " letterbox" : ""}`}
|
||||
aria-label={description}
|
||||
title={description}
|
||||
lang={lang}
|
||||
role='application'
|
||||
src={attachment.get('url')}
|
||||
src={attachment.get("url")}
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
@@ -216,12 +216,12 @@ class Item extends PureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames('media-gallery__item', { standalone, letterbox, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100 })} key={attachment.get('id')} style={contentStyles}>
|
||||
<div className={classNames("media-gallery__item", { standalone, letterbox, "media-gallery__item--tall": height === 100, "media-gallery__item--wide": width === 100 })} key={attachment.get("id")} style={contentStyles}>
|
||||
<Blurhash
|
||||
hash={attachment.get('blurhash')}
|
||||
hash={attachment.get("blurhash")}
|
||||
dummy={!useBlurhash}
|
||||
className={classNames('media-gallery__preview', {
|
||||
'media-gallery__preview--hidden': visible && this.state.loaded,
|
||||
className={classNames("media-gallery__preview", {
|
||||
"media-gallery__preview--hidden": visible && this.state.loaded,
|
||||
})}
|
||||
/>
|
||||
|
||||
@@ -264,21 +264,21 @@ class MediaGallery extends PureComponent {
|
||||
};
|
||||
|
||||
state = {
|
||||
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'),
|
||||
visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== "hide_all" && !this.props.sensitive || displayMedia === "show_all"),
|
||||
width: this.props.defaultWidth,
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('resize', this.handleResize, { passive: true });
|
||||
window.addEventListener("resize", this.handleResize, { passive: true });
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
window.removeEventListener("resize", this.handleResize);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillReceiveProps (nextProps) {
|
||||
if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
|
||||
this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
|
||||
this.setState({ visible: displayMedia !== "hide_all" && !nextProps.sensitive || displayMedia === "show_all" });
|
||||
} else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) {
|
||||
this.setState({ visible: nextProps.visible });
|
||||
}
|
||||
@@ -336,14 +336,14 @@ class MediaGallery extends PureComponent {
|
||||
|
||||
isStandaloneEligible() {
|
||||
const { media, standalone } = this.props;
|
||||
return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
|
||||
return standalone && media.size === 1 && media.getIn([0, "meta", "small", "aspect"]);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { media, lang, intl, sensitive, letterbox, fullwidth, defaultWidth, autoplay, useBlurhash } = this.props;
|
||||
const { visible } = this.state;
|
||||
const size = media.take(4).size;
|
||||
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
|
||||
const uncached = media.every(attachment => attachment.get("type") === "unknown");
|
||||
|
||||
const width = this.state.width || defaultWidth;
|
||||
|
||||
@@ -351,10 +351,10 @@ class MediaGallery extends PureComponent {
|
||||
|
||||
const style = {};
|
||||
|
||||
const computedClass = classNames('media-gallery', { 'full-width': fullwidth });
|
||||
const computedClass = classNames("media-gallery", { "full-width": fullwidth });
|
||||
|
||||
if (this.isStandaloneEligible()) { // TODO: cropImages setting
|
||||
style.aspectRatio = `${this.props.media.getIn([0, 'meta', 'small', 'aspect'])}`;
|
||||
style.aspectRatio = `${this.props.media.getIn([0, "meta", "small", "aspect"])}`;
|
||||
}
|
||||
|
||||
if (this.isStandaloneEligible()) {
|
||||
@@ -373,7 +373,7 @@ class MediaGallery extends PureComponent {
|
||||
} else {
|
||||
children = media.map((attachment, i) => (
|
||||
<Item
|
||||
key={attachment.get('id')}
|
||||
key={attachment.get("id")}
|
||||
autoplay={autoplay}
|
||||
onClick={this.handleClick}
|
||||
attachment={attachment}
|
||||
@@ -384,7 +384,7 @@ class MediaGallery extends PureComponent {
|
||||
displayWidth={width}
|
||||
visible={visible || uncached}
|
||||
useBlurhash={useBlurhash}
|
||||
contentStyles={{ aspectRatio: '16 / 9' }}
|
||||
contentStyles={{ aspectRatio: "16 / 9" }}
|
||||
/>
|
||||
));
|
||||
}
|
||||
@@ -413,7 +413,7 @@ class MediaGallery extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className={computedClass} style={style} ref={this.handleRef}>
|
||||
<div className={classNames('spoiler-button', { 'spoiler-button--minified': visible && !uncached, 'spoiler-button--click-thru': uncached })}>
|
||||
<div className={classNames("spoiler-button", { "spoiler-button--minified": visible && !uncached, "spoiler-button--click-thru": uncached })}>
|
||||
{spoilerButton}
|
||||
{visible && sensitive && (
|
||||
<span className='sensitive-marker'>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import 'wicg-inert';
|
||||
import { multiply } from 'color-blend';
|
||||
import { createBrowserHistory } from 'history';
|
||||
import "wicg-inert";
|
||||
import { multiply } from "color-blend";
|
||||
import { createBrowserHistory } from "history";
|
||||
|
||||
export default class ModalRoot extends PureComponent {
|
||||
|
||||
@@ -26,15 +26,15 @@ export default class ModalRoot extends PureComponent {
|
||||
activeElement = this.props.children ? document.activeElement : null;
|
||||
|
||||
handleKeyUp = (e) => {
|
||||
if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
|
||||
if ((e.key === "Escape" || e.key === "Esc" || e.keyCode === 27)
|
||||
&& !!this.props.children && !this.props.noEsc) {
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
handleKeyDown = (e) => {
|
||||
if (e.key === 'Tab') {
|
||||
const focusable = Array.from(this.node.querySelectorAll('button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])')).filter((x) => window.getComputedStyle(x).display !== 'none');
|
||||
if (e.key === "Tab") {
|
||||
const focusable = Array.from(this.node.querySelectorAll("button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])")).filter((x) => window.getComputedStyle(x).display !== "none");
|
||||
const index = focusable.indexOf(e.target);
|
||||
|
||||
let element;
|
||||
@@ -54,8 +54,8 @@ export default class ModalRoot extends PureComponent {
|
||||
};
|
||||
|
||||
componentDidMount () {
|
||||
window.addEventListener('keyup', this.handleKeyUp, false);
|
||||
window.addEventListener('keydown', this.handleKeyDown, false);
|
||||
window.addEventListener("keyup", this.handleKeyUp, false);
|
||||
window.addEventListener("keydown", this.handleKeyDown, false);
|
||||
this.history = this.context.router ? this.context.router.history : createBrowserHistory();
|
||||
|
||||
if (this.props.children) {
|
||||
@@ -67,13 +67,13 @@ export default class ModalRoot extends PureComponent {
|
||||
if (!!nextProps.children && !this.props.children) {
|
||||
this.activeElement = document.activeElement;
|
||||
|
||||
this.getSiblings().forEach(sibling => sibling.setAttribute('inert', true));
|
||||
this.getSiblings().forEach(sibling => sibling.setAttribute("inert", true));
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (!this.props.children && !!prevProps.children) {
|
||||
this.getSiblings().forEach(sibling => sibling.removeAttribute('inert'));
|
||||
this.getSiblings().forEach(sibling => sibling.removeAttribute("inert"));
|
||||
|
||||
// Because of the wicg-inert polyfill, the activeElement may not be
|
||||
// immediately selectable, we have to wait for observers to run, as
|
||||
@@ -96,14 +96,14 @@ export default class ModalRoot extends PureComponent {
|
||||
}
|
||||
|
||||
componentWillUnmount () {
|
||||
window.removeEventListener('keyup', this.handleKeyUp);
|
||||
window.removeEventListener('keydown', this.handleKeyDown);
|
||||
window.removeEventListener("keyup", this.handleKeyUp);
|
||||
window.removeEventListener("keydown", this.handleKeyDown);
|
||||
}
|
||||
|
||||
_handleModalOpen () {
|
||||
this._modalHistoryKey = Date.now();
|
||||
this.unlistenHistory = this.history.listen((_, action) => {
|
||||
if (action === 'POP') {
|
||||
if (action === "POP") {
|
||||
this.props.onClose();
|
||||
}
|
||||
});
|
||||
@@ -151,7 +151,7 @@ export default class ModalRoot extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className='modal-root' ref={this.setRef}>
|
||||
<div style={{ pointerEvents: visible ? 'auto' : 'none' }}>
|
||||
<div style={{ pointerEvents: visible ? "auto" : "none" }}>
|
||||
<div role='presentation' className='modal-root__overlay' onClick={onClose} style={{ backgroundColor: backgroundColor ? `rgba(${backgroundColor.r}, ${backgroundColor.g}, ${backgroundColor.b}, 0.7)` : null }} />
|
||||
<div role='dialog' className='modal-root__container'>{children}</div>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { PureComponent } from 'react';
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { Switch, Route, withRouter } from 'react-router-dom';
|
||||
import { Switch, Route, withRouter } from "react-router-dom";
|
||||
|
||||
import AccountNavigation from 'flavours/glitch/features/account/navigation';
|
||||
import Trends from 'flavours/glitch/features/getting_started/containers/trends_container';
|
||||
import { showTrends } from 'flavours/glitch/initial_state';
|
||||
import AccountNavigation from "flavours/glitch/features/account/navigation";
|
||||
import Trends from "flavours/glitch/features/getting_started/containers/trends_container";
|
||||
import { showTrends } from "flavours/glitch/initial_state";
|
||||
|
||||
const DefaultNavigation = () => (
|
||||
showTrends ? (
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
export const NotSignedInIndicator: React.FC = () => (
|
||||
<div className='scrollable scrollable--flex'>
|
||||
|
||||
@@ -6,21 +6,21 @@
|
||||
|
||||
|
||||
// Package imports //
|
||||
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 ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
const messages = defineMessages({
|
||||
btnAll : { id: 'notification_purge.btn_all', defaultMessage: 'Select\nall' },
|
||||
btnNone : { id: 'notification_purge.btn_none', defaultMessage: 'Select\nnone' },
|
||||
btnInvert : { id: 'notification_purge.btn_invert', defaultMessage: 'Invert\nselection' },
|
||||
btnApply : { id: 'notification_purge.btn_apply', defaultMessage: 'Clear\nselected' },
|
||||
btnAll : { id: "notification_purge.btn_all", defaultMessage: "Select\nall" },
|
||||
btnNone : { id: "notification_purge.btn_none", defaultMessage: "Select\nnone" },
|
||||
btnInvert : { id: "notification_purge.btn_invert", defaultMessage: "Invert\nselection" },
|
||||
btnApply : { id: "notification_purge.btn_apply", defaultMessage: "Clear\nselected" },
|
||||
});
|
||||
|
||||
class NotificationPurgeButtons extends ImmutablePureComponent {
|
||||
@@ -40,11 +40,11 @@ class NotificationPurgeButtons extends ImmutablePureComponent {
|
||||
//className='active'
|
||||
return (
|
||||
<div className='column-header__notif-cleaning-buttons'>
|
||||
<button onClick={this.props.onMarkAll} className={classNames('column-header__button', { active: markNewForDelete })}>
|
||||
<button onClick={this.props.onMarkAll} className={classNames("column-header__button", { active: markNewForDelete })}>
|
||||
<b>∀</b><br />{intl.formatMessage(messages.btnAll)}
|
||||
</button>
|
||||
|
||||
<button onClick={this.props.onMarkNone} className={classNames('column-header__button', { active: !markNewForDelete })}>
|
||||
<button onClick={this.props.onMarkNone} className={classNames("column-header__button", { active: !markNewForDelete })}>
|
||||
<b>∅</b><br />{intl.formatMessage(messages.btnNone)}
|
||||
</button>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
export default class Permalink extends PureComponent {
|
||||
|
||||
@@ -40,7 +40,7 @@ export default class Permalink extends PureComponent {
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? ' ' + className : ''}`}>
|
||||
<a target='_blank' href={href} onClick={this.handleClick} {...other} className={`permalink${className ? " " + className : ""}`}>
|
||||
{children}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
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 { connect } from 'react-redux';
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { removePictureInPicture } from 'flavours/glitch/actions/picture_in_picture';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { removePictureInPicture } from "flavours/glitch/actions/picture_in_picture";
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
class PictureInPicturePlaceholder extends PureComponent {
|
||||
|
||||
|
||||
@@ -1,39 +1,39 @@
|
||||
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 ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import escapeTextContentForBrowser from 'escape-html';
|
||||
import spring from 'react-motion/lib/spring';
|
||||
import escapeTextContentForBrowser from "escape-html";
|
||||
import spring from "react-motion/lib/spring";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import emojify from 'flavours/glitch/features/emoji/emoji';
|
||||
import Motion from 'flavours/glitch/features/ui/util/optional_motion';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
import emojify from "flavours/glitch/features/emoji/emoji";
|
||||
import Motion from "flavours/glitch/features/ui/util/optional_motion";
|
||||
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
import { RelativeTimestamp } from "./relative_timestamp";
|
||||
|
||||
|
||||
const messages = defineMessages({
|
||||
closed: {
|
||||
id: 'poll.closed',
|
||||
defaultMessage: 'Closed',
|
||||
id: "poll.closed",
|
||||
defaultMessage: "Closed",
|
||||
},
|
||||
voted: {
|
||||
id: 'poll.voted',
|
||||
defaultMessage: 'You voted for this answer',
|
||||
id: "poll.voted",
|
||||
defaultMessage: "You voted for this answer",
|
||||
},
|
||||
votes: {
|
||||
id: 'poll.votes',
|
||||
defaultMessage: '{votes, plural, one {# vote} other {# votes}}',
|
||||
id: "poll.votes",
|
||||
defaultMessage: "{votes, plural, one {# vote} other {# votes}}",
|
||||
},
|
||||
});
|
||||
|
||||
const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
|
||||
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
|
||||
const makeEmojiMap = record => record.get("emojis").reduce((obj, emoji) => {
|
||||
obj[`:${emoji.get("shortcode")}:`] = emoji.toJS();
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
@@ -59,8 +59,8 @@ class Poll extends ImmutablePureComponent {
|
||||
|
||||
static getDerivedStateFromProps (props, state) {
|
||||
const { poll } = props;
|
||||
const expires_at = poll.get('expires_at');
|
||||
const expired = poll.get('expired') || expires_at !== null && (new Date(expires_at)).getTime() < Date.now();
|
||||
const expires_at = poll.get("expires_at");
|
||||
const expired = poll.get("expired") || expires_at !== null && (new Date(expires_at)).getTime() < Date.now();
|
||||
return (expired === state.expired) ? null : { expired };
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ class Poll extends ImmutablePureComponent {
|
||||
const { poll } = this.props;
|
||||
clearTimeout(this._timer);
|
||||
if (!this.state.expired) {
|
||||
const delay = (new Date(poll.get('expires_at'))).getTime() - Date.now();
|
||||
const delay = (new Date(poll.get("expires_at"))).getTime() - Date.now();
|
||||
this._timer = setTimeout(() => {
|
||||
this.setState({ expired: true });
|
||||
}, delay);
|
||||
@@ -88,7 +88,7 @@ class Poll extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
_toggleOption = value => {
|
||||
if (this.props.poll.get('multiple')) {
|
||||
if (this.props.poll.get("multiple")) {
|
||||
const tmp = { ...this.state.selected };
|
||||
if (tmp[value]) {
|
||||
delete tmp[value];
|
||||
@@ -108,8 +108,8 @@ class Poll extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
handleOptionKeyPress = (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
this._toggleOption(e.target.getAttribute('data-index'));
|
||||
if (e.key === "Enter" || e.key === " ") {
|
||||
this._toggleOption(e.target.getAttribute("data-index"));
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
}
|
||||
@@ -137,14 +137,14 @@ class Poll extends ImmutablePureComponent {
|
||||
|
||||
renderOption (option, optionIndex, showResults) {
|
||||
const { poll, lang, disabled, intl } = this.props;
|
||||
const pollVotesCount = poll.get('voters_count') || poll.get('votes_count');
|
||||
const percent = pollVotesCount === 0 ? 0 : (option.get('votes_count') / pollVotesCount) * 100;
|
||||
const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') >= other.get('votes_count'));
|
||||
const pollVotesCount = poll.get("voters_count") || poll.get("votes_count");
|
||||
const percent = pollVotesCount === 0 ? 0 : (option.get("votes_count") / pollVotesCount) * 100;
|
||||
const leading = poll.get("options").filterNot(other => other.get("title") === option.get("title")).every(other => option.get("votes_count") >= other.get("votes_count"));
|
||||
const active = !!this.state.selected[`${optionIndex}`];
|
||||
const voted = option.get('voted') || (poll.get('own_votes') && poll.get('own_votes').includes(optionIndex));
|
||||
const voted = option.get("voted") || (poll.get("own_votes") && poll.get("own_votes").includes(optionIndex));
|
||||
|
||||
const title = option.getIn(['translation', 'title']) || option.get('title');
|
||||
let titleHtml = option.getIn(['translation', 'titleHtml']) || option.get('titleHtml');
|
||||
const title = option.getIn(["translation", "title"]) || option.get("title");
|
||||
let titleHtml = option.getIn(["translation", "titleHtml"]) || option.get("titleHtml");
|
||||
|
||||
if (!titleHtml) {
|
||||
const emojiMap = makeEmojiMap(poll);
|
||||
@@ -152,11 +152,11 @@ class Poll extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={option.get('title')}>
|
||||
<label className={classNames('poll__option', { selectable: !showResults })}>
|
||||
<li key={option.get("title")}>
|
||||
<label className={classNames("poll__option", { selectable: !showResults })}>
|
||||
<input
|
||||
name='vote-options'
|
||||
type={poll.get('multiple') ? 'checkbox' : 'radio'}
|
||||
type={poll.get("multiple") ? "checkbox" : "radio"}
|
||||
value={optionIndex}
|
||||
checked={active}
|
||||
onChange={this.handleOptionChange}
|
||||
@@ -165,9 +165,9 @@ class Poll extends ImmutablePureComponent {
|
||||
|
||||
{!showResults && (
|
||||
<span
|
||||
className={classNames('poll__input', { checkbox: poll.get('multiple'), active })}
|
||||
className={classNames("poll__input", { checkbox: poll.get("multiple"), active })}
|
||||
tabIndex={0}
|
||||
role={poll.get('multiple') ? 'checkbox' : 'radio'}
|
||||
role={poll.get("multiple") ? "checkbox" : "radio"}
|
||||
onKeyPress={this.handleOptionKeyPress}
|
||||
aria-checked={active}
|
||||
aria-label={title}
|
||||
@@ -179,7 +179,7 @@ class Poll extends ImmutablePureComponent {
|
||||
<span
|
||||
className='poll__number'
|
||||
title={intl.formatMessage(messages.votes, {
|
||||
votes: option.get('votes_count'),
|
||||
votes: option.get("votes_count"),
|
||||
})}
|
||||
>
|
||||
{Math.round(percent)}%
|
||||
@@ -200,7 +200,7 @@ class Poll extends ImmutablePureComponent {
|
||||
{showResults && (
|
||||
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
|
||||
{({ width }) =>
|
||||
<span className={classNames('poll__chart', { leading })} style={{ width: `${width}%` }} />
|
||||
<span className={classNames("poll__chart", { leading })} style={{ width: `${width}%` }} />
|
||||
}
|
||||
</Motion>
|
||||
)}
|
||||
@@ -216,22 +216,22 @@ class Poll extends ImmutablePureComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
const timeRemaining = expired ? intl.formatMessage(messages.closed) : <RelativeTimestamp timestamp={poll.get('expires_at')} futureDate />;
|
||||
const showResults = poll.get('voted') || revealed || expired;
|
||||
const timeRemaining = expired ? intl.formatMessage(messages.closed) : <RelativeTimestamp timestamp={poll.get("expires_at")} futureDate />;
|
||||
const showResults = poll.get("voted") || revealed || expired;
|
||||
const disabled = this.props.disabled || Object.entries(this.state.selected).every(item => !item);
|
||||
|
||||
let votesCount = null;
|
||||
|
||||
if (poll.get('voters_count') !== null && poll.get('voters_count') !== undefined) {
|
||||
votesCount = <FormattedMessage id='poll.total_people' defaultMessage='{count, plural, one {# person} other {# people}}' values={{ count: poll.get('voters_count') }} />;
|
||||
if (poll.get("voters_count") !== null && poll.get("voters_count") !== undefined) {
|
||||
votesCount = <FormattedMessage id='poll.total_people' defaultMessage='{count, plural, one {# person} other {# people}}' values={{ count: poll.get("voters_count") }} />;
|
||||
} else {
|
||||
votesCount = <FormattedMessage id='poll.total_votes' defaultMessage='{count, plural, one {# vote} other {# votes}}' values={{ count: poll.get('votes_count') }} />;
|
||||
votesCount = <FormattedMessage id='poll.total_votes' defaultMessage='{count, plural, one {# vote} other {# votes}}' values={{ count: poll.get("votes_count") }} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='poll'>
|
||||
<ul>
|
||||
{poll.get('options').map((option, i) => this.renderOption(option, i, showResults))}
|
||||
{poll.get("options").map((option, i) => this.renderOption(option, i, showResults))}
|
||||
</ul>
|
||||
|
||||
<div className='poll__footer'>
|
||||
@@ -239,7 +239,7 @@ class Poll extends ImmutablePureComponent {
|
||||
{!showResults && <><button className='poll__link' onClick={this.handleReveal}><FormattedMessage id='poll.reveal' defaultMessage='See results' /></button> · </>}
|
||||
{showResults && !this.props.disabled && <><button className='poll__link' onClick={this.handleRefresh}><FormattedMessage id='poll.refresh' defaultMessage='Refresh' /></button> · </>}
|
||||
{votesCount}
|
||||
{poll.get('expires_at') && <> · {timeRemaining}</>}
|
||||
{poll.get("expires_at") && <> · {timeRemaining}</>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
interface Props {
|
||||
value: string;
|
||||
checked: boolean;
|
||||
name: string;
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
label: React.ReactNode;
|
||||
value: string,
|
||||
checked: boolean,
|
||||
name: string,
|
||||
onChange: (event: React.ChangeEvent<HTMLInputElement>) => void,
|
||||
label: React.ReactNode,
|
||||
}
|
||||
|
||||
export const RadioButton: React.FC<Props> = ({
|
||||
@@ -27,7 +27,7 @@ export const RadioButton: React.FC<Props> = ({
|
||||
onChange={onChange}
|
||||
/>
|
||||
|
||||
<span className={classNames('radio-button__input', { checked })} />
|
||||
<span className={classNames("radio-button__input", { checked })} />
|
||||
|
||||
<span>{label}</span>
|
||||
</label>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import illustration from 'flavours/glitch/images/elephant_ui_working.svg';
|
||||
import illustration from "flavours/glitch/images/elephant_ui_working.svg";
|
||||
|
||||
const RegenerationIndicator = () => (
|
||||
<div className='regeneration-indicator'>
|
||||
|
||||
@@ -1,69 +1,69 @@
|
||||
import { Component } from 'react';
|
||||
import { Component } from "react";
|
||||
|
||||
import type { IntlShape } from 'react-intl';
|
||||
import { injectIntl, defineMessages } from 'react-intl';
|
||||
import { type IntlShape } from "react-intl";
|
||||
import { injectIntl, defineMessages } from "react-intl";
|
||||
|
||||
const messages = defineMessages({
|
||||
today: { id: 'relative_time.today', defaultMessage: 'today' },
|
||||
just_now: { id: 'relative_time.just_now', defaultMessage: 'now' },
|
||||
today: { id: "relative_time.today", defaultMessage: "today" },
|
||||
just_now: { id: "relative_time.just_now", defaultMessage: "now" },
|
||||
just_now_full: {
|
||||
id: 'relative_time.full.just_now',
|
||||
defaultMessage: 'just now',
|
||||
id: "relative_time.full.just_now",
|
||||
defaultMessage: "just now",
|
||||
},
|
||||
seconds: { id: 'relative_time.seconds', defaultMessage: '{number}s' },
|
||||
seconds: { id: "relative_time.seconds", defaultMessage: "{number}s" },
|
||||
seconds_full: {
|
||||
id: 'relative_time.full.seconds',
|
||||
defaultMessage: '{number, plural, one {# second} other {# seconds}} ago',
|
||||
id: "relative_time.full.seconds",
|
||||
defaultMessage: "{number, plural, one {# second} other {# seconds}} ago",
|
||||
},
|
||||
minutes: { id: 'relative_time.minutes', defaultMessage: '{number}m' },
|
||||
minutes: { id: "relative_time.minutes", defaultMessage: "{number}m" },
|
||||
minutes_full: {
|
||||
id: 'relative_time.full.minutes',
|
||||
defaultMessage: '{number, plural, one {# minute} other {# minutes}} ago',
|
||||
id: "relative_time.full.minutes",
|
||||
defaultMessage: "{number, plural, one {# minute} other {# minutes}} ago",
|
||||
},
|
||||
hours: { id: 'relative_time.hours', defaultMessage: '{number}h' },
|
||||
hours: { id: "relative_time.hours", defaultMessage: "{number}h" },
|
||||
hours_full: {
|
||||
id: 'relative_time.full.hours',
|
||||
defaultMessage: '{number, plural, one {# hour} other {# hours}} ago',
|
||||
id: "relative_time.full.hours",
|
||||
defaultMessage: "{number, plural, one {# hour} other {# hours}} ago",
|
||||
},
|
||||
days: { id: 'relative_time.days', defaultMessage: '{number}d' },
|
||||
days: { id: "relative_time.days", defaultMessage: "{number}d" },
|
||||
days_full: {
|
||||
id: 'relative_time.full.days',
|
||||
defaultMessage: '{number, plural, one {# day} other {# days}} ago',
|
||||
id: "relative_time.full.days",
|
||||
defaultMessage: "{number, plural, one {# day} other {# days}} ago",
|
||||
},
|
||||
moments_remaining: {
|
||||
id: 'time_remaining.moments',
|
||||
defaultMessage: 'Moments remaining',
|
||||
id: "time_remaining.moments",
|
||||
defaultMessage: "Moments remaining",
|
||||
},
|
||||
seconds_remaining: {
|
||||
id: 'time_remaining.seconds',
|
||||
defaultMessage: '{number, plural, one {# second} other {# seconds}} left',
|
||||
id: "time_remaining.seconds",
|
||||
defaultMessage: "{number, plural, one {# second} other {# seconds}} left",
|
||||
},
|
||||
minutes_remaining: {
|
||||
id: 'time_remaining.minutes',
|
||||
defaultMessage: '{number, plural, one {# minute} other {# minutes}} left',
|
||||
id: "time_remaining.minutes",
|
||||
defaultMessage: "{number, plural, one {# minute} other {# minutes}} left",
|
||||
},
|
||||
hours_remaining: {
|
||||
id: 'time_remaining.hours',
|
||||
defaultMessage: '{number, plural, one {# hour} other {# hours}} left',
|
||||
id: "time_remaining.hours",
|
||||
defaultMessage: "{number, plural, one {# hour} other {# hours}} left",
|
||||
},
|
||||
days_remaining: {
|
||||
id: 'time_remaining.days',
|
||||
defaultMessage: '{number, plural, one {# day} other {# days}} left',
|
||||
id: "time_remaining.days",
|
||||
defaultMessage: "{number, plural, one {# day} other {# days}} left",
|
||||
},
|
||||
});
|
||||
|
||||
const dateFormatOptions = {
|
||||
hour12: false,
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
} as const;
|
||||
|
||||
const shortDateFormatOptions = {
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
} as const;
|
||||
|
||||
const SECOND = 1000;
|
||||
@@ -77,25 +77,25 @@ const selectUnits = (delta: number) => {
|
||||
const absDelta = Math.abs(delta);
|
||||
|
||||
if (absDelta < MINUTE) {
|
||||
return 'second';
|
||||
return "second";
|
||||
} else if (absDelta < HOUR) {
|
||||
return 'minute';
|
||||
return "minute";
|
||||
} else if (absDelta < DAY) {
|
||||
return 'hour';
|
||||
return "hour";
|
||||
}
|
||||
|
||||
return 'day';
|
||||
return "day";
|
||||
};
|
||||
|
||||
const getUnitDelay = (units: string) => {
|
||||
switch (units) {
|
||||
case 'second':
|
||||
case "second":
|
||||
return SECOND;
|
||||
case 'minute':
|
||||
case "minute":
|
||||
return MINUTE;
|
||||
case 'hour':
|
||||
case "hour":
|
||||
return HOUR;
|
||||
case 'day':
|
||||
case "day":
|
||||
return DAY;
|
||||
default:
|
||||
return MAX_DELAY;
|
||||
@@ -147,7 +147,7 @@ export const timeAgoString = (
|
||||
} else {
|
||||
relativeTime = intl.formatDate(date, {
|
||||
...shortDateFormatOptions,
|
||||
year: 'numeric',
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,14 +190,14 @@ const timeRemainingString = (
|
||||
};
|
||||
|
||||
interface Props {
|
||||
intl: IntlShape;
|
||||
timestamp: string;
|
||||
year: number;
|
||||
futureDate?: boolean;
|
||||
short?: boolean;
|
||||
intl: IntlShape,
|
||||
timestamp: string,
|
||||
year: number,
|
||||
futureDate?: boolean,
|
||||
short?: boolean,
|
||||
}
|
||||
interface States {
|
||||
now: number;
|
||||
now: number,
|
||||
}
|
||||
class RelativeTimestamp extends Component<Props, States> {
|
||||
state = {
|
||||
@@ -260,7 +260,7 @@ class RelativeTimestamp extends Component<Props, States> {
|
||||
render() {
|
||||
const { timestamp, intl, year, futureDate, short } = this.props;
|
||||
|
||||
const timeGiven = timestamp.includes('T');
|
||||
const timeGiven = timestamp.includes("T");
|
||||
const date = new Date(timestamp);
|
||||
const relativeTime = futureDate
|
||||
? timeRemainingString(intl, date, this.state.now, timeGiven)
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { PropsWithChildren } from 'react';
|
||||
import React from 'react';
|
||||
import { type PropsWithChildren } from "react";
|
||||
import React from "react";
|
||||
|
||||
import { createBrowserHistory } from 'history';
|
||||
import { Router as OriginalRouter } from 'react-router';
|
||||
import { createBrowserHistory } from "history";
|
||||
import { Router as OriginalRouter } from "react-router";
|
||||
|
||||
interface MastodonLocationState {
|
||||
fromMastodon?: boolean;
|
||||
mastodonModalKey?: string;
|
||||
fromMastodon?: boolean,
|
||||
mastodonModalKey?: string,
|
||||
}
|
||||
|
||||
const browserHistory = createBrowserHistory<
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { Children, cloneElement, PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { Children, cloneElement, PureComponent } from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { List as ImmutableList } from 'immutable';
|
||||
import { connect } from 'react-redux';
|
||||
import { List as ImmutableList } from "immutable";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { supportsPassiveEvents } from 'detect-passive-events';
|
||||
import { throttle } from 'lodash';
|
||||
import { supportsPassiveEvents } from "detect-passive-events";
|
||||
import { throttle } from "lodash";
|
||||
|
||||
import IntersectionObserverArticleContainer from 'flavours/glitch/containers/intersection_observer_article_container';
|
||||
import ScrollContainer from 'flavours/glitch/containers/scroll_container';
|
||||
import IntersectionObserverWrapper from 'flavours/glitch/features/ui/util/intersection_observer_wrapper';
|
||||
import IntersectionObserverArticleContainer from "flavours/glitch/containers/intersection_observer_article_container";
|
||||
import ScrollContainer from "flavours/glitch/containers/scroll_container";
|
||||
import IntersectionObserverWrapper from "flavours/glitch/features/ui/util/intersection_observer_wrapper";
|
||||
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from '../features/ui/util/fullscreen';
|
||||
import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from "../features/ui/util/fullscreen";
|
||||
|
||||
import { LoadMore } from './load_more';
|
||||
import { LoadPending } from './load_pending';
|
||||
import { LoadingIndicator } from './loading_indicator';
|
||||
import { LoadMore } from "./load_more";
|
||||
import { LoadPending } from "./load_pending";
|
||||
import { LoadingIndicator } from "./loading_indicator";
|
||||
|
||||
const MOUSE_IDLE_DELAY = 300;
|
||||
|
||||
@@ -236,7 +236,7 @@ class ScrollableList extends PureComponent {
|
||||
attachIntersectionObserver () {
|
||||
let nodeOptions = {
|
||||
root: this.node,
|
||||
rootMargin: '300% 0px',
|
||||
rootMargin: "300% 0px",
|
||||
};
|
||||
|
||||
this.intersectionObserverWrapper
|
||||
@@ -249,21 +249,21 @@ class ScrollableList extends PureComponent {
|
||||
|
||||
attachScrollListener () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.addEventListener('scroll', this.handleScroll);
|
||||
document.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
document.addEventListener("scroll", this.handleScroll);
|
||||
document.addEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.addEventListener('scroll', this.handleScroll);
|
||||
this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
this.node.addEventListener("scroll", this.handleScroll);
|
||||
this.node.addEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
detachScrollListener () {
|
||||
if (this.props.bindToDocument) {
|
||||
document.removeEventListener('scroll', this.handleScroll);
|
||||
document.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
document.removeEventListener("scroll", this.handleScroll);
|
||||
document.removeEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
} else {
|
||||
this.node.removeEventListener('scroll', this.handleScroll);
|
||||
this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
|
||||
this.node.removeEventListener("scroll", this.handleScroll);
|
||||
this.node.removeEventListener("wheel", this.handleWheel, listenerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -324,7 +324,7 @@ class ScrollableList extends PureComponent {
|
||||
);
|
||||
} else if (isLoading || childrenCount > 0 || hasMore || !emptyMessage) {
|
||||
scrollableArea = (
|
||||
<div className={classNames('scrollable', { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
|
||||
<div className={classNames("scrollable", { fullscreen })} ref={this.setRef} onMouseMove={this.handleMouseMove}>
|
||||
<div role='feed' className='item-list'>
|
||||
{prepend}
|
||||
|
||||
@@ -356,7 +356,7 @@ class ScrollableList extends PureComponent {
|
||||
);
|
||||
} else {
|
||||
scrollableArea = (
|
||||
<div className={classNames('scrollable scrollable--flex', { fullscreen })} ref={this.setRef}>
|
||||
<div className={classNames("scrollable scrollable--flex", { fullscreen })} ref={this.setRef}>
|
||||
{alwaysPrepend && prepend}
|
||||
|
||||
<div className='empty-column-indicator'>
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
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 { fetchServer } from 'flavours/glitch/actions/server';
|
||||
import { ServerHeroImage } from 'flavours/glitch/components/server_hero_image';
|
||||
import { ShortNumber } from 'flavours/glitch/components/short_number';
|
||||
import { Skeleton } from 'flavours/glitch/components/skeleton';
|
||||
import Account from 'flavours/glitch/containers/account_container';
|
||||
import { domain } from 'flavours/glitch/initial_state';
|
||||
import { fetchServer } from "flavours/glitch/actions/server";
|
||||
import { ServerHeroImage } from "flavours/glitch/components/server_hero_image";
|
||||
import { ShortNumber } from "flavours/glitch/components/short_number";
|
||||
import { Skeleton } from "flavours/glitch/components/skeleton";
|
||||
import Account from "flavours/glitch/containers/account_container";
|
||||
import { domain } from "flavours/glitch/initial_state";
|
||||
|
||||
const messages = defineMessages({
|
||||
aboutActiveUsers: { id: 'server_banner.about_active_users', defaultMessage: 'People using this server during the last 30 days (Monthly Active Users)' },
|
||||
aboutActiveUsers: { id: "server_banner.about_active_users", defaultMessage: "People using this server during the last 30 days (Monthly Active Users)" },
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
server: state.getIn(['server', 'server']),
|
||||
server: state.getIn(["server", "server"]),
|
||||
});
|
||||
|
||||
class ServerBanner extends PureComponent {
|
||||
@@ -37,15 +37,15 @@ class ServerBanner extends PureComponent {
|
||||
|
||||
render () {
|
||||
const { server, intl } = this.props;
|
||||
const isLoading = server.get('isLoading');
|
||||
const isLoading = server.get("isLoading");
|
||||
|
||||
return (
|
||||
<div className='server-banner'>
|
||||
<div className='server-banner__introduction'>
|
||||
<FormattedMessage id='server_banner.introduction' defaultMessage='{domain} is part of the decentralized social network powered by {mastodon}.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank'>Mastodon</a> }} />
|
||||
<FormattedMessage id='server_banner.introduction' defaultMessage='{domain} is part of the decentralized social network powered by {mastodon}.' values={{ domain: <strong>{domain}</strong>, mastodon: <a href='https://joinmastodon.org' target='_blank' rel="noreferrer">Mastodon</a> }} />
|
||||
</div>
|
||||
|
||||
<ServerHeroImage blurhash={server.getIn(['thumbnail', 'blurhash'])} src={server.getIn(['thumbnail', 'url'])} className='server-banner__hero' />
|
||||
<ServerHeroImage blurhash={server.getIn(["thumbnail", "blurhash"])} src={server.getIn(["thumbnail", "url"])} className='server-banner__hero' />
|
||||
|
||||
<div className='server-banner__description'>
|
||||
{isLoading ? (
|
||||
@@ -56,14 +56,14 @@ class ServerBanner extends PureComponent {
|
||||
<br />
|
||||
<Skeleton width='70%' />
|
||||
</>
|
||||
) : server.get('description')}
|
||||
) : server.get("description")}
|
||||
</div>
|
||||
|
||||
<div className='server-banner__meta'>
|
||||
<div className='server-banner__meta__column'>
|
||||
<h4><FormattedMessage id='server_banner.administered_by' defaultMessage='Administered by:' /></h4>
|
||||
|
||||
<Account id={server.getIn(['contact', 'account', 'id'])} size={36} />
|
||||
<Account id={server.getIn(["contact", "account", "id"])} size={36} />
|
||||
</div>
|
||||
|
||||
<div className='server-banner__meta__column'>
|
||||
@@ -77,7 +77,7 @@ class ServerBanner extends PureComponent {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<strong className='server-banner__number'><ShortNumber value={server.getIn(['usage', 'users', 'active_month'])} /></strong>
|
||||
<strong className='server-banner__number'><ShortNumber value={server.getIn(["usage", "users", "active_month"])} /></strong>
|
||||
<br />
|
||||
<span className='server-banner__number-label' title={intl.formatMessage(messages.aboutActiveUsers)}><FormattedMessage id='server_banner.active_users' defaultMessage='active users' /></span>
|
||||
</>
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
import { useCallback, useState } from "react";
|
||||
import * as React from "react";
|
||||
|
||||
import classNames from 'classnames';
|
||||
import classNames from "classnames";
|
||||
|
||||
import { Blurhash } from './blurhash';
|
||||
import { Blurhash } from "./blurhash";
|
||||
|
||||
interface Props {
|
||||
src: string;
|
||||
srcSet?: string;
|
||||
blurhash?: string;
|
||||
className?: string;
|
||||
src: string,
|
||||
srcSet?: string,
|
||||
blurhash?: string,
|
||||
className?: string,
|
||||
}
|
||||
|
||||
export const ServerHeroImage: React.FC<Props> = ({
|
||||
@@ -26,7 +26,7 @@ export const ServerHeroImage: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('image', { loaded }, className)}
|
||||
className={classNames("image", { loaded }, className)}
|
||||
role='presentation'
|
||||
>
|
||||
{blurhash && <Blurhash hash={blurhash} className='image__preview' />}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
export default class SettingText extends PureComponent {
|
||||
|
||||
@@ -21,7 +21,7 @@ export default class SettingText extends PureComponent {
|
||||
|
||||
return (
|
||||
<label>
|
||||
<span style={{ display: 'none' }}>{label}</span>
|
||||
<span style={{ display: "none" }}>{label}</span>
|
||||
<input
|
||||
className='setting-text'
|
||||
value={settings.getIn(settingPath)}
|
||||
|
||||
@@ -1,47 +1,11 @@
|
||||
import { memo } from 'react';
|
||||
import React, { memo } from "react";
|
||||
|
||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
||||
import { FormattedMessage, FormattedNumber } from "react-intl";
|
||||
|
||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
||||
|
||||
type ShortNumberRenderer = (
|
||||
displayNumber: JSX.Element,
|
||||
pluralReady: number,
|
||||
) => JSX.Element;
|
||||
|
||||
interface ShortNumberProps {
|
||||
value: number;
|
||||
renderer?: ShortNumberRenderer;
|
||||
children?: ShortNumberRenderer;
|
||||
}
|
||||
|
||||
export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({
|
||||
value,
|
||||
renderer,
|
||||
children,
|
||||
}) => {
|
||||
const shortNumber = toShortNumber(value);
|
||||
const [, division] = shortNumber;
|
||||
|
||||
if (children && renderer) {
|
||||
console.warn(
|
||||
'Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.',
|
||||
);
|
||||
}
|
||||
|
||||
const customRenderer = children ?? renderer ?? null;
|
||||
|
||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
||||
|
||||
return (
|
||||
customRenderer?.(displayNumber, pluralReady(value, division)) ??
|
||||
displayNumber
|
||||
);
|
||||
};
|
||||
export const ShortNumber = memo(ShortNumberRenderer);
|
||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from "../utils/numbers";
|
||||
|
||||
interface ShortNumberCounterProps {
|
||||
value: number[];
|
||||
value: number[],
|
||||
}
|
||||
const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
|
||||
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
||||
@@ -88,3 +52,39 @@ const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
type ShortNumberRenderer = (
|
||||
displayNumber: React.JSX.Element,
|
||||
pluralReady: number,
|
||||
) => React.JSX.Element;
|
||||
|
||||
interface ShortNumberProps {
|
||||
value: number,
|
||||
renderer?: ShortNumberRenderer,
|
||||
children?: ShortNumberRenderer,
|
||||
}
|
||||
|
||||
export const ShortNumberRenderer: React.FC<ShortNumberProps> = ({
|
||||
value,
|
||||
renderer,
|
||||
children,
|
||||
}) => {
|
||||
const shortNumber = toShortNumber(value);
|
||||
const [, division] = shortNumber;
|
||||
|
||||
if (children && renderer) {
|
||||
console.warn(
|
||||
"Both renderer prop and renderer as a child provided. This is a mistake and you really should fix that. Only renderer passed as a child will be used.",
|
||||
);
|
||||
}
|
||||
|
||||
const customRenderer = children ?? renderer ?? null;
|
||||
|
||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
||||
|
||||
return (
|
||||
customRenderer?.(displayNumber, pluralReady(value, division)) ??
|
||||
displayNumber
|
||||
);
|
||||
};
|
||||
export const ShortNumber = memo(ShortNumberRenderer);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import * as React from 'react';
|
||||
import * as React from "react";
|
||||
|
||||
interface Props {
|
||||
width?: number | string;
|
||||
height?: number | string;
|
||||
width?: number | string,
|
||||
height?: number | string,
|
||||
}
|
||||
|
||||
export const Skeleton: React.FC<Props> = ({ width, height }) => (
|
||||
|
||||
@@ -1,52 +1,52 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { 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 ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import { HotKeys } from 'react-hotkeys';
|
||||
import { HotKeys } from "react-hotkeys";
|
||||
|
||||
import PictureInPicturePlaceholder from 'flavours/glitch/components/picture_in_picture_placeholder';
|
||||
import PollContainer from 'flavours/glitch/containers/poll_container';
|
||||
import NotificationOverlayContainer from 'flavours/glitch/features/notifications/containers/overlay_container';
|
||||
import { displayMedia } from 'flavours/glitch/initial_state';
|
||||
import { autoUnfoldCW } from 'flavours/glitch/utils/content_warning';
|
||||
import PictureInPicturePlaceholder from "flavours/glitch/components/picture_in_picture_placeholder";
|
||||
import PollContainer from "flavours/glitch/containers/poll_container";
|
||||
import NotificationOverlayContainer from "flavours/glitch/features/notifications/containers/overlay_container";
|
||||
import { displayMedia } from "flavours/glitch/initial_state";
|
||||
import { autoUnfoldCW } from "flavours/glitch/utils/content_warning";
|
||||
|
||||
import Card from '../features/status/components/card';
|
||||
import Bundle from '../features/ui/components/bundle';
|
||||
import { MediaGallery, Video, Audio } from '../features/ui/util/async-components';
|
||||
import Card from "../features/status/components/card";
|
||||
import Bundle from "../features/ui/components/bundle";
|
||||
import { MediaGallery, Video, Audio } from "../features/ui/util/async-components";
|
||||
|
||||
import AttachmentList from './attachment_list';
|
||||
import StatusActionBar from './status_action_bar';
|
||||
import StatusContent from './status_content';
|
||||
import StatusHeader from './status_header';
|
||||
import StatusIcons from './status_icons';
|
||||
import StatusPrepend from './status_prepend';
|
||||
import AttachmentList from "./attachment_list";
|
||||
import StatusActionBar from "./status_action_bar";
|
||||
import StatusContent from "./status_content";
|
||||
import StatusHeader from "./status_header";
|
||||
import StatusIcons from "./status_icons";
|
||||
import StatusPrepend from "./status_prepend";
|
||||
|
||||
const domParser = new DOMParser();
|
||||
|
||||
export const textForScreenReader = (intl, status, rebloggedByText = false, expanded = false) => {
|
||||
const displayName = status.getIn(['account', 'display_name']);
|
||||
const displayName = status.getIn(["account", "display_name"]);
|
||||
|
||||
const spoilerText = status.getIn(['translation', 'spoiler_text']) || status.get('spoiler_text');
|
||||
const contentHtml = status.getIn(['translation', 'contentHtml']) || status.get('contentHtml');
|
||||
const contentText = domParser.parseFromString(contentHtml, 'text/html').documentElement.textContent;
|
||||
const spoilerText = status.getIn(["translation", "spoiler_text"]) || status.get("spoiler_text");
|
||||
const contentHtml = status.getIn(["translation", "contentHtml"]) || status.get("contentHtml");
|
||||
const contentText = domParser.parseFromString(contentHtml, "text/html").documentElement.textContent;
|
||||
|
||||
const values = [
|
||||
displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName,
|
||||
displayName.length === 0 ? status.getIn(["account", "acct"]).split("@")[0] : displayName,
|
||||
spoilerText && !expanded ? spoilerText : contentText,
|
||||
intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }),
|
||||
status.getIn(['account', 'acct']),
|
||||
intl.formatDate(status.get("created_at"), { hour: "2-digit", minute: "2-digit", month: "short", day: "numeric" }),
|
||||
status.getIn(["account", "acct"]),
|
||||
];
|
||||
|
||||
if (rebloggedByText) {
|
||||
values.push(rebloggedByText);
|
||||
}
|
||||
|
||||
return values.join(', ');
|
||||
return values.join(", ");
|
||||
};
|
||||
|
||||
export const defaultMediaVisibility = (status, settings) => {
|
||||
@@ -54,15 +54,15 @@ export const defaultMediaVisibility = (status, settings) => {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') {
|
||||
status = status.get('reblog');
|
||||
if (status.get("reblog", null) !== null && typeof status.get("reblog") === "object") {
|
||||
status = status.get("reblog");
|
||||
}
|
||||
|
||||
if (settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text')) {
|
||||
if (settings.getIn(["media", "reveal_behind_cw"]) && !!status.get("spoiler_text")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all');
|
||||
return (displayMedia !== "hide_all" && !status.get("sensitive") || displayMedia === "show_all");
|
||||
};
|
||||
|
||||
class Status extends ImmutablePureComponent {
|
||||
@@ -133,26 +133,26 @@ class Status extends ImmutablePureComponent {
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
// evaluate to false. See react-immutable-pure-component for usage.
|
||||
updateOnProps = [
|
||||
'status',
|
||||
'account',
|
||||
'settings',
|
||||
'prepend',
|
||||
'muted',
|
||||
'notification',
|
||||
'hidden',
|
||||
'expanded',
|
||||
'unread',
|
||||
'pictureInPicture',
|
||||
'previousId',
|
||||
'nextInReplyToId',
|
||||
'rootId',
|
||||
"status",
|
||||
"account",
|
||||
"settings",
|
||||
"prepend",
|
||||
"muted",
|
||||
"notification",
|
||||
"hidden",
|
||||
"expanded",
|
||||
"unread",
|
||||
"pictureInPicture",
|
||||
"previousId",
|
||||
"nextInReplyToId",
|
||||
"rootId",
|
||||
];
|
||||
|
||||
updateOnStates = [
|
||||
'isExpanded',
|
||||
'isCollapsed',
|
||||
'showMedia',
|
||||
'forceFilter',
|
||||
"isExpanded",
|
||||
"isCollapsed",
|
||||
"showMedia",
|
||||
"forceFilter",
|
||||
];
|
||||
|
||||
// If our settings have changed to disable collapsed statuses, then we
|
||||
@@ -172,13 +172,13 @@ class Status extends ImmutablePureComponent {
|
||||
update.expandedProp = nextProps.expanded;
|
||||
updated = true;
|
||||
}
|
||||
if (nextProps.status?.get('hidden') !== prevState.statusPropHidden) {
|
||||
update.statusPropHidden = nextProps.status?.get('hidden');
|
||||
if (nextProps.status?.get("hidden") !== prevState.statusPropHidden) {
|
||||
update.statusPropHidden = nextProps.status?.get("hidden");
|
||||
updated = true;
|
||||
}
|
||||
|
||||
// Update state based on new props
|
||||
if (!nextProps.settings.getIn(['collapsed', 'enabled'])) {
|
||||
if (!nextProps.settings.getIn(["collapsed", "enabled"])) {
|
||||
if (prevState.isCollapsed) {
|
||||
update.isCollapsed = false;
|
||||
updated = true;
|
||||
@@ -186,8 +186,8 @@ class Status extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
// Handle uncollapsing toots when the shared CW state is expanded
|
||||
if (nextProps.settings.getIn(['content_warnings', 'shared_state']) &&
|
||||
nextProps.status?.get('spoiler_text')?.length && nextProps.status?.get('hidden') === false &&
|
||||
if (nextProps.settings.getIn(["content_warnings", "shared_state"]) &&
|
||||
nextProps.status?.get("spoiler_text")?.length && nextProps.status?.get("hidden") === false &&
|
||||
prevState.statusPropHidden !== false && prevState.isCollapsed
|
||||
) {
|
||||
update.isCollapsed = false;
|
||||
@@ -200,7 +200,9 @@ class Status extends ImmutablePureComponent {
|
||||
nextProps.expanded !== undefined
|
||||
) {
|
||||
update.isExpanded = nextProps.expanded;
|
||||
if (nextProps.expanded) update.isCollapsed = false;
|
||||
if (nextProps.expanded) {
|
||||
update.isCollapsed = false;
|
||||
}
|
||||
updated = true;
|
||||
}
|
||||
|
||||
@@ -209,14 +211,14 @@ class Status extends ImmutablePureComponent {
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) {
|
||||
if (nextProps.status && nextProps.status.get("id") !== prevState.statusId) {
|
||||
update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings);
|
||||
update.statusId = nextProps.status.get('id');
|
||||
update.statusId = nextProps.status.get("id");
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if (nextProps.settings.getIn(['media', 'reveal_behind_cw']) !== prevState.revealBehindCW) {
|
||||
update.revealBehindCW = nextProps.settings.getIn(['media', 'reveal_behind_cw']);
|
||||
if (nextProps.settings.getIn(["media", "reveal_behind_cw"]) !== prevState.revealBehindCW) {
|
||||
update.revealBehindCW = nextProps.settings.getIn(["media", "reveal_behind_cw"]);
|
||||
if (update.revealBehindCW) {
|
||||
update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings);
|
||||
}
|
||||
@@ -257,26 +259,30 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
// Prevent a crash when node is undefined. Not completely sure why this
|
||||
// happens, might be because status === null.
|
||||
if (node === undefined) return;
|
||||
if (node === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const autoCollapseSettings = settings.getIn(['collapsed', 'auto']);
|
||||
const autoCollapseSettings = settings.getIn(["collapsed", "auto"]);
|
||||
|
||||
// Don't autocollapse if CW state is shared and status is explicitly revealed,
|
||||
// as it could cause surprising changes when receiving notifications
|
||||
if (settings.getIn(['content_warnings', 'shared_state']) && status.get('spoiler_text').length && !status.get('hidden')) return;
|
||||
if (settings.getIn(["content_warnings", "shared_state"]) && status.get("spoiler_text").length && !status.get("hidden")) {
|
||||
return;
|
||||
}
|
||||
|
||||
let autoCollapseHeight = parseInt(autoCollapseSettings.get('height'));
|
||||
if (status.get('media_attachments').size && !muted) {
|
||||
let autoCollapseHeight = parseInt(autoCollapseSettings.get("height"));
|
||||
if (status.get("media_attachments").size && !muted) {
|
||||
autoCollapseHeight += 210;
|
||||
}
|
||||
|
||||
if (collapse ||
|
||||
autoCollapseSettings.get('all') ||
|
||||
(autoCollapseSettings.get('notifications') && muted) ||
|
||||
(autoCollapseSettings.get('lengthy') && node.clientHeight > autoCollapseHeight) ||
|
||||
(autoCollapseSettings.get('reblogs') && prepend === 'reblogged_by') ||
|
||||
(autoCollapseSettings.get('replies') && status.get('in_reply_to_id', null) !== null) ||
|
||||
(autoCollapseSettings.get('media') && !(status.get('spoiler_text').length) && status.get('media_attachments').size > 0)
|
||||
autoCollapseSettings.get("all") ||
|
||||
(autoCollapseSettings.get("notifications") && muted) ||
|
||||
(autoCollapseSettings.get("lengthy") && node.clientHeight > autoCollapseHeight) ||
|
||||
(autoCollapseSettings.get("reblogs") && prepend === "reblogged_by") ||
|
||||
(autoCollapseSettings.get("replies") && status.get("in_reply_to_id", null) !== null) ||
|
||||
(autoCollapseSettings.get("media") && !(status.get("spoiler_text").length) && status.get("media_attachments").size > 0)
|
||||
) {
|
||||
this.setCollapsed(true);
|
||||
// Hack to fix timeline jumps on second rendering when auto-collapsing
|
||||
@@ -285,21 +291,27 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
// Hack to fix timeline jumps when a preview card is fetched
|
||||
this.setState({
|
||||
showCard: !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get('card') && this.props.settings.get('inline_preview_cards'),
|
||||
showCard: !this.props.muted && !this.props.hidden && this.props.status && this.props.status.get("card") && this.props.settings.get("inline_preview_cards"),
|
||||
});
|
||||
}
|
||||
|
||||
// Hack to fix timeline jumps on second rendering when auto-collapsing
|
||||
// or on subsequent rendering when a preview card has been fetched
|
||||
getSnapshotBeforeUpdate() {
|
||||
if (!this.props.getScrollPosition) return null;
|
||||
if (!this.props.getScrollPosition) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { muted, hidden, status, settings } = this.props;
|
||||
|
||||
const doShowCard = !muted && !hidden && status && status.get('card') && settings.get('inline_preview_cards');
|
||||
const doShowCard = !muted && !hidden && status && status.get("card") && settings.get("inline_preview_cards");
|
||||
if (this.state.autoCollapsed || (doShowCard && !this.state.showCard)) {
|
||||
if (doShowCard) this.setState({ showCard: true });
|
||||
if (this.state.autoCollapsed) this.setState({ autoCollapsed: false });
|
||||
if (doShowCard) {
|
||||
this.setState({ showCard: true });
|
||||
}
|
||||
if (this.state.autoCollapsed) {
|
||||
this.setState({ autoCollapsed: false });
|
||||
}
|
||||
return this.props.getScrollPosition();
|
||||
} else {
|
||||
return null;
|
||||
@@ -329,7 +341,7 @@ class Status extends ImmutablePureComponent {
|
||||
// `setCollapsed()` automatically checks for us whether toot collapsing
|
||||
// is enabled, so we don't have to.
|
||||
setCollapsed = (value) => {
|
||||
if (this.props.settings.getIn(['collapsed', 'enabled'])) {
|
||||
if (this.props.settings.getIn(["collapsed", "enabled"])) {
|
||||
if (value) {
|
||||
this.setExpansion(false);
|
||||
}
|
||||
@@ -340,7 +352,7 @@ class Status extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
setExpansion = (value) => {
|
||||
if (this.props.settings.getIn(['content_warnings', 'shared_state']) && this.props.status.get('hidden') === value) {
|
||||
if (this.props.settings.getIn(["content_warnings", "shared_state"]) && this.props.status.get("hidden") === value) {
|
||||
this.props.onToggleHidden(this.props.status);
|
||||
}
|
||||
|
||||
@@ -359,11 +371,14 @@ class Status extends ImmutablePureComponent {
|
||||
const { router } = this.context;
|
||||
const { status } = this.props;
|
||||
const { isCollapsed } = this.state;
|
||||
if (!router) return;
|
||||
if (!router) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
|
||||
if (isCollapsed) this.setCollapsed(false);
|
||||
else if (e.shiftKey) {
|
||||
if (isCollapsed) {
|
||||
this.setCollapsed(false);
|
||||
} else if (e.shiftKey) {
|
||||
this.setCollapsed(true);
|
||||
document.getSelection().removeAllRanges();
|
||||
} else if (this.props.onClick) {
|
||||
@@ -372,9 +387,9 @@ class Status extends ImmutablePureComponent {
|
||||
} else {
|
||||
if (destination === undefined) {
|
||||
destination = `/@${
|
||||
status.getIn(['reblog', 'account', 'acct'], status.getIn(['account', 'acct']))
|
||||
status.getIn(["reblog", "account", "acct"], status.getIn(["account", "acct"]))
|
||||
}/${
|
||||
status.getIn(['reblog', 'id'], status.get('id'))
|
||||
status.getIn(["reblog", "id"], status.get("id"))
|
||||
}`;
|
||||
}
|
||||
router.history.push(destination);
|
||||
@@ -388,37 +403,37 @@ class Status extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
handleExpandedToggle = () => {
|
||||
if (this.props.settings.getIn(['content_warnings', 'shared_state'])) {
|
||||
if (this.props.settings.getIn(["content_warnings", "shared_state"])) {
|
||||
this.props.onToggleHidden(this.props.status);
|
||||
} else if (this.props.status.get('spoiler_text')) {
|
||||
} else if (this.props.status.get("spoiler_text")) {
|
||||
this.setExpansion(!this.state.isExpanded);
|
||||
}
|
||||
};
|
||||
|
||||
handleOpenVideo = (options) => {
|
||||
const { status } = this.props;
|
||||
const lang = status.getIn(['translation', 'language']) || status.get('language');
|
||||
this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), lang, options);
|
||||
const lang = status.getIn(["translation", "language"]) || status.get("language");
|
||||
this.props.onOpenVideo(status.get("id"), status.getIn(["media_attachments", 0]), lang, options);
|
||||
};
|
||||
|
||||
handleOpenMedia = (media, index) => {
|
||||
const { status } = this.props;
|
||||
const lang = status.getIn(['translation', 'language']) || status.get('language');
|
||||
this.props.onOpenMedia(status.get('id'), media, index, lang);
|
||||
const lang = status.getIn(["translation", "language"]) || status.get("language");
|
||||
this.props.onOpenMedia(status.get("id"), media, index, lang);
|
||||
};
|
||||
|
||||
handleHotkeyOpenMedia = e => {
|
||||
const { status, onOpenMedia, onOpenVideo } = this.props;
|
||||
const statusId = status.get('id');
|
||||
const statusId = status.get("id");
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
if (status.get('media_attachments').size > 0) {
|
||||
const lang = status.getIn(['translation', 'language']) || status.get('language');
|
||||
if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
|
||||
onOpenVideo(statusId, status.getIn(['media_attachments', 0]), lang, { startTime: 0 });
|
||||
if (status.get("media_attachments").size > 0) {
|
||||
const lang = status.getIn(["translation", "language"]) || status.get("language");
|
||||
if (status.getIn(["media_attachments", 0, "type"]) === "video") {
|
||||
onOpenVideo(statusId, status.getIn(["media_attachments", 0]), lang, { startTime: 0 });
|
||||
} else {
|
||||
onOpenMedia(statusId, status.get('media_attachments'), 0, lang);
|
||||
onOpenMedia(statusId, status.get("media_attachments"), 0, lang);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -448,29 +463,30 @@ class Status extends ImmutablePureComponent {
|
||||
|
||||
handleHotkeyMention = e => {
|
||||
e.preventDefault();
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onMention(this.props.status.get("account"), this.context.router.history);
|
||||
};
|
||||
|
||||
handleHotkeyOpen = () => {
|
||||
const status = this.props.status;
|
||||
this.context.router.history.push(`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`);
|
||||
this.context.router.history.push(`/@${status.getIn(["account", "acct"])}/${status.get("id")}`);
|
||||
};
|
||||
|
||||
handleHotkeyOpenProfile = () => {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}`);
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(["account", "acct"])}`);
|
||||
};
|
||||
|
||||
handleHotkeyMoveUp = e => {
|
||||
this.props.onMoveUp(this.props.containerId || this.props.id, e.target.getAttribute('data-featured'));
|
||||
this.props.onMoveUp(this.props.containerId || this.props.id, e.target.getAttribute("data-featured"));
|
||||
};
|
||||
|
||||
handleHotkeyMoveDown = e => {
|
||||
this.props.onMoveDown(this.props.containerId || this.props.id, e.target.getAttribute('data-featured'));
|
||||
this.props.onMoveDown(this.props.containerId || this.props.id, e.target.getAttribute("data-featured"));
|
||||
};
|
||||
|
||||
handleHotkeyCollapse = () => {
|
||||
if (!this.props.settings.getIn(['collapsed', 'enabled']))
|
||||
if (!this.props.settings.getIn(["collapsed", "enabled"])) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setCollapsed(!this.state.isCollapsed);
|
||||
};
|
||||
@@ -497,15 +513,15 @@ class Status extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
renderLoadingMediaGallery () {
|
||||
return <div className='media-gallery' style={{ height: '110px' }} />;
|
||||
return <div className='media-gallery' style={{ height: "110px" }} />;
|
||||
}
|
||||
|
||||
renderLoadingVideoPlayer () {
|
||||
return <div className='video-player' style={{ height: '110px' }} />;
|
||||
return <div className='video-player' style={{ height: "110px" }} />;
|
||||
}
|
||||
|
||||
renderLoadingAudioPlayer () {
|
||||
return <div className='audio-player' style={{ height: '110px' }} />;
|
||||
return <div className='audio-player' style={{ height: "110px" }} />;
|
||||
}
|
||||
|
||||
render () {
|
||||
@@ -549,7 +565,7 @@ class Status extends ImmutablePureComponent {
|
||||
let media = contentMedia;
|
||||
let mediaIcons = contentMediaIcons;
|
||||
|
||||
if (settings.getIn(['content_warnings', 'media_outside'])) {
|
||||
if (settings.getIn(["content_warnings", "media_outside"])) {
|
||||
media = extraMedia;
|
||||
mediaIcons = extraMediaIcons;
|
||||
}
|
||||
@@ -558,7 +574,7 @@ class Status extends ImmutablePureComponent {
|
||||
return null;
|
||||
}
|
||||
|
||||
const isExpanded = settings.getIn(['content_warnings', 'shared_state']) ? !status.get('hidden') : this.state.isExpanded;
|
||||
const isExpanded = settings.getIn(["content_warnings", "shared_state"]) ? !status.get("hidden") : this.state.isExpanded;
|
||||
|
||||
const handlers = {
|
||||
reply: this.handleHotkeyReply,
|
||||
@@ -582,17 +598,17 @@ class Status extends ImmutablePureComponent {
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
<div ref={this.handleRef} className='status focusable' tabIndex={0}>
|
||||
<span>{status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}</span>
|
||||
<span>{status.get('content')}</span>
|
||||
<span>{status.getIn(["account", "display_name"]) || status.getIn(["account", "username"])}</span>
|
||||
<span>{status.get("content")}</span>
|
||||
</div>
|
||||
</HotKeys>
|
||||
);
|
||||
}
|
||||
|
||||
const connectUp = previousId && previousId === status.get('in_reply_to_id');
|
||||
const connectToRoot = rootId && rootId === status.get('in_reply_to_id');
|
||||
const connectReply = nextInReplyToId && nextInReplyToId === status.get('id');
|
||||
const matchedFilters = status.get('matched_filters');
|
||||
const connectUp = previousId && previousId === status.get("in_reply_to_id");
|
||||
const connectToRoot = rootId && rootId === status.get("in_reply_to_id");
|
||||
const connectReply = nextInReplyToId && nextInReplyToId === status.get("id");
|
||||
const matchedFilters = status.get("matched_filters");
|
||||
|
||||
if (this.state.forceFilter === undefined ? matchedFilters : this.state.forceFilter) {
|
||||
const minHandlers = this.props.muted ? {} : {
|
||||
@@ -603,8 +619,8 @@ class Status extends ImmutablePureComponent {
|
||||
return (
|
||||
<HotKeys handlers={minHandlers}>
|
||||
<div className='status__wrapper status__wrapper--filtered focusable' tabIndex={0} ref={this.handleRef}>
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {matchedFilters.join(', ')}.
|
||||
{' '}
|
||||
<FormattedMessage id='status.filtered' defaultMessage='Filtered' />: {matchedFilters.join(", ")}.
|
||||
{" "}
|
||||
<button className='status__wrapper--filtered__button' onClick={this.handleUnfilterClick}>
|
||||
<FormattedMessage id='status.show_filter_reason' defaultMessage='Show anyway' />
|
||||
</button>
|
||||
@@ -616,8 +632,8 @@ class Status extends ImmutablePureComponent {
|
||||
// If user backgrounds for collapsed statuses are enabled, then we
|
||||
// initialize our background accordingly. This will only be rendered if
|
||||
// the status is collapsed.
|
||||
if (settings.getIn(['collapsed', 'backgrounds', 'user_backgrounds'])) {
|
||||
background = status.getIn(['account', 'header']);
|
||||
if (settings.getIn(["collapsed", "backgrounds", "user_backgrounds"])) {
|
||||
background = status.getIn(["account", "header"]);
|
||||
}
|
||||
|
||||
// This handles our media attachments.
|
||||
@@ -628,78 +644,78 @@ class Status extends ImmutablePureComponent {
|
||||
// `media`, we snatch the thumbnail to use as our `background` if media
|
||||
// backgrounds for collapsed statuses are enabled.
|
||||
|
||||
attachments = status.get('media_attachments');
|
||||
attachments = status.get("media_attachments");
|
||||
|
||||
if (pictureInPicture.get('inUse')) {
|
||||
if (pictureInPicture.get("inUse")) {
|
||||
media.push(<PictureInPicturePlaceholder />);
|
||||
mediaIcons.push('video-camera');
|
||||
mediaIcons.push("video-camera");
|
||||
} else if (attachments.size > 0) {
|
||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||
const language = status.getIn(["translation", "language"]) || status.get("language");
|
||||
|
||||
if (muted || attachments.some(item => item.get('type') === 'unknown')) {
|
||||
if (muted || attachments.some(item => item.get("type") === "unknown")) {
|
||||
media.push(
|
||||
<AttachmentList
|
||||
compact
|
||||
media={status.get('media_attachments')}
|
||||
media={status.get("media_attachments")}
|
||||
/>,
|
||||
);
|
||||
} else if (attachments.getIn([0, 'type']) === 'audio') {
|
||||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||
} else if (attachments.getIn([0, "type"]) === "audio") {
|
||||
const attachment = status.getIn(["media_attachments", 0]);
|
||||
const description = attachment.getIn(["translation", "description"]) || attachment.get("description");
|
||||
|
||||
media.push(
|
||||
<Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} >
|
||||
{Component => (
|
||||
<Component
|
||||
src={attachment.get('url')}
|
||||
src={attachment.get("url")}
|
||||
alt={description}
|
||||
lang={language}
|
||||
poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])}
|
||||
backgroundColor={attachment.getIn(['meta', 'colors', 'background'])}
|
||||
foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])}
|
||||
accentColor={attachment.getIn(['meta', 'colors', 'accent'])}
|
||||
duration={attachment.getIn(['meta', 'original', 'duration'], 0)}
|
||||
poster={attachment.get("preview_url") || status.getIn(["account", "avatar_static"])}
|
||||
backgroundColor={attachment.getIn(["meta", "colors", "background"])}
|
||||
foregroundColor={attachment.getIn(["meta", "colors", "foreground"])}
|
||||
accentColor={attachment.getIn(["meta", "colors", "accent"])}
|
||||
duration={attachment.getIn(["meta", "original", "duration"], 0)}
|
||||
width={this.props.cachedMediaWidth}
|
||||
height={110}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||
sensitive={status.get('sensitive')}
|
||||
blurhash={attachment.get('blurhash')}
|
||||
deployPictureInPicture={pictureInPicture.get("available") ? this.handleDeployPictureInPicture : undefined}
|
||||
sensitive={status.get("sensitive")}
|
||||
blurhash={attachment.get("blurhash")}
|
||||
visible={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
useBlurhash={settings.getIn(['media', 'use_blurhash'])}
|
||||
useBlurhash={settings.getIn(["media", "use_blurhash"])}
|
||||
/>
|
||||
)}
|
||||
</Bundle>,
|
||||
);
|
||||
mediaIcons.push('music');
|
||||
} else if (attachments.getIn([0, 'type']) === 'video') {
|
||||
const attachment = status.getIn(['media_attachments', 0]);
|
||||
const description = attachment.getIn(['translation', 'description']) || attachment.get('description');
|
||||
mediaIcons.push("music");
|
||||
} else if (attachments.getIn([0, "type"]) === "video") {
|
||||
const attachment = status.getIn(["media_attachments", 0]);
|
||||
const description = attachment.getIn(["translation", "description"]) || attachment.get("description");
|
||||
|
||||
media.push(
|
||||
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
|
||||
{Component => (<Component
|
||||
preview={attachment.get('preview_url')}
|
||||
frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])}
|
||||
blurhash={attachment.get('blurhash')}
|
||||
src={attachment.get('url')}
|
||||
preview={attachment.get("preview_url")}
|
||||
frameRate={attachment.getIn(["meta", "original", "frame_rate"])}
|
||||
blurhash={attachment.get("blurhash")}
|
||||
src={attachment.get("url")}
|
||||
alt={description}
|
||||
lang={language}
|
||||
inline
|
||||
sensitive={status.get('sensitive')}
|
||||
letterbox={settings.getIn(['media', 'letterbox'])}
|
||||
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
|
||||
sensitive={status.get("sensitive")}
|
||||
letterbox={settings.getIn(["media", "letterbox"])}
|
||||
fullwidth={!rootId && settings.getIn(["media", "fullwidth"])}
|
||||
preventPlayback={isCollapsed || !isExpanded}
|
||||
onOpenVideo={this.handleOpenVideo}
|
||||
deployPictureInPicture={pictureInPicture.get('available') ? this.handleDeployPictureInPicture : undefined}
|
||||
deployPictureInPicture={pictureInPicture.get("available") ? this.handleDeployPictureInPicture : undefined}
|
||||
visible={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
useBlurhash={settings.getIn(['media', 'use_blurhash'])}
|
||||
useBlurhash={settings.getIn(["media", "use_blurhash"])}
|
||||
/>)}
|
||||
</Bundle>,
|
||||
);
|
||||
mediaIcons.push('video-camera');
|
||||
mediaIcons.push("video-camera");
|
||||
} else { // Media type is 'image' or 'gifv'
|
||||
media.push(
|
||||
<Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery}>
|
||||
@@ -707,60 +723,60 @@ class Status extends ImmutablePureComponent {
|
||||
<Component
|
||||
media={attachments}
|
||||
lang={language}
|
||||
sensitive={status.get('sensitive')}
|
||||
letterbox={settings.getIn(['media', 'letterbox'])}
|
||||
fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])}
|
||||
sensitive={status.get("sensitive")}
|
||||
letterbox={settings.getIn(["media", "letterbox"])}
|
||||
fullwidth={!rootId && settings.getIn(["media", "fullwidth"])}
|
||||
hidden={isCollapsed || !isExpanded}
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
cacheWidth={this.props.cacheMediaWidth}
|
||||
defaultWidth={this.props.cachedMediaWidth}
|
||||
visible={this.state.showMedia}
|
||||
onToggleVisibility={this.handleToggleMediaVisibility}
|
||||
useBlurhash={settings.getIn(['media', 'use_blurhash'])}
|
||||
useBlurhash={settings.getIn(["media", "use_blurhash"])}
|
||||
/>
|
||||
)}
|
||||
</Bundle>,
|
||||
);
|
||||
mediaIcons.push('picture-o');
|
||||
mediaIcons.push("picture-o");
|
||||
}
|
||||
|
||||
if (!status.get('sensitive') && !(status.get('spoiler_text').length > 0) && settings.getIn(['collapsed', 'backgrounds', 'preview_images'])) {
|
||||
background = attachments.getIn([0, 'preview_url']);
|
||||
if (!status.get("sensitive") && !(status.get("spoiler_text").length > 0) && settings.getIn(["collapsed", "backgrounds", "preview_images"])) {
|
||||
background = attachments.getIn([0, "preview_url"]);
|
||||
}
|
||||
} else if (status.get('card') && settings.get('inline_preview_cards') && !this.props.muted) {
|
||||
} else if (status.get("card") && settings.get("inline_preview_cards") && !this.props.muted) {
|
||||
media.push(
|
||||
<Card
|
||||
onOpenMedia={this.handleOpenMedia}
|
||||
card={status.get('card')}
|
||||
card={status.get("card")}
|
||||
compact
|
||||
sensitive={status.get('sensitive')}
|
||||
useBlurhash={settings.getIn(['media', 'use_blurhash'])}
|
||||
sensitive={status.get("sensitive")}
|
||||
useBlurhash={settings.getIn(["media", "use_blurhash"])}
|
||||
/>,
|
||||
);
|
||||
mediaIcons.push('link');
|
||||
mediaIcons.push("link");
|
||||
}
|
||||
|
||||
if (status.get('poll')) {
|
||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||
contentMedia.push(<PollContainer pollId={status.get('poll')} lang={language} />);
|
||||
contentMediaIcons.push('tasks');
|
||||
if (status.get("poll")) {
|
||||
const language = status.getIn(["translation", "language"]) || status.get("language");
|
||||
contentMedia.push(<PollContainer pollId={status.get("poll")} lang={language} />);
|
||||
contentMediaIcons.push("tasks");
|
||||
}
|
||||
|
||||
// Here we prepare extra data-* attributes for CSS selectors.
|
||||
// Users can use those for theming, hiding avatars etc via UserStyle
|
||||
const selectorAttribs = {
|
||||
'data-status-by': `@${status.getIn(['account', 'acct'])}`,
|
||||
"data-status-by": `@${status.getIn(["account", "acct"])}`,
|
||||
};
|
||||
|
||||
if (this.props.prepend && account) {
|
||||
const notifKind = {
|
||||
favourite: 'favourited',
|
||||
reblog: 'boosted',
|
||||
reblogged_by: 'boosted',
|
||||
status: 'posted',
|
||||
favourite: "favourited",
|
||||
reblog: "boosted",
|
||||
reblogged_by: "boosted",
|
||||
status: "posted",
|
||||
}[this.props.prepend];
|
||||
|
||||
selectorAttribs[`data-${notifKind}-by`] = `@${account.get('acct')}`;
|
||||
selectorAttribs[`data-${notifKind}-by`] = `@${account.get("acct")}`;
|
||||
|
||||
prepend = (
|
||||
<StatusPrepend
|
||||
@@ -772,19 +788,19 @@ class Status extends ImmutablePureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
if (this.props.prepend === 'reblog') {
|
||||
rebloggedByText = intl.formatMessage({ id: 'status.reblogged_by', defaultMessage: '{name} boosted' }, { name: account.get('acct') });
|
||||
if (this.props.prepend === "reblog") {
|
||||
rebloggedByText = intl.formatMessage({ id: "status.reblogged_by", defaultMessage: "{name} boosted" }, { name: account.get("acct") });
|
||||
}
|
||||
|
||||
const computedClass = classNames('status', `status-${status.get('visibility')}`, {
|
||||
const computedClass = classNames("status", `status-${status.get("visibility")}`, {
|
||||
collapsed: isCollapsed,
|
||||
'has-background': isCollapsed && background,
|
||||
'status__wrapper-reply': !!status.get('in_reply_to_id'),
|
||||
'status--in-thread': !!rootId,
|
||||
'status--first-in-thread': previousId && (!connectUp || connectToRoot),
|
||||
"has-background": isCollapsed && background,
|
||||
"status__wrapper-reply": !!status.get("in_reply_to_id"),
|
||||
"status--in-thread": !!rootId,
|
||||
"status--first-in-thread": previousId && (!connectUp || connectToRoot),
|
||||
unread,
|
||||
muted,
|
||||
}, 'focusable');
|
||||
}, "focusable");
|
||||
|
||||
return (
|
||||
<HotKeys handlers={handlers}>
|
||||
@@ -794,13 +810,13 @@ class Status extends ImmutablePureComponent {
|
||||
{...selectorAttribs}
|
||||
ref={handleRef}
|
||||
tabIndex={0}
|
||||
data-featured={featured ? 'true' : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get('hidden'))}
|
||||
data-nosnippet={status.getIn(['account', 'noindex'], true) || undefined}
|
||||
data-featured={featured ? "true" : null}
|
||||
aria-label={textForScreenReader(intl, status, rebloggedByText, !status.get("hidden"))}
|
||||
data-nosnippet={status.getIn(["account", "noindex"], true) || undefined}
|
||||
>
|
||||
{!muted && prepend}
|
||||
|
||||
{(connectReply || connectUp || connectToRoot) && <div className={classNames('status__line', { 'status__line--full': connectReply, 'status__line--first': !status.get('in_reply_to_id') && !connectToRoot })} />}
|
||||
{(connectReply || connectUp || connectToRoot) && <div className={classNames("status__line", { "status__line--full": connectReply, "status__line--first": !status.get("in_reply_to_id") && !connectToRoot })} />}
|
||||
|
||||
<header className='status__info'>
|
||||
<span>
|
||||
@@ -817,10 +833,10 @@ class Status extends ImmutablePureComponent {
|
||||
<StatusIcons
|
||||
status={status}
|
||||
mediaIcons={contentMediaIcons.concat(extraMediaIcons)}
|
||||
collapsible={settings.getIn(['collapsed', 'enabled'])}
|
||||
collapsible={settings.getIn(["collapsed", "enabled"])}
|
||||
collapsed={isCollapsed}
|
||||
setCollapsed={setCollapsed}
|
||||
settings={settings.get('status_icons')}
|
||||
settings={settings.get("status_icons")}
|
||||
/>
|
||||
</header>
|
||||
<StatusContent
|
||||
@@ -833,15 +849,15 @@ class Status extends ImmutablePureComponent {
|
||||
onTranslate={this.handleTranslate}
|
||||
parseClick={parseClick}
|
||||
disabled={!router}
|
||||
tagLinks={settings.get('tag_misleading_links')}
|
||||
rewriteMentions={settings.get('rewrite_mentions')}
|
||||
tagLinks={settings.get("tag_misleading_links")}
|
||||
rewriteMentions={settings.get("rewrite_mentions")}
|
||||
/>
|
||||
|
||||
{!isCollapsed || !(muted || !settings.getIn(['collapsed', 'show_action_bar'])) ? (
|
||||
{!isCollapsed || !(muted || !settings.getIn(["collapsed", "show_action_bar"])) ? (
|
||||
<StatusActionBar
|
||||
status={status}
|
||||
account={status.get('account')}
|
||||
showReplyCount={settings.get('show_reply_count')}
|
||||
account={status.get("account")}
|
||||
showReplyCount={settings.get("show_reply_count")}
|
||||
onFilter={matchedFilters ? this.handleFilterClick : null}
|
||||
{...other}
|
||||
/>
|
||||
|
||||
@@ -1,53 +1,53 @@
|
||||
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 DropdownMenuContainer from 'flavours/glitch/containers/dropdown_menu_container';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
|
||||
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
|
||||
import DropdownMenuContainer from "flavours/glitch/containers/dropdown_menu_container";
|
||||
import { me } from "flavours/glitch/initial_state";
|
||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from "flavours/glitch/permissions";
|
||||
import { accountAdminLink, statusAdminLink } from "flavours/glitch/utils/backend_links";
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import { RelativeTimestamp } from './relative_timestamp';
|
||||
import { IconButton } from "./icon_button";
|
||||
import { RelativeTimestamp } from "./relative_timestamp";
|
||||
|
||||
const messages = defineMessages({
|
||||
delete: { id: 'status.delete', defaultMessage: 'Delete' },
|
||||
redraft: { id: 'status.redraft', defaultMessage: 'Delete & re-draft' },
|
||||
edit: { id: 'status.edit', defaultMessage: 'Edit' },
|
||||
direct: { id: 'status.direct', defaultMessage: 'Privately mention @{name}' },
|
||||
mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
|
||||
mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
|
||||
block: { id: 'account.block', defaultMessage: 'Block @{name}' },
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
share: { id: 'status.share', defaultMessage: 'Share' },
|
||||
more: { id: 'status.more', defaultMessage: 'More' },
|
||||
replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' },
|
||||
reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
|
||||
reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost with original visibility' },
|
||||
cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
|
||||
cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' },
|
||||
favourite: { id: 'status.favourite', defaultMessage: 'Favorite' },
|
||||
bookmark: { id: 'status.bookmark', defaultMessage: 'Bookmark' },
|
||||
open: { id: 'status.open', defaultMessage: 'Expand this status' },
|
||||
report: { id: 'status.report', defaultMessage: 'Report @{name}' },
|
||||
muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' },
|
||||
unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' },
|
||||
pin: { id: 'status.pin', defaultMessage: 'Pin on profile' },
|
||||
unpin: { id: 'status.unpin', defaultMessage: 'Unpin from profile' },
|
||||
embed: { id: 'status.embed', defaultMessage: 'Embed' },
|
||||
admin_account: { id: 'status.admin_account', defaultMessage: 'Open moderation interface for @{name}' },
|
||||
admin_status: { id: 'status.admin_status', defaultMessage: 'Open this post in the moderation interface' },
|
||||
admin_domain: { id: 'status.admin_domain', defaultMessage: 'Open moderation interface for {domain}' },
|
||||
copy: { id: 'status.copy', defaultMessage: 'Copy link to post' },
|
||||
hide: { id: 'status.hide', defaultMessage: 'Hide post' },
|
||||
edited: { id: 'status.edited', defaultMessage: 'Edited {date}' },
|
||||
filter: { id: 'status.filter', defaultMessage: 'Filter this post' },
|
||||
openOriginalPage: { id: 'account.open_original_page', defaultMessage: 'Open original page' },
|
||||
delete: { id: "status.delete", defaultMessage: "Delete" },
|
||||
redraft: { id: "status.redraft", defaultMessage: "Delete & re-draft" },
|
||||
edit: { id: "status.edit", defaultMessage: "Edit" },
|
||||
direct: { id: "status.direct", defaultMessage: "Privately mention @{name}" },
|
||||
mention: { id: "status.mention", defaultMessage: "Mention @{name}" },
|
||||
mute: { id: "account.mute", defaultMessage: "Mute @{name}" },
|
||||
block: { id: "account.block", defaultMessage: "Block @{name}" },
|
||||
reply: { id: "status.reply", defaultMessage: "Reply" },
|
||||
share: { id: "status.share", defaultMessage: "Share" },
|
||||
more: { id: "status.more", defaultMessage: "More" },
|
||||
replyAll: { id: "status.replyAll", defaultMessage: "Reply to thread" },
|
||||
reblog: { id: "status.reblog", defaultMessage: "Boost" },
|
||||
reblog_private: { id: "status.reblog_private", defaultMessage: "Boost with original visibility" },
|
||||
cancel_reblog_private: { id: "status.cancel_reblog_private", defaultMessage: "Unboost" },
|
||||
cannot_reblog: { id: "status.cannot_reblog", defaultMessage: "This post cannot be boosted" },
|
||||
favourite: { id: "status.favourite", defaultMessage: "Favorite" },
|
||||
bookmark: { id: "status.bookmark", defaultMessage: "Bookmark" },
|
||||
open: { id: "status.open", defaultMessage: "Expand this status" },
|
||||
report: { id: "status.report", defaultMessage: "Report @{name}" },
|
||||
muteConversation: { id: "status.mute_conversation", defaultMessage: "Mute conversation" },
|
||||
unmuteConversation: { id: "status.unmute_conversation", defaultMessage: "Unmute conversation" },
|
||||
pin: { id: "status.pin", defaultMessage: "Pin on profile" },
|
||||
unpin: { id: "status.unpin", defaultMessage: "Unpin from profile" },
|
||||
embed: { id: "status.embed", defaultMessage: "Embed" },
|
||||
admin_account: { id: "status.admin_account", defaultMessage: "Open moderation interface for @{name}" },
|
||||
admin_status: { id: "status.admin_status", defaultMessage: "Open this post in the moderation interface" },
|
||||
admin_domain: { id: "status.admin_domain", defaultMessage: "Open moderation interface for {domain}" },
|
||||
copy: { id: "status.copy", defaultMessage: "Copy link to post" },
|
||||
hide: { id: "status.hide", defaultMessage: "Hide post" },
|
||||
edited: { id: "status.edited", defaultMessage: "Edited {date}" },
|
||||
filter: { id: "status.filter", defaultMessage: "Filter this post" },
|
||||
openOriginalPage: { id: "account.open_original_page", defaultMessage: "Open original page" },
|
||||
});
|
||||
|
||||
class StatusActionBar extends ImmutablePureComponent {
|
||||
@@ -85,10 +85,10 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
// Avoid checking props that are functions (and whose equality will always
|
||||
// evaluate to false. See react-immutable-pure-component for usage.
|
||||
updateOnProps = [
|
||||
'status',
|
||||
'showReplyCount',
|
||||
'withCounters',
|
||||
'withDismiss',
|
||||
"status",
|
||||
"showReplyCount",
|
||||
"withCounters",
|
||||
"withDismiss",
|
||||
];
|
||||
|
||||
handleReplyClick = () => {
|
||||
@@ -97,13 +97,13 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
if (signedIn) {
|
||||
this.props.onReply(this.props.status, this.context.router.history);
|
||||
} else {
|
||||
this.props.onInteractionModal('reply', this.props.status);
|
||||
this.props.onInteractionModal("reply", this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
handleShareClick = () => {
|
||||
navigator.share({
|
||||
url: this.props.status.get('url'),
|
||||
url: this.props.status.get("url"),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -113,7 +113,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
if (signedIn) {
|
||||
this.props.onFavourite(this.props.status, e);
|
||||
} else {
|
||||
this.props.onInteractionModal('favourite', this.props.status);
|
||||
this.props.onInteractionModal("favourite", this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,7 +123,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
if (signedIn) {
|
||||
this.props.onReblog(this.props.status, e);
|
||||
} else {
|
||||
this.props.onInteractionModal('reblog', this.props.status);
|
||||
this.props.onInteractionModal("reblog", this.props.status);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -148,15 +148,15 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
handleMentionClick = () => {
|
||||
this.props.onMention(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onMention(this.props.status.get("account"), this.context.router.history);
|
||||
};
|
||||
|
||||
handleDirectClick = () => {
|
||||
this.props.onDirect(this.props.status.get('account'), this.context.router.history);
|
||||
this.props.onDirect(this.props.status.get("account"), this.context.router.history);
|
||||
};
|
||||
|
||||
handleMuteClick = () => {
|
||||
this.props.onMute(this.props.status.get('account'));
|
||||
this.props.onMute(this.props.status.get("account"));
|
||||
};
|
||||
|
||||
handleBlockClick = () => {
|
||||
@@ -166,9 +166,9 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
handleOpen = () => {
|
||||
let state = { ...this.context.router.history.location.state };
|
||||
if (state.mastodonModalKey) {
|
||||
this.context.router.history.replace(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
this.context.router.history.replace(`/@${this.props.status.getIn(["account", "acct"])}/${this.props.status.get("id")}`);
|
||||
} else {
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(['account', 'acct'])}/${this.props.status.get('id')}`);
|
||||
this.context.router.history.push(`/@${this.props.status.getIn(["account", "acct"])}/${this.props.status.get("id")}`);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,7 +185,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
};
|
||||
|
||||
handleCopy = () => {
|
||||
const url = this.props.status.get('url');
|
||||
const url = this.props.status.get("url");
|
||||
navigator.clipboard.writeText(url);
|
||||
};
|
||||
|
||||
@@ -201,26 +201,26 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
const { status, intl, withDismiss, withCounters, showReplyCount, scrollKey } = this.props;
|
||||
const { permissions, signedIn } = this.context.identity;
|
||||
|
||||
const mutingConversation = status.get('muted');
|
||||
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
|
||||
const pinnableStatus = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
|
||||
const writtenByMe = status.getIn(['account', 'id']) === me;
|
||||
const isRemote = status.getIn(['account', 'username']) !== status.getIn(['account', 'acct']);
|
||||
const mutingConversation = status.get("muted");
|
||||
const publicStatus = ["public", "unlisted"].includes(status.get("visibility"));
|
||||
const pinnableStatus = ["public", "unlisted", "private"].includes(status.get("visibility"));
|
||||
const writtenByMe = status.getIn(["account", "id"]) === me;
|
||||
const isRemote = status.getIn(["account", "username"]) !== status.getIn(["account", "acct"]);
|
||||
|
||||
let menu = [];
|
||||
let reblogIcon = 'retweet';
|
||||
let reblogIcon = "retweet";
|
||||
let replyIcon;
|
||||
let replyTitle;
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
|
||||
|
||||
if (publicStatus && isRemote) {
|
||||
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get('url') });
|
||||
menu.push({ text: intl.formatMessage(messages.openOriginalPage), href: status.get("url") });
|
||||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.copy), action: this.handleCopy });
|
||||
|
||||
if (publicStatus && 'share' in navigator) {
|
||||
if (publicStatus && "share" in navigator) {
|
||||
menu.push({ text: intl.formatMessage(messages.share), action: this.handleShareClick });
|
||||
}
|
||||
|
||||
@@ -232,7 +232,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
menu.push(null);
|
||||
|
||||
if (writtenByMe && pinnableStatus) {
|
||||
menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
menu.push({ text: intl.formatMessage(status.get("pinned") ? messages.unpin : messages.pin), action: this.handlePinClick });
|
||||
menu.push(null);
|
||||
}
|
||||
|
||||
@@ -246,8 +246,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.redraft), action: this.handleRedraftClick, dangerous: true });
|
||||
} else {
|
||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
|
||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
|
||||
menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(["account", "username"]) }), action: this.handleMentionClick });
|
||||
menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(["account", "username"]) }), action: this.handleDirectClick });
|
||||
menu.push(null);
|
||||
|
||||
if (!this.props.onFilter) {
|
||||
@@ -255,40 +255,40 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
menu.push(null);
|
||||
}
|
||||
|
||||
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(["account", "username"]) }), action: this.handleMuteClick, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(["account", "username"]) }), action: this.handleBlockClick, dangerous: true });
|
||||
menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(["account", "username"]) }), action: this.handleReport, dangerous: true });
|
||||
|
||||
if (((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS && (accountAdminLink || statusAdminLink)) || (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION)) {
|
||||
menu.push(null);
|
||||
if ((permissions & PERMISSION_MANAGE_USERS) === PERMISSION_MANAGE_USERS) {
|
||||
if (accountAdminLink !== undefined) {
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(['account', 'username']) }), href: accountAdminLink(status.getIn(['account', 'id'])) });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_account, { name: status.getIn(["account", "username"]) }), href: accountAdminLink(status.getIn(["account", "id"])) });
|
||||
}
|
||||
if (statusAdminLink !== undefined) {
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(['account', 'id']), status.get('id')) });
|
||||
menu.push({ text: intl.formatMessage(messages.admin_status), href: statusAdminLink(status.getIn(["account", "id"]), status.get("id")) });
|
||||
}
|
||||
}
|
||||
if (isRemote && (permissions & PERMISSION_MANAGE_FEDERATION) === PERMISSION_MANAGE_FEDERATION) {
|
||||
const domain = status.getIn(['account', 'acct']).split('@')[1];
|
||||
const domain = status.getIn(["account", "acct"]).split("@")[1];
|
||||
menu.push({ text: intl.formatMessage(messages.admin_domain, { domain: domain }), href: `/admin/instances/${domain}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status.get('in_reply_to_id', null) === null) {
|
||||
replyIcon = 'reply';
|
||||
if (status.get("in_reply_to_id", null) === null) {
|
||||
replyIcon = "reply";
|
||||
replyTitle = intl.formatMessage(messages.reply);
|
||||
} else {
|
||||
replyIcon = 'reply-all';
|
||||
replyIcon = "reply-all";
|
||||
replyTitle = intl.formatMessage(messages.replyAll);
|
||||
}
|
||||
|
||||
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
|
||||
const reblogPrivate = status.getIn(["account", "id"]) === me && status.get("visibility") === "private";
|
||||
|
||||
let reblogTitle = '';
|
||||
if (status.get('reblogged')) {
|
||||
let reblogTitle = "";
|
||||
if (status.get("reblogged")) {
|
||||
reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
|
||||
} else if (publicStatus) {
|
||||
reblogTitle = intl.formatMessage(messages.reblog);
|
||||
@@ -309,12 +309,12 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
title={replyTitle}
|
||||
icon={replyIcon}
|
||||
onClick={this.handleReplyClick}
|
||||
counter={showReplyCount ? status.get('replies_count') : undefined}
|
||||
counter={showReplyCount ? status.get("replies_count") : undefined}
|
||||
obfuscateCount
|
||||
/>
|
||||
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon={reblogIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
|
||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
|
||||
<IconButton className='status__action-bar-button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
|
||||
<IconButton className={classNames("status__action-bar-button", { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get("reblogged")} title={reblogTitle} icon={reblogIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get("reblogs_count") : undefined} />
|
||||
<IconButton className='status__action-bar-button star-icon' animate active={status.get("favourited")} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get("favourites_count") : undefined} />
|
||||
<IconButton className='status__action-bar-button bookmark-icon' disabled={!signedIn} active={status.get("bookmarked")} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} />
|
||||
|
||||
{filterButton}
|
||||
|
||||
@@ -331,8 +331,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<div className='status__action-bar-spacer' />
|
||||
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>
|
||||
<RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>}
|
||||
<a href={status.get("url")} className='status__relative-time' target='_blank' rel="noopener noreferrer">
|
||||
<RelativeTimestamp timestamp={status.get("created_at")} />{status.get("edited_at") && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get("edited_at"), { hour12: false, year: "numeric", month: "short", day: "2-digit", hour: "2-digit", minute: "2-digit" }) })}> *</abbr>}
|
||||
</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import { FormattedMessage, injectIntl } from 'react-intl';
|
||||
import { FormattedMessage, injectIntl } from "react-intl";
|
||||
|
||||
import classnames from 'classnames';
|
||||
import classnames from "classnames";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { connect } from 'react-redux';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { autoPlayGif } from 'flavours/glitch/initial_state';
|
||||
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
import { autoPlayGif } from "flavours/glitch/initial_state";
|
||||
import { decode as decodeIDNA } from "flavours/glitch/utils/idna";
|
||||
|
||||
import Permalink from './permalink';
|
||||
import Permalink from "./permalink";
|
||||
|
||||
const textMatchesTarget = (text, origin, host) => {
|
||||
return (text === origin || text === host
|
||||
|| text.startsWith(origin + '/') || text.startsWith(host + '/')
|
||||
|| 'www.' + text === host || ('www.' + text).startsWith(host + '/'));
|
||||
|| text.startsWith(origin + "/") || text.startsWith(host + "/")
|
||||
|| "www." + text === host || ("www." + text).startsWith(host + "/"));
|
||||
};
|
||||
|
||||
const isLinkMisleading = (link) => {
|
||||
@@ -29,30 +29,32 @@ const isLinkMisleading = (link) => {
|
||||
|
||||
const walk = (node) => {
|
||||
switch (node.nodeType) {
|
||||
case Node.TEXT_NODE:
|
||||
linkTextParts.push(node.textContent);
|
||||
break;
|
||||
case Node.ELEMENT_NODE:
|
||||
if (node.classList.contains('invisible')) return;
|
||||
const children = node.childNodes;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
walk(children[i]);
|
||||
}
|
||||
break;
|
||||
case Node.TEXT_NODE:
|
||||
linkTextParts.push(node.textContent);
|
||||
break;
|
||||
case Node.ELEMENT_NODE:
|
||||
if (node.classList.contains("invisible")) {
|
||||
return;
|
||||
}
|
||||
const children = node.childNodes;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
walk(children[i]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
walk(link);
|
||||
|
||||
const linkText = linkTextParts.join('');
|
||||
const linkText = linkTextParts.join("");
|
||||
const targetURL = new URL(link.href);
|
||||
|
||||
if (targetURL.protocol === 'magnet:') {
|
||||
return !linkText.startsWith('magnet:');
|
||||
if (targetURL.protocol === "magnet:") {
|
||||
return !linkText.startsWith("magnet:");
|
||||
}
|
||||
|
||||
if (targetURL.protocol === 'xmpp:') {
|
||||
return !(linkText === targetURL.href || 'xmpp:' + linkText === targetURL.href);
|
||||
if (targetURL.protocol === "xmpp:") {
|
||||
return !(linkText === targetURL.href || "xmpp:" + linkText === targetURL.href);
|
||||
}
|
||||
|
||||
// The following may not work with international domain names
|
||||
@@ -61,10 +63,10 @@ const isLinkMisleading = (link) => {
|
||||
}
|
||||
|
||||
// The link hasn't been recognized, maybe it features an international domain name
|
||||
const hostname = decodeIDNA(targetURL.hostname).normalize('NFKC');
|
||||
const hostname = decodeIDNA(targetURL.hostname).normalize("NFKC");
|
||||
const host = targetURL.host.replace(targetURL.hostname, hostname);
|
||||
const origin = targetURL.origin.replace(targetURL.host, host);
|
||||
const text = linkText.normalize('NFKC');
|
||||
const text = linkText.normalize("NFKC");
|
||||
return !(textMatchesTarget(text, origin, host) || textMatchesTarget(text.toLowerCase(), origin, host));
|
||||
};
|
||||
|
||||
@@ -93,7 +95,7 @@ class StatusContent extends PureComponent {
|
||||
|
||||
static defaultProps = {
|
||||
tagLinks: true,
|
||||
rewriteMentions: 'no',
|
||||
rewriteMentions: "no",
|
||||
};
|
||||
|
||||
state = {
|
||||
@@ -108,60 +110,64 @@ class StatusContent extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const links = node.querySelectorAll('a');
|
||||
const links = node.querySelectorAll("a");
|
||||
|
||||
for (var i = 0; i < links.length; ++i) {
|
||||
let link = links[i];
|
||||
if (link.classList.contains('status-link')) {
|
||||
if (link.classList.contains("status-link")) {
|
||||
continue;
|
||||
}
|
||||
link.classList.add('status-link');
|
||||
link.classList.add("status-link");
|
||||
|
||||
let mention = this.props.status.get('mentions').find(item => link.href === item.get('url'));
|
||||
let mention = this.props.status.get("mentions").find(item => link.href === item.get("url"));
|
||||
|
||||
if (mention) {
|
||||
link.addEventListener('click', this.onMentionClick.bind(this, mention), false);
|
||||
link.setAttribute('title', `@${mention.get('acct')}`);
|
||||
if (rewriteMentions !== 'no') {
|
||||
while (link.firstChild) link.removeChild(link.firstChild);
|
||||
link.appendChild(document.createTextNode('@'));
|
||||
const acctSpan = document.createElement('span');
|
||||
acctSpan.textContent = rewriteMentions === 'acct' ? mention.get('acct') : mention.get('username');
|
||||
link.addEventListener("click", this.onMentionClick.bind(this, mention), false);
|
||||
link.setAttribute("title", `@${mention.get("acct")}`);
|
||||
if (rewriteMentions !== "no") {
|
||||
while (link.firstChild) {
|
||||
link.removeChild(link.firstChild);
|
||||
}
|
||||
link.appendChild(document.createTextNode("@"));
|
||||
const acctSpan = document.createElement("span");
|
||||
acctSpan.textContent = rewriteMentions === "acct" ? mention.get("acct") : mention.get("username");
|
||||
link.appendChild(acctSpan);
|
||||
}
|
||||
} else if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) {
|
||||
link.addEventListener('click', this.onHashtagClick.bind(this, link.text), false);
|
||||
} else if (link.textContent[0] === "#" || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === "#")) {
|
||||
link.addEventListener("click", this.onHashtagClick.bind(this, link.text), false);
|
||||
} else {
|
||||
link.addEventListener('click', this.onLinkClick.bind(this), false);
|
||||
link.setAttribute('title', link.href);
|
||||
link.classList.add('unhandled-link');
|
||||
link.addEventListener("click", this.onLinkClick.bind(this), false);
|
||||
link.setAttribute("title", link.href);
|
||||
link.classList.add("unhandled-link");
|
||||
|
||||
link.setAttribute('target', '_blank');
|
||||
link.setAttribute('rel', 'noopener nofollow noreferrer');
|
||||
link.setAttribute("target", "_blank");
|
||||
link.setAttribute("rel", "noopener nofollow noreferrer");
|
||||
|
||||
try {
|
||||
if (tagLinks && isLinkMisleading(link)) {
|
||||
// Add a tag besides the link to display its origin
|
||||
|
||||
const url = new URL(link.href);
|
||||
const tag = document.createElement('span');
|
||||
tag.classList.add('link-origin-tag');
|
||||
const tag = document.createElement("span");
|
||||
tag.classList.add("link-origin-tag");
|
||||
switch (url.protocol) {
|
||||
case 'xmpp:':
|
||||
tag.textContent = `[${url.href}]`;
|
||||
break;
|
||||
case 'magnet:':
|
||||
tag.textContent = '(magnet)';
|
||||
break;
|
||||
default:
|
||||
tag.textContent = `[${url.host}]`;
|
||||
case "xmpp:":
|
||||
tag.textContent = `[${url.href}]`;
|
||||
break;
|
||||
case "magnet:":
|
||||
tag.textContent = "(magnet)";
|
||||
break;
|
||||
default:
|
||||
tag.textContent = `[${url.host}]`;
|
||||
}
|
||||
link.insertAdjacentText('beforeend', ' ');
|
||||
link.insertAdjacentElement('beforeend', tag);
|
||||
link.insertAdjacentText("beforeend", " ");
|
||||
link.insertAdjacentElement("beforeend", tag);
|
||||
}
|
||||
} catch (e) {
|
||||
// The URL is invalid, remove the href just to be safe
|
||||
if (tagLinks && e instanceof TypeError) link.removeAttribute('href');
|
||||
if (tagLinks && e instanceof TypeError) {
|
||||
link.removeAttribute("href");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -172,11 +178,11 @@ class StatusContent extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
||||
const emojis = currentTarget.querySelectorAll(".custom-emoji");
|
||||
|
||||
for (var i = 0; i < emojis.length; i++) {
|
||||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-original');
|
||||
emoji.src = emoji.getAttribute("data-original");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -185,11 +191,11 @@ class StatusContent extends PureComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
||||
const emojis = currentTarget.querySelectorAll(".custom-emoji");
|
||||
|
||||
for (var i = 0; i < emojis.length; i++) {
|
||||
let emoji = emojis[i];
|
||||
emoji.src = emoji.getAttribute('data-static');
|
||||
emoji.src = emoji.getAttribute("data-static");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -199,23 +205,27 @@ class StatusContent extends PureComponent {
|
||||
|
||||
componentDidUpdate () {
|
||||
this._updateStatusLinks();
|
||||
if (this.props.onUpdate) this.props.onUpdate();
|
||||
if (this.props.onUpdate) {
|
||||
this.props.onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
onLinkClick = (e) => {
|
||||
if (this.props.collapsed) {
|
||||
if (this.props.parseClick) this.props.parseClick(e);
|
||||
if (this.props.parseClick) {
|
||||
this.props.parseClick(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMentionClick = (mention, e) => {
|
||||
if (this.props.parseClick) {
|
||||
this.props.parseClick(e, `/@${mention.get('acct')}`);
|
||||
this.props.parseClick(e, `/@${mention.get("acct")}`);
|
||||
}
|
||||
};
|
||||
|
||||
onHashtagClick = (hashtag, e) => {
|
||||
hashtag = hashtag.replace(/^#/, '');
|
||||
hashtag = hashtag.replace(/^#/, "");
|
||||
|
||||
if (this.props.parseClick) {
|
||||
this.props.parseClick(e, `/tags/${hashtag}`);
|
||||
@@ -238,7 +248,7 @@ class StatusContent extends PureComponent {
|
||||
|
||||
let element = e.target;
|
||||
while (element !== e.currentTarget) {
|
||||
if (['button', 'video', 'a', 'label', 'canvas'].includes(element.localName) || element.getAttribute('role') === 'button') {
|
||||
if (["button", "video", "a", "label", "canvas"].includes(element.localName) || element.getAttribute("role") === "button") {
|
||||
return;
|
||||
}
|
||||
element = element.parentNode;
|
||||
@@ -283,28 +293,28 @@ class StatusContent extends PureComponent {
|
||||
|
||||
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
|
||||
|
||||
const content = { __html: status.getIn(['translation', 'contentHtml']) || status.get('contentHtml') };
|
||||
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
|
||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||
const classNames = classnames('status__content', {
|
||||
'status__content--with-action': parseClick && !disabled,
|
||||
'status__content--with-spoiler': status.get('spoiler_text').length > 0,
|
||||
const content = { __html: status.getIn(["translation", "contentHtml"]) || status.get("contentHtml") };
|
||||
const spoilerContent = { __html: status.getIn(["translation", "spoilerHtml"]) || status.get("spoilerHtml") };
|
||||
const language = status.getIn(["translation", "language"]) || status.get("language");
|
||||
const classNames = classnames("status__content", {
|
||||
"status__content--with-action": parseClick && !disabled,
|
||||
"status__content--with-spoiler": status.get("spoiler_text").length > 0,
|
||||
});
|
||||
|
||||
|
||||
if (status.get('spoiler_text').length > 0) {
|
||||
let mentionsPlaceholder = '';
|
||||
if (status.get("spoiler_text").length > 0) {
|
||||
let mentionsPlaceholder = "";
|
||||
|
||||
const mentionLinks = status.get('mentions').map(item => (
|
||||
const mentionLinks = status.get("mentions").map(item => (
|
||||
<Permalink
|
||||
to={`/@${item.get('acct')}`}
|
||||
href={item.get('url')}
|
||||
key={item.get('id')}
|
||||
to={`/@${item.get("acct")}`}
|
||||
href={item.get("url")}
|
||||
key={item.get("id")}
|
||||
className='mention'
|
||||
>
|
||||
@<span>{item.get('username')}</span>
|
||||
@<span>{item.get("username")}</span>
|
||||
</Permalink>
|
||||
)).reduce((aggregate, item) => [...aggregate, item, ' '], []);
|
||||
)).reduce((aggregate, item) => [...aggregate, item, " "], []);
|
||||
|
||||
let toggleText = null;
|
||||
if (hidden) {
|
||||
@@ -345,10 +355,10 @@ class StatusContent extends PureComponent {
|
||||
return (
|
||||
<div className={classNames} tabIndex={0} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
|
||||
<p
|
||||
style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
|
||||
style={{ marginBottom: hidden && status.get("mentions").isEmpty() ? "0px" : null }}
|
||||
>
|
||||
<span dangerouslySetInnerHTML={spoilerContent} className='translate' lang={language} />
|
||||
{' '}
|
||||
{" "}
|
||||
<button type='button' className='status__content__spoiler-link' onClick={this.handleSpoilerClick} aria-expanded={!hidden}>
|
||||
{toggleText}
|
||||
</button>
|
||||
@@ -356,7 +366,7 @@ class StatusContent extends PureComponent {
|
||||
|
||||
{mentionsPlaceholder}
|
||||
|
||||
<div className={`status__content__spoiler ${!hidden ? 'status__content__spoiler--visible' : ''}`}>
|
||||
<div className={`status__content__spoiler ${!hidden ? "status__content__spoiler--visible" : ""}`}>
|
||||
<div
|
||||
ref={this.setContentsRef}
|
||||
key={`contents-${tagLinks}`}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Package imports.
|
||||
import PropTypes from 'prop-types';
|
||||
import { PureComponent } from 'react';
|
||||
import PropTypes from "prop-types";
|
||||
import { PureComponent } from "react";
|
||||
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
// Mastodon imports.
|
||||
import { Avatar } from './avatar';
|
||||
import AvatarOverlay from './avatar_overlay';
|
||||
import { DisplayName } from './display_name';
|
||||
import { Avatar } from "./avatar";
|
||||
import AvatarOverlay from "./avatar_overlay";
|
||||
import { DisplayName } from "./display_name";
|
||||
|
||||
export default class StatusHeader extends PureComponent {
|
||||
|
||||
@@ -25,7 +25,7 @@ export default class StatusHeader extends PureComponent {
|
||||
|
||||
handleAccountClick = (e) => {
|
||||
const { status } = this.props;
|
||||
this.handleClick(status.getIn(['account', 'acct']), e);
|
||||
this.handleClick(status.getIn(["account", "acct"]), e);
|
||||
};
|
||||
|
||||
// Rendering.
|
||||
@@ -35,7 +35,7 @@ export default class StatusHeader extends PureComponent {
|
||||
friend,
|
||||
} = this.props;
|
||||
|
||||
const account = status.get('account');
|
||||
const account = status.get("account");
|
||||
|
||||
let statusAvatar;
|
||||
if (friend === undefined || friend === null) {
|
||||
@@ -47,7 +47,7 @@ export default class StatusHeader extends PureComponent {
|
||||
return (
|
||||
<div className='status__info__account'>
|
||||
<a
|
||||
href={account.get('url')}
|
||||
href={account.get("url")}
|
||||
target='_blank'
|
||||
className='status__avatar'
|
||||
onClick={this.handleAccountClick}
|
||||
@@ -56,7 +56,7 @@ export default class StatusHeader extends PureComponent {
|
||||
{statusAvatar}
|
||||
</a>
|
||||
<a
|
||||
href={account.get('url')}
|
||||
href={account.get("url")}
|
||||
target='_blank'
|
||||
className='status__display-name'
|
||||
onClick={this.handleAccountClick}
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
// Package imports.
|
||||
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 ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
|
||||
// Mastodon imports.
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { languages } from 'flavours/glitch/initial_state';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
import { languages } from "flavours/glitch/initial_state";
|
||||
|
||||
import { IconButton } from './icon_button';
|
||||
import VisibilityIcon from './status_visibility_icon';
|
||||
import { IconButton } from "./icon_button";
|
||||
import VisibilityIcon from "./status_visibility_icon";
|
||||
|
||||
// Messages for use with internationalization stuff.
|
||||
const messages = defineMessages({
|
||||
collapse: { id: 'status.collapse', defaultMessage: 'Collapse' },
|
||||
uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' },
|
||||
inReplyTo: { id: 'status.in_reply_to', defaultMessage: 'This toot is a reply' },
|
||||
previewCard: { id: 'status.has_preview_card', defaultMessage: 'Features an attached preview card' },
|
||||
pictures: { id: 'status.has_pictures', defaultMessage: 'Features attached pictures' },
|
||||
poll: { id: 'status.is_poll', defaultMessage: 'This toot is a poll' },
|
||||
video: { id: 'status.has_video', defaultMessage: 'Features attached videos' },
|
||||
audio: { id: 'status.has_audio', defaultMessage: 'Features attached audio files' },
|
||||
localOnly: { id: 'status.local_only', defaultMessage: 'Only visible from your instance' },
|
||||
collapse: { id: "status.collapse", defaultMessage: "Collapse" },
|
||||
uncollapse: { id: "status.uncollapse", defaultMessage: "Uncollapse" },
|
||||
inReplyTo: { id: "status.in_reply_to", defaultMessage: "This toot is a reply" },
|
||||
previewCard: { id: "status.has_preview_card", defaultMessage: "Features an attached preview card" },
|
||||
pictures: { id: "status.has_pictures", defaultMessage: "Features attached pictures" },
|
||||
poll: { id: "status.is_poll", defaultMessage: "This toot is a poll" },
|
||||
video: { id: "status.has_video", defaultMessage: "Features attached videos" },
|
||||
audio: { id: "status.has_audio", defaultMessage: "Features attached audio files" },
|
||||
localOnly: { id: "status.local_only", defaultMessage: "Only visible from your instance" },
|
||||
});
|
||||
|
||||
const LanguageIcon = ({ language }) => {
|
||||
if (!languages) return null;
|
||||
if (!languages) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const lang = languages.find((lang) => lang[0] === language);
|
||||
if (!lang) return null;
|
||||
if (!lang) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className='text-icon' title={`${lang[2]} (${lang[1]})`} aria-hidden='true'>
|
||||
@@ -69,11 +73,11 @@ class StatusIcons extends PureComponent {
|
||||
const { intl } = this.props;
|
||||
|
||||
const message = {
|
||||
'link': messages.previewCard,
|
||||
'picture-o': messages.pictures,
|
||||
'tasks': messages.poll,
|
||||
'video-camera': messages.video,
|
||||
'music': messages.audio,
|
||||
"link": messages.previewCard,
|
||||
"picture-o": messages.pictures,
|
||||
"tasks": messages.poll,
|
||||
"video-camera": messages.video,
|
||||
"music": messages.audio,
|
||||
}[mediaIcon];
|
||||
|
||||
return message && intl.formatMessage(message);
|
||||
@@ -105,8 +109,8 @@ class StatusIcons extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className='status__info__icons'>
|
||||
{settings.get('language') && status.get('language') && <LanguageIcon language={status.get('language')} />}
|
||||
{settings.get('reply') && status.get('in_reply_to_id', null) !== null ? (
|
||||
{settings.get("language") && status.get("language") && <LanguageIcon language={status.get("language")} />}
|
||||
{settings.get("reply") && status.get("in_reply_to_id", null) !== null ? (
|
||||
<Icon
|
||||
className='status__reply-icon'
|
||||
fixedWidth
|
||||
@@ -115,15 +119,15 @@ class StatusIcons extends PureComponent {
|
||||
title={intl.formatMessage(messages.inReplyTo)}
|
||||
/>
|
||||
) : null}
|
||||
{settings.get('local_only') && status.get('local_only') &&
|
||||
{settings.get("local_only") && status.get("local_only") &&
|
||||
<Icon
|
||||
fixedWidth
|
||||
id='home'
|
||||
aria-hidden='true'
|
||||
title={intl.formatMessage(messages.localOnly)}
|
||||
/>}
|
||||
{settings.get('media') && !!mediaIcons && mediaIcons.map(icon => this.renderIcon(icon))}
|
||||
{settings.get('visibility') && <VisibilityIcon visibility={status.get('visibility')} />}
|
||||
{settings.get("media") && !!mediaIcons && mediaIcons.map(icon => this.renderIcon(icon))}
|
||||
{settings.get("visibility") && <VisibilityIcon visibility={status.get("visibility")} />}
|
||||
{collapsible && (
|
||||
<IconButton
|
||||
className='status__collapse-button'
|
||||
|
||||
@@ -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 ImmutablePropTypes from "react-immutable-proptypes";
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import { debounce } from 'lodash';
|
||||
import { debounce } from "lodash";
|
||||
|
||||
import RegenerationIndicator from 'flavours/glitch/components/regeneration_indicator';
|
||||
import StatusContainer from 'flavours/glitch/containers/status_container';
|
||||
import RegenerationIndicator from "flavours/glitch/components/regeneration_indicator";
|
||||
import StatusContainer from "flavours/glitch/containers/status_container";
|
||||
|
||||
import { LoadGap } from './load_gap';
|
||||
import ScrollableList from './scrollable_list';
|
||||
import { LoadGap } from "./load_gap";
|
||||
import ScrollableList from "./scrollable_list";
|
||||
|
||||
export default class StatusList extends ImmutablePureComponent {
|
||||
|
||||
@@ -93,7 +93,7 @@ export default class StatusList extends ImmutablePureComponent {
|
||||
let scrollableContent = (isLoading || statusIds.size > 0) ? (
|
||||
statusIds.map((statusId, index) => statusId === null ? (
|
||||
<LoadGap
|
||||
key={'gap:' + statusIds.get(index + 1)}
|
||||
key={"gap:" + statusIds.get(index + 1)}
|
||||
disabled={isLoading}
|
||||
maxId={index > 0 ? statusIds.get(index - 1) : null}
|
||||
onClick={onLoadMore}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
// Package imports //
|
||||
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 ImmutablePropTypes from "react-immutable-proptypes";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { me } from 'flavours/glitch/initial_state';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
import { me } from "flavours/glitch/initial_state";
|
||||
|
||||
export default class StatusPrepend extends PureComponent {
|
||||
|
||||
@@ -20,7 +20,7 @@ export default class StatusPrepend extends PureComponent {
|
||||
|
||||
handleClick = (e) => {
|
||||
const { account, parseClick } = this.props;
|
||||
parseClick(e, `/@${account.get('acct')}`);
|
||||
parseClick(e, `/@${account.get("acct")}`);
|
||||
};
|
||||
|
||||
Message = () => {
|
||||
@@ -28,77 +28,77 @@ export default class StatusPrepend extends PureComponent {
|
||||
let link = (
|
||||
<a
|
||||
onClick={this.handleClick}
|
||||
href={account.get('url')}
|
||||
href={account.get("url")}
|
||||
className='status__display-name'
|
||||
>
|
||||
<b
|
||||
dangerouslySetInnerHTML={{
|
||||
__html : account.get('display_name_html') || account.get('username'),
|
||||
__html : account.get("display_name_html") || account.get("username"),
|
||||
}}
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
switch (type) {
|
||||
case 'featured':
|
||||
return (
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
|
||||
);
|
||||
case 'reblogged_by':
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} boosted'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
case 'favourite':
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.favourite'
|
||||
defaultMessage='{name} favorited your status'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
case 'reblog':
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.reblog'
|
||||
defaultMessage='{name} boosted your status'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
case 'status':
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.status'
|
||||
defaultMessage='{name} just posted'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
);
|
||||
case 'poll':
|
||||
if (me === account.get('id')) {
|
||||
case "featured":
|
||||
return (
|
||||
<FormattedMessage id='status.pinned' defaultMessage='Pinned post' />
|
||||
);
|
||||
case "reblogged_by":
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.own_poll'
|
||||
defaultMessage='Your poll has ended'
|
||||
id='status.reblogged_by'
|
||||
defaultMessage='{name} boosted'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
case "favourite":
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.poll'
|
||||
defaultMessage='A poll you have voted in has ended'
|
||||
id='notification.favourite'
|
||||
defaultMessage='{name} favorited your status'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
case "reblog":
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.reblog'
|
||||
defaultMessage='{name} boosted your status'
|
||||
values={{ name : link }}
|
||||
/>
|
||||
);
|
||||
case "status":
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.status'
|
||||
defaultMessage='{name} just posted'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
);
|
||||
case "poll":
|
||||
if (me === account.get("id")) {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.own_poll'
|
||||
defaultMessage='Your poll has ended'
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.poll'
|
||||
defaultMessage='A poll you have voted in has ended'
|
||||
/>
|
||||
);
|
||||
}
|
||||
case "update":
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.update'
|
||||
defaultMessage='{name} edited a post'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
case 'update':
|
||||
return (
|
||||
<FormattedMessage
|
||||
id='notification.update'
|
||||
defaultMessage='{name} edited a post'
|
||||
values={{ name: link }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
@@ -110,32 +110,32 @@ export default class StatusPrepend extends PureComponent {
|
||||
let iconId;
|
||||
|
||||
switch(type) {
|
||||
case 'favourite':
|
||||
iconId = 'star';
|
||||
break;
|
||||
case 'featured':
|
||||
iconId = 'thumb-tack';
|
||||
break;
|
||||
case 'poll':
|
||||
iconId = 'tasks';
|
||||
break;
|
||||
case 'reblog':
|
||||
case 'reblogged_by':
|
||||
iconId = 'retweet';
|
||||
break;
|
||||
case 'status':
|
||||
iconId = 'bell';
|
||||
break;
|
||||
case 'update':
|
||||
iconId = 'pencil';
|
||||
break;
|
||||
case "favourite":
|
||||
iconId = "star";
|
||||
break;
|
||||
case "featured":
|
||||
iconId = "thumb-tack";
|
||||
break;
|
||||
case "poll":
|
||||
iconId = "tasks";
|
||||
break;
|
||||
case "reblog":
|
||||
case "reblogged_by":
|
||||
iconId = "retweet";
|
||||
break;
|
||||
case "status":
|
||||
iconId = "bell";
|
||||
break;
|
||||
case "update":
|
||||
iconId = "pencil";
|
||||
break;
|
||||
}
|
||||
|
||||
return !type ? null : (
|
||||
<aside className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend' : 'notification__message'}>
|
||||
<div className={type === 'reblogged_by' || type === 'featured' ? 'status__prepend-icon-wrapper' : 'notification__favourite-icon-wrapper'}>
|
||||
<aside className={type === "reblogged_by" || type === "featured" ? "status__prepend" : "notification__message"}>
|
||||
<div className={type === "reblogged_by" || type === "featured" ? "status__prepend-icon-wrapper" : "notification__favourite-icon-wrapper"}>
|
||||
<Icon
|
||||
className={`status__prepend-icon ${type === 'favourite' ? 'star-icon' : ''}`}
|
||||
className={`status__prepend-icon ${type === "favourite" ? "star-icon" : ""}`}
|
||||
id={iconId}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
// Package imports //
|
||||
import PropTypes from 'prop-types';
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
import { defineMessages, injectIntl } from "react-intl";
|
||||
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
import ImmutablePureComponent from "react-immutable-pure-component";
|
||||
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { Icon } from "flavours/glitch/components/icon";
|
||||
|
||||
const messages = defineMessages({
|
||||
public: { id: 'privacy.public.short', defaultMessage: 'Public' },
|
||||
unlisted: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
|
||||
private: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
|
||||
direct: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
|
||||
public: { id: "privacy.public.short", defaultMessage: "Public" },
|
||||
unlisted: { id: "privacy.unlisted.short", defaultMessage: "Unlisted" },
|
||||
private: { id: "privacy.private.short", defaultMessage: "Followers only" },
|
||||
direct: { id: "privacy.direct.short", defaultMessage: "Mentioned people only" },
|
||||
});
|
||||
|
||||
class VisibilityIcon extends ImmutablePureComponent {
|
||||
@@ -26,10 +26,10 @@ class VisibilityIcon extends ImmutablePureComponent {
|
||||
const { withLabel, visibility, intl } = this.props;
|
||||
|
||||
const visibilityIcon = {
|
||||
public: 'globe',
|
||||
unlisted: 'unlock',
|
||||
private: 'lock',
|
||||
direct: 'envelope',
|
||||
public: "globe",
|
||||
unlisted: "unlock",
|
||||
private: "lock",
|
||||
direct: "envelope",
|
||||
}[visibility];
|
||||
|
||||
const label = intl.formatMessage(messages[visibility]);
|
||||
@@ -43,7 +43,7 @@ class VisibilityIcon extends ImmutablePureComponent {
|
||||
/>);
|
||||
|
||||
if (withLabel) {
|
||||
return (<span style={{ whiteSpace: 'nowrap' }}>{icon} {label}</span>);
|
||||
return (<span style={{ whiteSpace: "nowrap" }}>{icon} {label}</span>);
|
||||
} else {
|
||||
return icon;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import * as React from 'react';
|
||||
import React from "react";
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
interface Props {
|
||||
resource: JSX.Element;
|
||||
url: string;
|
||||
resource: React.JSX.Element,
|
||||
url: string,
|
||||
}
|
||||
|
||||
export const TimelineHint: React.FC<Props> = ({ resource, url }) => (
|
||||
|
||||
Reference in New Issue
Block a user