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 { LoadingIndicator } from 'mastodon/components/loading_indicator'; | ||||
| import AccountCard from 'mastodon/features/directory/components/account_card'; | ||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| import { Card } from './components/card'; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   suggestions: state.getIn(['suggestions', 'items']), | ||||
|   isLoading: state.getIn(['suggestions', 'isLoading']), | ||||
| @ -54,7 +55,11 @@ class Suggestions extends PureComponent { | ||||
|     return ( | ||||
|       <div className='explore__suggestions scrollable' data-nosnippet> | ||||
|         {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> | ||||
|     ); | ||||
|  | ||||
| @ -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_suggestions.curated_suggestion": "Staff pick", | ||||
|   "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.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}.", | ||||
| @ -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.personalized_suggestion": "Personalized 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.who_to_follow": "Who to follow", | ||||
|   "followed_tags": "Followed hashtags", | ||||
|  | ||||
| @ -2016,7 +2016,10 @@ a .account__avatar { | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   gap: 8px; | ||||
| } | ||||
| 
 | ||||
| .account__relationship, | ||||
| .explore__suggestions__card { | ||||
|   .icon-button { | ||||
|     border: 1px solid var(--background-border-color); | ||||
|     border-radius: 4px; | ||||
| @ -2964,6 +2967,75 @@ $ui-header-logo-wordmark-width: 99px; | ||||
|   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) { | ||||
|   .columns-area__panels__pane--compositional { | ||||
|     display: none; | ||||
| @ -7293,10 +7365,11 @@ a.status-card { | ||||
|         content: ''; | ||||
|         position: absolute; | ||||
|         bottom: -1px; | ||||
|         left: 0; | ||||
|         width: 100%; | ||||
|         left: 50%; | ||||
|         transform: translateX(-50%); | ||||
|         width: 40px; | ||||
|         height: 3px; | ||||
|         border-radius: 4px; | ||||
|         border-radius: 4px 4px 0 0; | ||||
|         background: $highlight-text-color; | ||||
|       } | ||||
|     } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user