Rewrite <ShortNumber /> as FC and TS (#25492)
		
	This commit is contained in:
		
					parent
					
						
							
								e0d230fb37
							
						
					
				
			
			
				commit
				
					
						20e85c0e83
					
				
			
		
					 10 changed files with 98 additions and 123 deletions
				
			
		|  | @ -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 { | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue