Converted hashtag.jsx to TypeScript (#27872)
Co-authored-by: Claire <claire.github-309c@sitedethib.com> Co-authored-by: Renaud Chaput <renchap@gmail.com>
This commit is contained in:
		
					parent
					
						
							
								1142f4c79e
							
						
					
				
			
			
				commit
				
					
						3a7f10c3f1
					
				
			
		
					 6 changed files with 149 additions and 125 deletions
				
			
		|  | @ -6,7 +6,7 @@ import { FormattedMessage } from 'react-intl'; | |||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| import api from 'mastodon/api'; | ||||
| import Hashtag from 'mastodon/components/hashtag'; | ||||
| import { Hashtag } from 'mastodon/components/hashtag'; | ||||
| 
 | ||||
| export default class Trends extends PureComponent { | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,120 +0,0 @@ | |||
| // @ts-check | ||||
| import PropTypes from 'prop-types'; | ||||
| import { Component } from 'react'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import classNames from 'classnames'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| 
 | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 
 | ||||
| import { Sparklines, SparklinesCurve } from 'react-sparklines'; | ||||
| 
 | ||||
| import { ShortNumber } from 'mastodon/components/short_number'; | ||||
| import { Skeleton } from 'mastodon/components/skeleton'; | ||||
| 
 | ||||
| class SilentErrorBoundary extends Component { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     children: PropTypes.node, | ||||
|   }; | ||||
| 
 | ||||
|   state = { | ||||
|     error: false, | ||||
|   }; | ||||
| 
 | ||||
|   componentDidCatch() { | ||||
|     this.setState({ error: true }); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     if (this.state.error) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     return this.props.children; | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Used to render counter of how much people are talking about hashtag | ||||
|  * @type {(displayNumber: JSX.Element, pluralReady: number) => JSX.Element} | ||||
|  */ | ||||
| export const accountsCountRenderer = (displayNumber, pluralReady) => ( | ||||
|   <FormattedMessage | ||||
|     id='trends.counter_by_accounts' | ||||
|     defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}' | ||||
|     values={{ | ||||
|       count: pluralReady, | ||||
|       counter: <strong>{displayNumber}</strong>, | ||||
|       days: 2, | ||||
|     }} | ||||
|   /> | ||||
| ); | ||||
| 
 | ||||
| // @ts-expect-error | ||||
| export const ImmutableHashtag = ({ hashtag }) => ( | ||||
|   <Hashtag | ||||
|     name={hashtag.get('name')} | ||||
|     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()} | ||||
|   /> | ||||
| ); | ||||
| 
 | ||||
| ImmutableHashtag.propTypes = { | ||||
|   hashtag: ImmutablePropTypes.map.isRequired, | ||||
| }; | ||||
| 
 | ||||
| // @ts-expect-error | ||||
| const Hashtag = ({ name, to, people, uses, history, className, description, withGraph }) => ( | ||||
|   <div className={classNames('trends__item', className)}> | ||||
|     <div className='trends__item__name'> | ||||
|       <Link to={to}> | ||||
|         {name ? <>#<span>{name}</span></> : <Skeleton width={50} />} | ||||
|       </Link> | ||||
| 
 | ||||
|       {description ? ( | ||||
|         <span>{description}</span> | ||||
|       ) : ( | ||||
|         typeof people !== 'undefined' ? <ShortNumber value={people} renderer={accountsCountRenderer} /> : <Skeleton width={100} /> | ||||
|       )} | ||||
|     </div> | ||||
| 
 | ||||
|     {typeof uses !== 'undefined' && ( | ||||
|       <div className='trends__item__current'> | ||||
|         <ShortNumber value={uses} /> | ||||
|       </div> | ||||
|     )} | ||||
| 
 | ||||
|     {withGraph && ( | ||||
|       <div className='trends__item__sparkline'> | ||||
|         <SilentErrorBoundary> | ||||
|           <Sparklines width={50} height={28} data={history ? history : Array.from(Array(7)).map(() => 0)}> | ||||
|             <SparklinesCurve style={{ fill: 'none' }} /> | ||||
|           </Sparklines> | ||||
|         </SilentErrorBoundary> | ||||
|       </div> | ||||
|     )} | ||||
|   </div> | ||||
| ); | ||||
| 
 | ||||
| Hashtag.propTypes = { | ||||
|   name: PropTypes.string, | ||||
|   to: PropTypes.string, | ||||
|   people: PropTypes.number, | ||||
|   description: PropTypes.node, | ||||
|   uses: PropTypes.number, | ||||
|   history: PropTypes.arrayOf(PropTypes.number), | ||||
|   className: PropTypes.string, | ||||
|   withGraph: PropTypes.bool, | ||||
| }; | ||||
| 
 | ||||
| Hashtag.defaultProps = { | ||||
|   withGraph: true, | ||||
| }; | ||||
| 
 | ||||
| export default Hashtag; | ||||
							
								
								
									
										145
									
								
								app/javascript/mastodon/components/hashtag.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								app/javascript/mastodon/components/hashtag.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,145 @@ | |||
| import type { JSX } from 'react'; | ||||
| import { Component } from 'react'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import classNames from 'classnames'; | ||||
| import { Link } from 'react-router-dom'; | ||||
| 
 | ||||
| import type Immutable from 'immutable'; | ||||
| 
 | ||||
| import { Sparklines, SparklinesCurve } from 'react-sparklines'; | ||||
| 
 | ||||
| import { ShortNumber } from 'mastodon/components/short_number'; | ||||
| import { Skeleton } from 'mastodon/components/skeleton'; | ||||
| 
 | ||||
| interface SilentErrorBoundaryProps { | ||||
|   children: React.ReactNode; | ||||
| } | ||||
| 
 | ||||
| class SilentErrorBoundary extends Component<SilentErrorBoundaryProps> { | ||||
|   state = { | ||||
|     error: false, | ||||
|   }; | ||||
| 
 | ||||
|   componentDidCatch() { | ||||
|     this.setState({ error: true }); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     if (this.state.error) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     return this.props.children; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Used to render counter of how much people are talking about hashtag | ||||
|  * @param displayNumber Counter number to display | ||||
|  * @param pluralReady Whether the count is plural | ||||
|  * @returns Formatted counter of how much people are talking about hashtag | ||||
|  */ | ||||
| export const accountsCountRenderer = ( | ||||
|   displayNumber: JSX.Element, | ||||
|   pluralReady: number, | ||||
| ) => ( | ||||
|   <FormattedMessage | ||||
|     id='trends.counter_by_accounts' | ||||
|     defaultMessage='{count, plural, one {{counter} person} other {{counter} people}} in the past {days, plural, one {day} other {# days}}' | ||||
|     values={{ | ||||
|       count: pluralReady, | ||||
|       counter: <strong>{displayNumber}</strong>, | ||||
|       days: 2, | ||||
|     }} | ||||
|   /> | ||||
| ); | ||||
| 
 | ||||
| interface ImmutableHashtagProps { | ||||
|   hashtag: Immutable.Map<string, unknown>; | ||||
| } | ||||
| 
 | ||||
| export const ImmutableHashtag = ({ hashtag }: ImmutableHashtagProps) => ( | ||||
|   <Hashtag | ||||
|     name={hashtag.get('name') as string} | ||||
|     to={`/tags/${hashtag.get('name') as string}`} | ||||
|     people={ | ||||
|       (hashtag.getIn(['history', 0, 'accounts']) as number) * 1 + | ||||
|       (hashtag.getIn(['history', 1, 'accounts']) as number) * 1 | ||||
|     } | ||||
|     history={( | ||||
|       hashtag.get('history') as Immutable.Collection.Indexed< | ||||
|         Immutable.Map<string, number> | ||||
|       > | ||||
|     ) | ||||
|       .reverse() | ||||
|       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
 | ||||
|       .map((day) => day.get('uses')!) | ||||
|       .toArray()} | ||||
|   /> | ||||
| ); | ||||
| 
 | ||||
| export interface HashtagProps { | ||||
|   className?: string; | ||||
|   description?: React.ReactNode; | ||||
|   history?: number[]; | ||||
|   name: string; | ||||
|   people: number; | ||||
|   to: string; | ||||
|   uses?: number; | ||||
|   withGraph?: boolean; | ||||
| } | ||||
| 
 | ||||
| export const Hashtag: React.FC<HashtagProps> = ({ | ||||
|   name, | ||||
|   to, | ||||
|   people, | ||||
|   uses, | ||||
|   history, | ||||
|   className, | ||||
|   description, | ||||
|   withGraph = true, | ||||
| }) => ( | ||||
|   <div className={classNames('trends__item', className)}> | ||||
|     <div className='trends__item__name'> | ||||
|       <Link to={to}> | ||||
|         {name ? ( | ||||
|           <> | ||||
|             #<span>{name}</span> | ||||
|           </> | ||||
|         ) : ( | ||||
|           <Skeleton width={50} /> | ||||
|         )} | ||||
|       </Link> | ||||
| 
 | ||||
|       {description ? ( | ||||
|         <span>{description}</span> | ||||
|       ) : typeof people !== 'undefined' ? ( | ||||
|         <ShortNumber value={people} renderer={accountsCountRenderer} /> | ||||
|       ) : ( | ||||
|         <Skeleton width={100} /> | ||||
|       )} | ||||
|     </div> | ||||
| 
 | ||||
|     {typeof uses !== 'undefined' && ( | ||||
|       <div className='trends__item__current'> | ||||
|         <ShortNumber value={uses} /> | ||||
|       </div> | ||||
|     )} | ||||
| 
 | ||||
|     {withGraph && ( | ||||
|       <div className='trends__item__sparkline'> | ||||
|         <SilentErrorBoundary> | ||||
|           <Sparklines | ||||
|             width={50} | ||||
|             height={28} | ||||
|             data={history ? history : Array.from(Array(7)).map(() => 0)} | ||||
|           > | ||||
|             <SparklinesCurve style={{ fill: 'none' }} /> | ||||
|           </Sparklines> | ||||
|         </SilentErrorBoundary> | ||||
|       </div> | ||||
|     )} | ||||
|   </div> | ||||
| ); | ||||
|  | @ -5,7 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 
 | ||||
| import Hashtag from 'mastodon/components/hashtag'; | ||||
| import { Hashtag } from 'mastodon/components/hashtag'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   lastStatusAt: { id: 'account.featured_tags.last_status_at', defaultMessage: 'Last post on {date}' }, | ||||
|  |  | |||
|  | @ -13,7 +13,7 @@ import { debounce } from 'lodash'; | |||
| 
 | ||||
| import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; | ||||
| import ColumnHeader from 'mastodon/components/column_header'; | ||||
| import Hashtag from 'mastodon/components/hashtag'; | ||||
| import { Hashtag } from 'mastodon/components/hashtag'; | ||||
| import ScrollableList from 'mastodon/components/scrollable_list'; | ||||
| import Column from 'mastodon/features/ui/components/column'; | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue