Change design of people tab on explore in web UI (#30059)
This commit is contained in:
		
							parent
							
								
									85fdbd0ad5
								
							
						
					
					
						commit
						0ec061aa8f
					
				
							
								
								
									
										88
									
								
								app/javascript/mastodon/features/explore/components/card.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								app/javascript/mastodon/features/explore/components/card.jsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | |||||||
|  | import PropTypes from 'prop-types'; | ||||||
|  | import { useCallback } from 'react'; | ||||||
|  | 
 | ||||||
|  | import { FormattedMessage, useIntl, defineMessages } from 'react-intl'; | ||||||
|  | 
 | ||||||
|  | import { Link } from 'react-router-dom'; | ||||||
|  | 
 | ||||||
|  | import { useDispatch, useSelector } from 'react-redux'; | ||||||
|  | 
 | ||||||
|  | import CloseIcon from '@/material-icons/400-24px/close.svg?react'; | ||||||
|  | import { followAccount, unfollowAccount } from 'mastodon/actions/accounts'; | ||||||
|  | import { dismissSuggestion } from 'mastodon/actions/suggestions'; | ||||||
|  | import { Avatar } from 'mastodon/components/avatar'; | ||||||
|  | import { Button } from 'mastodon/components/button'; | ||||||
|  | import { DisplayName } from 'mastodon/components/display_name'; | ||||||
|  | import { IconButton } from 'mastodon/components/icon_button'; | ||||||
|  | import { domain } from 'mastodon/initial_state'; | ||||||
|  | 
 | ||||||
|  | const messages = defineMessages({ | ||||||
|  |   follow: { id: 'account.follow', defaultMessage: 'Follow' }, | ||||||
|  |   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, | ||||||
|  |   dismiss: { id: 'follow_suggestions.dismiss', defaultMessage: "Don't show again" }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const Card = ({ id, source }) => { | ||||||
|  |   const intl = useIntl(); | ||||||
|  |   const account = useSelector(state => state.getIn(['accounts', id])); | ||||||
|  |   const relationship = useSelector(state => state.getIn(['relationships', id])); | ||||||
|  |   const dispatch = useDispatch(); | ||||||
|  |   const following = relationship?.get('following') ?? relationship?.get('requested'); | ||||||
|  | 
 | ||||||
|  |   const handleFollow = useCallback(() => { | ||||||
|  |     if (following) { | ||||||
|  |       dispatch(unfollowAccount(id)); | ||||||
|  |     } else { | ||||||
|  |       dispatch(followAccount(id)); | ||||||
|  |     } | ||||||
|  |   }, [id, following, dispatch]); | ||||||
|  | 
 | ||||||
|  |   const handleDismiss = useCallback(() => { | ||||||
|  |     dispatch(dismissSuggestion(id)); | ||||||
|  |   }, [id, dispatch]); | ||||||
|  | 
 | ||||||
|  |   let label; | ||||||
|  | 
 | ||||||
|  |   switch (source) { | ||||||
|  |   case 'friends_of_friends': | ||||||
|  |     label = <FormattedMessage id='follow_suggestions.friends_of_friends_longer' defaultMessage='Popular among people you follow' />; | ||||||
|  |     break; | ||||||
|  |   case 'similar_to_recently_followed': | ||||||
|  |     label = <FormattedMessage id='follow_suggestions.similar_to_recently_followed_longer' defaultMessage='Similar to profiles you recently followed' />; | ||||||
|  |     break; | ||||||
|  |   case 'featured': | ||||||
|  |     label = <FormattedMessage id='follow_suggestions.featured_longer' defaultMessage='Hand-picked by the {domain} team' values={{ domain }} />; | ||||||
|  |     break; | ||||||
|  |   case 'most_followed': | ||||||
|  |     label = <FormattedMessage id='follow_suggestions.popular_suggestion_longer' defaultMessage='Popular on {domain}' values={{ domain }} />; | ||||||
|  |     break; | ||||||
|  |   case 'most_interactions': | ||||||
|  |     label = <FormattedMessage id='follow_suggestions.popular_suggestion_longer' defaultMessage='Popular on {domain}' values={{ domain }} />; | ||||||
|  |     break; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <div className='explore__suggestions__card'> | ||||||
|  |       <div className='explore__suggestions__card__source'> | ||||||
|  |         {label} | ||||||
|  |       </div> | ||||||
|  | 
 | ||||||
|  |       <div className='explore__suggestions__card__body'> | ||||||
|  |         <Link to={`/@${account.get('acct')}`}><Avatar account={account} size={48} /></Link> | ||||||
|  | 
 | ||||||
|  |         <div className='explore__suggestions__card__body__main'> | ||||||
|  |           <div className='explore__suggestions__card__body__main__name-button'> | ||||||
|  |             <Link className='explore__suggestions__card__body__main__name-button__name' to={`/@${account.get('acct')}`}><DisplayName account={account} /></Link> | ||||||
|  |             <IconButton iconComponent={CloseIcon} onClick={handleDismiss} title={intl.formatMessage(messages.dismiss)} /> | ||||||
|  |             <Button text={intl.formatMessage(following ? messages.unfollow : messages.follow)} secondary={following} onClick={handleFollow} /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | Card.propTypes = { | ||||||
|  |   id: PropTypes.string.isRequired, | ||||||
|  |   source: PropTypes.oneOf(['friends_of_friends', 'similar_to_recently_followed', 'featured', 'most_followed', 'most_interactions']), | ||||||
|  | }; | ||||||
| @ -10,9 +10,10 @@ import { connect } from 'react-redux'; | |||||||
| 
 | 
 | ||||||
| import { fetchSuggestions } from 'mastodon/actions/suggestions'; | import { fetchSuggestions } from 'mastodon/actions/suggestions'; | ||||||
| import { LoadingIndicator } from 'mastodon/components/loading_indicator'; | import { LoadingIndicator } from 'mastodon/components/loading_indicator'; | ||||||
| import AccountCard from 'mastodon/features/directory/components/account_card'; |  | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
|  | import { Card } from './components/card'; | ||||||
|  | 
 | ||||||
| const mapStateToProps = state => ({ | const mapStateToProps = state => ({ | ||||||
|   suggestions: state.getIn(['suggestions', 'items']), |   suggestions: state.getIn(['suggestions', 'items']), | ||||||
|   isLoading: state.getIn(['suggestions', 'isLoading']), |   isLoading: state.getIn(['suggestions', 'isLoading']), | ||||||
| @ -54,7 +55,11 @@ class Suggestions extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='explore__suggestions scrollable' data-nosnippet> |       <div className='explore__suggestions scrollable' data-nosnippet> | ||||||
|         {isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => ( |         {isLoading ? <LoadingIndicator /> : suggestions.map(suggestion => ( | ||||||
|           <AccountCard key={suggestion.get('account')} id={suggestion.get('account')} /> |           <Card | ||||||
|  |             key={suggestion.get('account')} | ||||||
|  |             id={suggestion.get('account')} | ||||||
|  |             source={suggestion.getIn(['sources', 0])} | ||||||
|  |           /> | ||||||
|         ))} |         ))} | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -308,6 +308,8 @@ | |||||||
|   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.", |   "follow_requests.unlocked_explanation": "Even though your account is not locked, the {domain} staff thought you might want to review follow requests from these accounts manually.", | ||||||
|   "follow_suggestions.curated_suggestion": "Staff pick", |   "follow_suggestions.curated_suggestion": "Staff pick", | ||||||
|   "follow_suggestions.dismiss": "Don't show again", |   "follow_suggestions.dismiss": "Don't show again", | ||||||
|  |   "follow_suggestions.featured_longer": "Hand-picked by the {domain} team", | ||||||
|  |   "follow_suggestions.friends_of_friends_longer": "Popular among people you follow", | ||||||
|   "follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.", |   "follow_suggestions.hints.featured": "This profile has been hand-picked by the {domain} team.", | ||||||
|   "follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.", |   "follow_suggestions.hints.friends_of_friends": "This profile is popular among the people you follow.", | ||||||
|   "follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.", |   "follow_suggestions.hints.most_followed": "This profile is one of the most followed on {domain}.", | ||||||
| @ -315,6 +317,8 @@ | |||||||
|   "follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.", |   "follow_suggestions.hints.similar_to_recently_followed": "This profile is similar to the profiles you have most recently followed.", | ||||||
|   "follow_suggestions.personalized_suggestion": "Personalized suggestion", |   "follow_suggestions.personalized_suggestion": "Personalized suggestion", | ||||||
|   "follow_suggestions.popular_suggestion": "Popular suggestion", |   "follow_suggestions.popular_suggestion": "Popular suggestion", | ||||||
|  |   "follow_suggestions.popular_suggestion_longer": "Popular on {domain}", | ||||||
|  |   "follow_suggestions.similar_to_recently_followed_longer": "Similar to profiles you recently followed", | ||||||
|   "follow_suggestions.view_all": "View all", |   "follow_suggestions.view_all": "View all", | ||||||
|   "follow_suggestions.who_to_follow": "Who to follow", |   "follow_suggestions.who_to_follow": "Who to follow", | ||||||
|   "followed_tags": "Followed hashtags", |   "followed_tags": "Followed hashtags", | ||||||
|  | |||||||
| @ -2016,7 +2016,10 @@ a .account__avatar { | |||||||
|   display: flex; |   display: flex; | ||||||
|   align-items: center; |   align-items: center; | ||||||
|   gap: 8px; |   gap: 8px; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
|  | .account__relationship, | ||||||
|  | .explore__suggestions__card { | ||||||
|   .icon-button { |   .icon-button { | ||||||
|     border: 1px solid var(--background-border-color); |     border: 1px solid var(--background-border-color); | ||||||
|     border-radius: 4px; |     border-radius: 4px; | ||||||
| @ -2964,6 +2967,75 @@ $ui-header-logo-wordmark-width: 99px; | |||||||
|   display: none; |   display: none; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .explore__suggestions__card { | ||||||
|  |   padding: 12px 16px; | ||||||
|  |   gap: 8px; | ||||||
|  |   display: flex; | ||||||
|  |   flex-direction: column; | ||||||
|  |   border-bottom: 1px solid var(--background-border-color); | ||||||
|  | 
 | ||||||
|  |   &:last-child { | ||||||
|  |     border-bottom: 0; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__source { | ||||||
|  |     padding-inline-start: 60px; | ||||||
|  |     font-size: 13px; | ||||||
|  |     line-height: 16px; | ||||||
|  |     color: $dark-text-color; | ||||||
|  |     text-overflow: ellipsis; | ||||||
|  |     overflow: hidden; | ||||||
|  |     white-space: nowrap; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   &__body { | ||||||
|  |     display: flex; | ||||||
|  |     gap: 12px; | ||||||
|  |     align-items: center; | ||||||
|  | 
 | ||||||
|  |     &__main { | ||||||
|  |       flex: 1 1 auto; | ||||||
|  |       display: flex; | ||||||
|  |       flex-direction: column; | ||||||
|  |       gap: 8px; | ||||||
|  |       min-width: 0; | ||||||
|  | 
 | ||||||
|  |       &__name-button { | ||||||
|  |         display: flex; | ||||||
|  |         align-items: center; | ||||||
|  |         gap: 8px; | ||||||
|  | 
 | ||||||
|  |         &__name { | ||||||
|  |           display: block; | ||||||
|  |           color: inherit; | ||||||
|  |           text-decoration: none; | ||||||
|  |           flex: 1 1 auto; | ||||||
|  |           min-width: 0; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .button { | ||||||
|  |           min-width: 80px; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         .display-name { | ||||||
|  |           font-size: 15px; | ||||||
|  |           line-height: 20px; | ||||||
|  |           color: $secondary-text-color; | ||||||
|  | 
 | ||||||
|  |           strong { | ||||||
|  |             font-weight: 700; | ||||||
|  |           } | ||||||
|  | 
 | ||||||
|  |           &__account { | ||||||
|  |             color: $darker-text-color; | ||||||
|  |             display: block; | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | 
 | ||||||
| @media screen and (max-width: $no-gap-breakpoint - 1px) { | @media screen and (max-width: $no-gap-breakpoint - 1px) { | ||||||
|   .columns-area__panels__pane--compositional { |   .columns-area__panels__pane--compositional { | ||||||
|     display: none; |     display: none; | ||||||
| @ -7293,10 +7365,11 @@ a.status-card { | |||||||
|         content: ''; |         content: ''; | ||||||
|         position: absolute; |         position: absolute; | ||||||
|         bottom: -1px; |         bottom: -1px; | ||||||
|         left: 0; |         left: 50%; | ||||||
|         width: 100%; |         transform: translateX(-50%); | ||||||
|  |         width: 40px; | ||||||
|         height: 3px; |         height: 3px; | ||||||
|         border-radius: 4px; |         border-radius: 4px 4px 0 0; | ||||||
|         background: $highlight-text-color; |         background: $highlight-text-color; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user