diff --git a/app/javascript/mastodon/components/account.jsx b/app/javascript/mastodon/components/account.jsx
index dd5aff1d8e..fd5ea60407 100644
--- a/app/javascript/mastodon/components/account.jsx
+++ b/app/javascript/mastodon/components/account.jsx
@@ -8,15 +8,15 @@ import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
-import { counterRenderer } from 'mastodon/components/common_counter';
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 { me } from '../initial_state';
import { Avatar } from './avatar';
import Button from './button';
+import { FollowersCounter } from './counters';
import { DisplayName } from './display_name';
import { IconButton } from './icon_button';
import { RelativeTimestamp } from './relative_timestamp';
@@ -160,7 +160,7 @@ class Account extends ImmutablePureComponent {
{!minimal && (
- {verification} {muteTimeRemaining}
+ {verification} {muteTimeRemaining}
)}
diff --git a/app/javascript/mastodon/components/animated_number.tsx b/app/javascript/mastodon/components/animated_number.tsx
index ad985a29ea..3122d6421e 100644
--- a/app/javascript/mastodon/components/animated_number.tsx
+++ b/app/javascript/mastodon/components/animated_number.tsx
@@ -4,7 +4,7 @@ import { TransitionMotion, spring } from 'react-motion';
import { reduceMotion } from '../initial_state';
-import ShortNumber from './short_number';
+import { ShortNumber } from './short_number';
const obfuscatedCount = (count: number) => {
if (count < 0) {
diff --git a/app/javascript/mastodon/components/autosuggest_hashtag.tsx b/app/javascript/mastodon/components/autosuggest_hashtag.tsx
index c6798054db..59d66ec878 100644
--- a/app/javascript/mastodon/components/autosuggest_hashtag.tsx
+++ b/app/javascript/mastodon/components/autosuggest_hashtag.tsx
@@ -1,6 +1,6 @@
import { FormattedMessage } from 'react-intl';
-import ShortNumber from 'mastodon/components/short_number';
+import { ShortNumber } from 'mastodon/components/short_number';
interface Props {
tag: {
diff --git a/app/javascript/mastodon/components/common_counter.jsx b/app/javascript/mastodon/components/common_counter.jsx
deleted file mode 100644
index 23e1f22638..0000000000
--- a/app/javascript/mastodon/components/common_counter.jsx
+++ /dev/null
@@ -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) => {displayNumber}
- : (displayNumber) => displayNumber;
-
- switch (counterType) {
- case 'statuses': {
- return (displayNumber, pluralReady) => (
-
- );
- }
- case 'following': {
- return (displayNumber, pluralReady) => (
-
- );
- }
- case 'followers': {
- return (displayNumber, pluralReady) => (
-
- );
- }
- default: throw Error(`Incorrect counter name: ${counterType}. Ensure it accepted by commonCounter function`);
- }
-}
diff --git a/app/javascript/mastodon/components/counters.tsx b/app/javascript/mastodon/components/counters.tsx
new file mode 100644
index 0000000000..e0c818f247
--- /dev/null
+++ b/app/javascript/mastodon/components/counters.tsx
@@ -0,0 +1,45 @@
+import React from 'react';
+
+import { FormattedMessage } from 'react-intl';
+
+export const StatusesCounter = (
+ displayNumber: React.ReactNode,
+ pluralReady: number
+) => (
+ {displayNumber},
+ }}
+ />
+);
+
+export const FollowingCounter = (
+ displayNumber: React.ReactNode,
+ pluralReady: number
+) => (
+ {displayNumber},
+ }}
+ />
+);
+
+export const FollowersCounter = (
+ displayNumber: React.ReactNode,
+ pluralReady: number
+) => (
+ {displayNumber},
+ }}
+ />
+);
diff --git a/app/javascript/mastodon/components/dismissable_banner.jsx b/app/javascript/mastodon/components/dismissable_banner.jsx
deleted file mode 100644
index 5aecc88b16..0000000000
--- a/app/javascript/mastodon/components/dismissable_banner.jsx
+++ /dev/null
@@ -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 (
-
-
- {children}
-
-
-
-
-
-
- );
- }
-
-}
-
-export default injectIntl(DismissableBanner);
diff --git a/app/javascript/mastodon/components/dismissable_banner.tsx b/app/javascript/mastodon/components/dismissable_banner.tsx
new file mode 100644
index 0000000000..d5cdb07503
--- /dev/null
+++ b/app/javascript/mastodon/components/dismissable_banner.tsx
@@ -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> = ({
+ 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 (
+
+ );
+};
diff --git a/app/javascript/mastodon/components/hashtag.jsx b/app/javascript/mastodon/components/hashtag.jsx
index 4a7b9ef719..14bb4ddc64 100644
--- a/app/javascript/mastodon/components/hashtag.jsx
+++ b/app/javascript/mastodon/components/hashtag.jsx
@@ -11,7 +11,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
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';
class SilentErrorBoundary extends Component {
diff --git a/app/javascript/mastodon/components/server_banner.jsx b/app/javascript/mastodon/components/server_banner.jsx
index 9982378601..63eec53492 100644
--- a/app/javascript/mastodon/components/server_banner.jsx
+++ b/app/javascript/mastodon/components/server_banner.jsx
@@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { fetchServer } from 'mastodon/actions/server';
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 Account from 'mastodon/containers/account_container';
import { domain } from 'mastodon/initial_state';
diff --git a/app/javascript/mastodon/components/short_number.jsx b/app/javascript/mastodon/components/short_number.jsx
deleted file mode 100644
index b7ac4f5fd5..0000000000
--- a/app/javascript/mastodon/components/short_number.jsx
+++ /dev/null
@@ -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 = ;
-
- 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 = (
-
- );
-
- let values = { count, rawNumber };
-
- switch (unit) {
- case DECIMAL_UNITS.THOUSAND: {
- return (
-
- );
- }
- case DECIMAL_UNITS.MILLION: {
- return (
-
- );
- }
- case DECIMAL_UNITS.BILLION: {
- return (
-
- );
- }
- // Not sure if we should go farther - @Sasha-Sorokin
- default: return count;
- }
-}
-
-ShortNumberCounter.propTypes = {
- value: PropTypes.arrayOf(PropTypes.number),
-};
-
-export default memo(ShortNumber);
diff --git a/app/javascript/mastodon/components/short_number.tsx b/app/javascript/mastodon/components/short_number.tsx
new file mode 100644
index 0000000000..010586c04f
--- /dev/null
+++ b/app/javascript/mastodon/components/short_number.tsx
@@ -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 = ({
+ 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 = ;
+
+ return (
+ customRenderer?.(displayNumber, pluralReady(value, division)) ||
+ displayNumber
+ );
+};
+export const ShortNumber = memo(ShortNumberRenderer);
+
+interface ShortNumberCounterProps {
+ value: number[];
+}
+const ShortNumberCounter: React.FC = ({ value }) => {
+ const [rawNumber, unit, maxFractionDigits = 0] = value;
+
+ const count = (
+
+ );
+
+ const values = { count, rawNumber };
+
+ switch (unit) {
+ case DECIMAL_UNITS.THOUSAND: {
+ return (
+
+ );
+ }
+ case DECIMAL_UNITS.MILLION: {
+ return (
+
+ );
+ }
+ case DECIMAL_UNITS.BILLION: {
+ return (
+
+ );
+ }
+ // Not sure if we should go farther - @Sasha-Sorokin
+ default:
+ return count;
+ }
+};
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx
index 688a456319..84a698810f 100644
--- a/app/javascript/mastodon/components/status_content.jsx
+++ b/app/javascript/mastodon/components/status_content.jsx
@@ -44,7 +44,7 @@ class TranslateButton extends PureComponent {
}
return (
-