Merge commit '4534498a8e43f59980ee56e9938efab8580c78c8' into glitch-soc/merge-upstream
commit
7e54a30f06
|
@ -8,15 +8,15 @@ import { Link } from 'react-router-dom';
|
||||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { counterRenderer } from 'mastodon/components/common_counter';
|
|
||||||
import { EmptyAccount } from 'mastodon/components/empty_account';
|
import { EmptyAccount } from 'mastodon/components/empty_account';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { VerifiedBadge } from 'mastodon/components/verified_badge';
|
import { VerifiedBadge } from 'mastodon/components/verified_badge';
|
||||||
|
|
||||||
import { me } from '../initial_state';
|
import { me } from '../initial_state';
|
||||||
|
|
||||||
import { Avatar } from './avatar';
|
import { Avatar } from './avatar';
|
||||||
import Button from './button';
|
import Button from './button';
|
||||||
|
import { FollowersCounter } from './counters';
|
||||||
import { DisplayName } from './display_name';
|
import { DisplayName } from './display_name';
|
||||||
import { IconButton } from './icon_button';
|
import { IconButton } from './icon_button';
|
||||||
import { RelativeTimestamp } from './relative_timestamp';
|
import { RelativeTimestamp } from './relative_timestamp';
|
||||||
|
@ -160,7 +160,7 @@ class Account extends ImmutablePureComponent {
|
||||||
<DisplayName account={account} />
|
<DisplayName account={account} />
|
||||||
{!minimal && (
|
{!minimal && (
|
||||||
<div className='account__details'>
|
<div className='account__details'>
|
||||||
<ShortNumber value={account.get('followers_count')} renderer={counterRenderer('followers')} /> {verification} {muteTimeRemaining}
|
<ShortNumber value={account.get('followers_count')} renderer={FollowersCounter} /> {verification} {muteTimeRemaining}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,7 +4,7 @@ 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) => {
|
const obfuscatedCount = (count: number) => {
|
||||||
if (count < 0) {
|
if (count < 0) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { FormattedMessage } from 'react-intl';
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tag: {
|
tag: {
|
||||||
|
|
|
@ -1,60 +0,0 @@
|
||||||
// @ts-check
|
|
||||||
import { FormattedMessage } from 'react-intl';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns custom renderer for one of the common counter types
|
|
||||||
* @param {"statuses" | "following" | "followers"} counterType
|
|
||||||
* Type of the counter
|
|
||||||
* @param {boolean} isBold Whether display number must be displayed in bold
|
|
||||||
* @returns {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element}
|
|
||||||
* Renderer function
|
|
||||||
* @throws If counterType is not covered by this function
|
|
||||||
*/
|
|
||||||
export function counterRenderer(counterType, isBold = true) {
|
|
||||||
/**
|
|
||||||
* @type {(displayNumber: JSX.Element) => JSX.Element}
|
|
||||||
*/
|
|
||||||
const renderCounter = isBold
|
|
||||||
? (displayNumber) => <strong>{displayNumber}</strong>
|
|
||||||
: (displayNumber) => displayNumber;
|
|
||||||
|
|
||||||
switch (counterType) {
|
|
||||||
case 'statuses': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.statuses_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'following': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.following_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case 'followers': {
|
|
||||||
return (displayNumber, pluralReady) => (
|
|
||||||
<FormattedMessage
|
|
||||||
id='account.followers_counter'
|
|
||||||
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
|
||||||
values={{
|
|
||||||
count: pluralReady,
|
|
||||||
counter: renderCounter(displayNumber),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
default: throw Error(`Incorrect counter name: ${counterType}. Ensure it accepted by commonCounter function`);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
export const StatusesCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.statuses_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Post} other {{counter} Posts}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FollowingCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.following_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Following} other {{counter} Following}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FollowersCounter = (
|
||||||
|
displayNumber: React.ReactNode,
|
||||||
|
pluralReady: number
|
||||||
|
) => (
|
||||||
|
<FormattedMessage
|
||||||
|
id='account.followers_counter'
|
||||||
|
defaultMessage='{count, plural, one {{counter} Follower} other {{counter} Followers}}'
|
||||||
|
values={{
|
||||||
|
count: pluralReady,
|
||||||
|
counter: <strong>{displayNumber}</strong>,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
|
@ -1,55 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { PureComponent } from 'react';
|
|
||||||
|
|
||||||
import { injectIntl, defineMessages } from 'react-intl';
|
|
||||||
|
|
||||||
import { bannerSettings } from 'mastodon/settings';
|
|
||||||
|
|
||||||
import { IconButton } from './icon_button';
|
|
||||||
|
|
||||||
const messages = defineMessages({
|
|
||||||
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
|
||||||
});
|
|
||||||
|
|
||||||
class DismissableBanner extends PureComponent {
|
|
||||||
|
|
||||||
static propTypes = {
|
|
||||||
id: PropTypes.string.isRequired,
|
|
||||||
children: PropTypes.node,
|
|
||||||
intl: PropTypes.object.isRequired,
|
|
||||||
};
|
|
||||||
|
|
||||||
state = {
|
|
||||||
visible: !bannerSettings.get(this.props.id),
|
|
||||||
};
|
|
||||||
|
|
||||||
handleDismiss = () => {
|
|
||||||
const { id } = this.props;
|
|
||||||
this.setState({ visible: false }, () => bannerSettings.set(id, true));
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
|
||||||
const { visible } = this.state;
|
|
||||||
|
|
||||||
if (!visible) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { children, intl } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='dismissable-banner'>
|
|
||||||
<div className='dismissable-banner__message'>
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className='dismissable-banner__action'>
|
|
||||||
<IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default injectIntl(DismissableBanner);
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
import type { PropsWithChildren } from 'react';
|
||||||
|
import { useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
|
||||||
|
import { bannerSettings } from 'mastodon/settings';
|
||||||
|
|
||||||
|
import { IconButton } from './icon_button';
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' },
|
||||||
|
});
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
|
||||||
|
id,
|
||||||
|
children,
|
||||||
|
}) => {
|
||||||
|
const [visible, setVisible] = useState(!bannerSettings.get(id));
|
||||||
|
const intl = useIntl();
|
||||||
|
|
||||||
|
const handleDismiss = useCallback(() => {
|
||||||
|
setVisible(false);
|
||||||
|
bannerSettings.set(id, true);
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='dismissable-banner'>
|
||||||
|
<div className='dismissable-banner__message'>{children}</div>
|
||||||
|
|
||||||
|
<div className='dismissable-banner__action'>
|
||||||
|
<IconButton
|
||||||
|
icon='times'
|
||||||
|
title={intl.formatMessage(messages.dismiss)}
|
||||||
|
onClick={handleDismiss}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
|
@ -11,7 +11,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
|
|
||||||
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
import { Sparklines, SparklinesCurve } from 'react-sparklines';
|
||||||
|
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
|
|
||||||
class SilentErrorBoundary extends Component {
|
class SilentErrorBoundary extends Component {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchServer } from 'mastodon/actions/server';
|
import { fetchServer } from 'mastodon/actions/server';
|
||||||
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
|
import { ServerHeroImage } from 'mastodon/components/server_hero_image';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
import Account from 'mastodon/containers/account_container';
|
import Account from 'mastodon/containers/account_container';
|
||||||
import { domain } from 'mastodon/initial_state';
|
import { domain } from 'mastodon/initial_state';
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import { FormattedMessage, FormattedNumber } from 'react-intl';
|
|
||||||
|
|
||||||
import { toShortNumber, pluralReady, DECIMAL_UNITS } from '../utils/numbers';
|
|
||||||
|
|
||||||
// @ts-check
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @callback ShortNumberRenderer
|
|
||||||
* @param {JSX.Element} displayNumber Number to display
|
|
||||||
* @param {number} pluralReady Number used for pluralization
|
|
||||||
* @returns {JSX.Element} Final render of number
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} ShortNumberProps
|
|
||||||
* @property {number} value Number to display in short variant
|
|
||||||
* @property {ShortNumberRenderer} [renderer]
|
|
||||||
* Custom renderer for numbers, provided as a prop. If another renderer
|
|
||||||
* passed as a child of this component, this prop won't be used.
|
|
||||||
* @property {ShortNumberRenderer} [children]
|
|
||||||
* Custom renderer for numbers, provided as a child. If another renderer
|
|
||||||
* passed as a prop of this component, this one will be used instead.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component that renders short big number to a shorter version
|
|
||||||
* @param {ShortNumberProps} param0 Props for the component
|
|
||||||
* @returns {JSX.Element} Rendered number
|
|
||||||
*/
|
|
||||||
function ShortNumber({ value, renderer, children }) {
|
|
||||||
const shortNumber = toShortNumber(value);
|
|
||||||
const [, division] = shortNumber;
|
|
||||||
|
|
||||||
if (children != null && renderer != null) {
|
|
||||||
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 != null ? children : renderer;
|
|
||||||
|
|
||||||
const displayNumber = <ShortNumberCounter value={shortNumber} />;
|
|
||||||
|
|
||||||
return customRenderer != null
|
|
||||||
? customRenderer(displayNumber, pluralReady(value, division))
|
|
||||||
: displayNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortNumber.propTypes = {
|
|
||||||
value: PropTypes.number.isRequired,
|
|
||||||
renderer: PropTypes.func,
|
|
||||||
children: PropTypes.func,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {object} ShortNumberCounterProps
|
|
||||||
* @property {import('../utils/number').ShortNumber} value Short number
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renders short number into corresponding localizable react fragment
|
|
||||||
* @param {ShortNumberCounterProps} param0 Props for the component
|
|
||||||
* @returns {JSX.Element} FormattedMessage ready to be embedded in code
|
|
||||||
*/
|
|
||||||
function ShortNumberCounter({ value }) {
|
|
||||||
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
|
||||||
|
|
||||||
const count = (
|
|
||||||
<FormattedNumber
|
|
||||||
value={rawNumber}
|
|
||||||
maximumFractionDigits={maxFractionDigits}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
|
|
||||||
let values = { count, rawNumber };
|
|
||||||
|
|
||||||
switch (unit) {
|
|
||||||
case DECIMAL_UNITS.THOUSAND: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.thousand'
|
|
||||||
defaultMessage='{count}K'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DECIMAL_UNITS.MILLION: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.million'
|
|
||||||
defaultMessage='{count}M'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
case DECIMAL_UNITS.BILLION: {
|
|
||||||
return (
|
|
||||||
<FormattedMessage
|
|
||||||
id='units.short.billion'
|
|
||||||
defaultMessage='{count}B'
|
|
||||||
values={values}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Not sure if we should go farther - @Sasha-Sorokin
|
|
||||||
default: return count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortNumberCounter.propTypes = {
|
|
||||||
value: PropTypes.arrayOf(PropTypes.number),
|
|
||||||
};
|
|
||||||
|
|
||||||
export default memo(ShortNumber);
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
import { memo } from 'react';
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
interface ShortNumberCounterProps {
|
||||||
|
value: number[];
|
||||||
|
}
|
||||||
|
const ShortNumberCounter: React.FC<ShortNumberCounterProps> = ({ value }) => {
|
||||||
|
const [rawNumber, unit, maxFractionDigits = 0] = value;
|
||||||
|
|
||||||
|
const count = (
|
||||||
|
<FormattedNumber
|
||||||
|
value={rawNumber}
|
||||||
|
maximumFractionDigits={maxFractionDigits}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const values = { count, rawNumber };
|
||||||
|
|
||||||
|
switch (unit) {
|
||||||
|
case DECIMAL_UNITS.THOUSAND: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.thousand'
|
||||||
|
defaultMessage='{count}K'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case DECIMAL_UNITS.MILLION: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.million'
|
||||||
|
defaultMessage='{count}M'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
case DECIMAL_UNITS.BILLION: {
|
||||||
|
return (
|
||||||
|
<FormattedMessage
|
||||||
|
id='units.short.billion'
|
||||||
|
defaultMessage='{count}B'
|
||||||
|
values={values}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Not sure if we should go farther - @Sasha-Sorokin
|
||||||
|
default:
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
};
|
|
@ -44,7 +44,7 @@ class TranslateButton extends PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button className='status__content__read-more-button' onClick={onClick}>
|
<button className='status__content__translate-button' onClick={onClick}>
|
||||||
<FormattedMessage id='status.translate' defaultMessage='Translate' />
|
<FormattedMessage id='status.translate' defaultMessage='Translate' />
|
||||||
</button>
|
</button>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,10 +11,10 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
|
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import Button from 'mastodon/components/button';
|
import Button from 'mastodon/components/button';
|
||||||
import { counterRenderer } from 'mastodon/components/common_counter';
|
import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { IconButton } from 'mastodon/components/icon_button';
|
import { IconButton } from 'mastodon/components/icon_button';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
import DropdownMenuContainer from 'mastodon/containers/dropdown_menu_container';
|
||||||
import { autoPlayGif, me, domain } from 'mastodon/initial_state';
|
import { autoPlayGif, me, domain } from 'mastodon/initial_state';
|
||||||
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions';
|
||||||
|
@ -451,21 +451,21 @@ class Header extends ImmutablePureComponent {
|
||||||
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/@${account.get('acct')}`} title={intl.formatNumber(account.get('statuses_count'))}>
|
<NavLink isActive={this.isStatusesPageActive} activeClassName='active' to={`/@${account.get('acct')}`} title={intl.formatNumber(account.get('statuses_count'))}>
|
||||||
<ShortNumber
|
<ShortNumber
|
||||||
value={account.get('statuses_count')}
|
value={account.get('statuses_count')}
|
||||||
renderer={counterRenderer('statuses')}
|
renderer={StatusesCounter}
|
||||||
/>
|
/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink exact activeClassName='active' to={`/@${account.get('acct')}/following`} title={intl.formatNumber(account.get('following_count'))}>
|
<NavLink exact activeClassName='active' to={`/@${account.get('acct')}/following`} title={intl.formatNumber(account.get('following_count'))}>
|
||||||
<ShortNumber
|
<ShortNumber
|
||||||
value={account.get('following_count')}
|
value={account.get('following_count')}
|
||||||
renderer={counterRenderer('following')}
|
renderer={FollowingCounter}
|
||||||
/>
|
/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<NavLink exact activeClassName='active' to={`/@${account.get('acct')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
|
<NavLink exact activeClassName='active' to={`/@${account.get('acct')}/followers`} title={intl.formatNumber(account.get('followers_count'))}>
|
||||||
<ShortNumber
|
<ShortNumber
|
||||||
value={account.get('followers_count')}
|
value={account.get('followers_count')}
|
||||||
renderer={counterRenderer('followers')}
|
renderer={FollowersCounter}
|
||||||
/>
|
/>
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { domain } from 'mastodon/initial_state';
|
import { domain } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { openModal } from 'mastodon/actions/modal';
|
||||||
import { Avatar } from 'mastodon/components/avatar';
|
import { Avatar } from 'mastodon/components/avatar';
|
||||||
import Button from 'mastodon/components/button';
|
import Button from 'mastodon/components/button';
|
||||||
import { DisplayName } from 'mastodon/components/display_name';
|
import { DisplayName } from 'mastodon/components/display_name';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';
|
import { autoPlayGif, me, unfollowModal } from 'mastodon/initial_state';
|
||||||
import { makeGetAccount } from 'mastodon/selectors';
|
import { makeGetAccount } from 'mastodon/selectors';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import classNames from 'classnames';
|
||||||
|
|
||||||
import { Blurhash } from 'mastodon/components/blurhash';
|
import { Blurhash } from 'mastodon/components/blurhash';
|
||||||
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
import { accountsCountRenderer } from 'mastodon/components/hashtag';
|
||||||
import ShortNumber from 'mastodon/components/short_number';
|
import { ShortNumber } from 'mastodon/components/short_number';
|
||||||
import { Skeleton } from 'mastodon/components/skeleton';
|
import { Skeleton } from 'mastodon/components/skeleton';
|
||||||
|
|
||||||
export default class Story extends PureComponent {
|
export default class Story extends PureComponent {
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchTrendingLinks } from 'mastodon/actions/trends';
|
import { fetchTrendingLinks } from 'mastodon/actions/trends';
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
|
|
||||||
import Story from './components/story';
|
import Story from './components/story';
|
||||||
|
|
|
@ -9,7 +9,7 @@ import { connect } from 'react-redux';
|
||||||
import { debounce } from 'lodash';
|
import { debounce } from 'lodash';
|
||||||
|
|
||||||
import { fetchTrendingStatuses, expandTrendingStatuses } from 'mastodon/actions/trends';
|
import { fetchTrendingStatuses, expandTrendingStatuses } from 'mastodon/actions/trends';
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import StatusList from 'mastodon/components/status_list';
|
import StatusList from 'mastodon/components/status_list';
|
||||||
import { getStatusList } from 'mastodon/selectors';
|
import { getStatusList } from 'mastodon/selectors';
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { fetchTrendingHashtags } from 'mastodon/actions/trends';
|
import { fetchTrendingHashtags } from 'mastodon/actions/trends';
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag';
|
||||||
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
import { LoadingIndicator } from 'mastodon/components/loading_indicator';
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { addColumn } from 'mastodon/actions/columns';
|
||||||
import { changeSetting } from 'mastodon/actions/settings';
|
import { changeSetting } from 'mastodon/actions/settings';
|
||||||
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
|
import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming';
|
||||||
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
|
import { expandPublicTimeline, expandCommunityTimeline } from 'mastodon/actions/timelines';
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import initialState, { domain } from 'mastodon/initial_state';
|
import initialState, { domain } from 'mastodon/initial_state';
|
||||||
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
import { useAppDispatch, useAppSelector } from 'mastodon/store';
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { FormattedMessage } from 'react-intl';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
import background from 'mastodon/../images/friends-cropped.png';
|
import background from 'mastodon/../images/friends-cropped.png';
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
|
|
||||||
|
|
||||||
export const ExplorePrompt = () => (
|
export const ExplorePrompt = () => (
|
||||||
|
|
|
@ -7,7 +7,7 @@ import { Helmet } from 'react-helmet';
|
||||||
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import DismissableBanner from 'mastodon/components/dismissable_banner';
|
import { DismissableBanner } from 'mastodon/components/dismissable_banner';
|
||||||
import { domain } from 'mastodon/initial_state';
|
import { domain } from 'mastodon/initial_state';
|
||||||
|
|
||||||
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
import { addColumn, removeColumn, moveColumn } from '../../actions/columns';
|
||||||
|
|
|
@ -15,7 +15,8 @@
|
||||||
.status__content a,
|
.status__content a,
|
||||||
.link-footer a,
|
.link-footer a,
|
||||||
.reply-indicator__content a,
|
.reply-indicator__content a,
|
||||||
.status__content__read-more-button {
|
.status__content__read-more-button,
|
||||||
|
.status__content__translate-button {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
|
|
|
@ -981,7 +981,8 @@ body > [data-popper-placement] {
|
||||||
max-height: 22px * 15; // 15 lines is roughly above 500 characters
|
max-height: 22px * 15; // 15 lines is roughly above 500 characters
|
||||||
}
|
}
|
||||||
|
|
||||||
.status__content__read-more-button {
|
.status__content__read-more-button,
|
||||||
|
.status__content__translate-button {
|
||||||
display: block;
|
display: block;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
line-height: 22px;
|
line-height: 22px;
|
||||||
|
|
|
@ -18,6 +18,9 @@
|
||||||
},
|
},
|
||||||
"user_input": "id",
|
"user_input": "id",
|
||||||
"confidence": "Weak",
|
"confidence": "Weak",
|
||||||
|
"cwe_id": [
|
||||||
|
89
|
||||||
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -38,26 +41,9 @@
|
||||||
},
|
},
|
||||||
"user_input": "ids.join(\",\")",
|
"user_input": "ids.join(\",\")",
|
||||||
"confidence": "Weak",
|
"confidence": "Weak",
|
||||||
"note": ""
|
"cwe_id": [
|
||||||
},
|
89
|
||||||
{
|
],
|
||||||
"warning_type": "Redirect",
|
|
||||||
"warning_code": 18,
|
|
||||||
"fingerprint": "5fad11cd67f905fab9b1d5739d01384a1748ebe78c5af5ac31518201925265a7",
|
|
||||||
"check_name": "Redirect",
|
|
||||||
"message": "Possible unprotected redirect",
|
|
||||||
"file": "app/controllers/remote_interaction_controller.rb",
|
|
||||||
"line": 24,
|
|
||||||
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
|
||||||
"code": "redirect_to(RemoteFollow.new(resource_params).interact_address_for(Status.find(params[:id])))",
|
|
||||||
"render_path": null,
|
|
||||||
"location": {
|
|
||||||
"type": "method",
|
|
||||||
"class": "RemoteInteractionController",
|
|
||||||
"method": "create"
|
|
||||||
},
|
|
||||||
"user_input": "RemoteFollow.new(resource_params).interact_address_for(Status.find(params[:id]))",
|
|
||||||
"confidence": "High",
|
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -88,6 +74,9 @@
|
||||||
},
|
},
|
||||||
"user_input": "(Unresolved Model).new.strike",
|
"user_input": "(Unresolved Model).new.strike",
|
||||||
"confidence": "Weak",
|
"confidence": "Weak",
|
||||||
|
"cwe_id": [
|
||||||
|
79
|
||||||
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -108,26 +97,9 @@
|
||||||
},
|
},
|
||||||
"user_input": "SecureRandom.hex(16)",
|
"user_input": "SecureRandom.hex(16)",
|
||||||
"confidence": "Medium",
|
"confidence": "Medium",
|
||||||
"note": ""
|
"cwe_id": [
|
||||||
},
|
89
|
||||||
{
|
],
|
||||||
"warning_type": "Mass Assignment",
|
|
||||||
"warning_code": 105,
|
|
||||||
"fingerprint": "7631e93d0099506e7c3e5c91ba8d88523b00a41a0834ae30031a5a4e8bb3020a",
|
|
||||||
"check_name": "PermitAttributes",
|
|
||||||
"message": "Potentially dangerous key allowed for mass assignment",
|
|
||||||
"file": "app/controllers/api/v2/search_controller.rb",
|
|
||||||
"line": 28,
|
|
||||||
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
|
||||||
"code": "params.permit(:type, :offset, :min_id, :max_id, :account_id)",
|
|
||||||
"render_path": null,
|
|
||||||
"location": {
|
|
||||||
"type": "method",
|
|
||||||
"class": "Api::V2::SearchController",
|
|
||||||
"method": "search_params"
|
|
||||||
},
|
|
||||||
"user_input": ":account_id",
|
|
||||||
"confidence": "High",
|
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -137,7 +109,7 @@
|
||||||
"check_name": "PermitAttributes",
|
"check_name": "PermitAttributes",
|
||||||
"message": "Potentially dangerous key allowed for mass assignment",
|
"message": "Potentially dangerous key allowed for mass assignment",
|
||||||
"file": "app/controllers/api/v1/admin/reports_controller.rb",
|
"file": "app/controllers/api/v1/admin/reports_controller.rb",
|
||||||
"line": 90,
|
"line": 88,
|
||||||
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
||||||
"code": "params.permit(:resolved, :account_id, :target_account_id)",
|
"code": "params.permit(:resolved, :account_id, :target_account_id)",
|
||||||
"render_path": null,
|
"render_path": null,
|
||||||
|
@ -148,6 +120,9 @@
|
||||||
},
|
},
|
||||||
"user_input": ":account_id",
|
"user_input": ":account_id",
|
||||||
"confidence": "High",
|
"confidence": "High",
|
||||||
|
"cwe_id": [
|
||||||
|
915
|
||||||
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -157,7 +132,7 @@
|
||||||
"check_name": "PermitAttributes",
|
"check_name": "PermitAttributes",
|
||||||
"message": "Potentially dangerous key allowed for mass assignment",
|
"message": "Potentially dangerous key allowed for mass assignment",
|
||||||
"file": "app/controllers/api/v1/notifications_controller.rb",
|
"file": "app/controllers/api/v1/notifications_controller.rb",
|
||||||
"line": 81,
|
"line": 77,
|
||||||
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
"link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/",
|
||||||
"code": "params.permit(:account_id, :types => ([]), :exclude_types => ([]))",
|
"code": "params.permit(:account_id, :types => ([]), :exclude_types => ([]))",
|
||||||
"render_path": null,
|
"render_path": null,
|
||||||
|
@ -168,26 +143,9 @@
|
||||||
},
|
},
|
||||||
"user_input": ":account_id",
|
"user_input": ":account_id",
|
||||||
"confidence": "High",
|
"confidence": "High",
|
||||||
"note": ""
|
"cwe_id": [
|
||||||
},
|
915
|
||||||
{
|
],
|
||||||
"warning_type": "Redirect",
|
|
||||||
"warning_code": 18,
|
|
||||||
"fingerprint": "ba568ac09683f98740f663f3d850c31785900215992e8c090497d359a2563d50",
|
|
||||||
"check_name": "Redirect",
|
|
||||||
"message": "Possible unprotected redirect",
|
|
||||||
"file": "app/controllers/remote_follow_controller.rb",
|
|
||||||
"line": 21,
|
|
||||||
"link": "https://brakemanscanner.org/docs/warning_types/redirect/",
|
|
||||||
"code": "redirect_to(RemoteFollow.new(resource_params).subscribe_address_for(@account))",
|
|
||||||
"render_path": null,
|
|
||||||
"location": {
|
|
||||||
"type": "method",
|
|
||||||
"class": "RemoteFollowController",
|
|
||||||
"method": "create"
|
|
||||||
},
|
|
||||||
"user_input": "RemoteFollow.new(resource_params).subscribe_address_for(@account)",
|
|
||||||
"confidence": "High",
|
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -218,6 +176,9 @@
|
||||||
},
|
},
|
||||||
"user_input": "(Unresolved Model).new.url",
|
"user_input": "(Unresolved Model).new.url",
|
||||||
"confidence": "Weak",
|
"confidence": "Weak",
|
||||||
|
"cwe_id": [
|
||||||
|
79
|
||||||
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -238,9 +199,12 @@
|
||||||
},
|
},
|
||||||
"user_input": ":account_id",
|
"user_input": ":account_id",
|
||||||
"confidence": "High",
|
"confidence": "High",
|
||||||
|
"cwe_id": [
|
||||||
|
915
|
||||||
|
],
|
||||||
"note": ""
|
"note": ""
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"updated": "2022-03-22 07:48:32 +0100",
|
"updated": "2023-07-05 14:34:42 -0400",
|
||||||
"brakeman_version": "5.2.1"
|
"brakeman_version": "5.4.1"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue