Rewrite <ShortNumber /> as FC and TS (#25492)
				
					
				
			This commit is contained in:
		@@ -9,7 +9,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			|||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
					import ImmutablePureComponent from 'react-immutable-pure-component';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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: {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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);
 | 
					 | 
				
			||||||
							
								
								
									
										90
									
								
								app/javascript/mastodon/components/short_number.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								app/javascript/mastodon/components/short_number.tsx
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -14,7 +14,7 @@ import Button from 'mastodon/components/button';
 | 
				
			|||||||
import { FollowersCounter, FollowingCounter, StatusesCounter } from 'mastodon/components/counters';
 | 
					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';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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 {
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user