Add dismissable hints to various timelines in web UI (#19315)
Co-authored-by: Yamagishi Kazutoshi <ykzts@desire.sh>
This commit is contained in:
		
							parent
							
								
									a5112b51fd
								
							
						
					
					
						commit
						f41ec9af05
					
				
							
								
								
									
										51
									
								
								app/javascript/mastodon/components/dismissable_banner.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								app/javascript/mastodon/components/dismissable_banner.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | ||||
| import React from 'react'; | ||||
| import IconButton from './icon_button'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { injectIntl, defineMessages } from 'react-intl'; | ||||
| import { bannerSettings } from 'mastodon/settings'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   dismiss: { id: 'dismissable_banner.dismiss', defaultMessage: 'Dismiss' }, | ||||
| }); | ||||
| 
 | ||||
| export default @injectIntl | ||||
| class DismissableBanner extends React.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 ( | ||||
|       <div className='dismissable-banner'> | ||||
|         <div className='dismissable-banner__message'> | ||||
|           {children} | ||||
|         </div> | ||||
| 
 | ||||
|         <div className='dismissable-banner__action'> | ||||
|           <IconButton icon='times' title={intl.formatMessage(messages.dismiss)} onClick={this.handleDismiss} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
| } | ||||
| @ -10,6 +10,8 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; | ||||
| import ColumnSettingsContainer from './containers/column_settings_container'; | ||||
| import { connectCommunityStream } from '../../actions/streaming'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import { domain } from 'mastodon/initial_state'; | ||||
| import DismissableBanner from 'mastodon/components/dismissable_banner'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   title: { id: 'column.community', defaultMessage: 'Local timeline' }, | ||||
| @ -134,6 +136,10 @@ class CommunityTimeline extends React.PureComponent { | ||||
|           <ColumnSettingsContainer columnId={columnId} /> | ||||
|         </ColumnHeader> | ||||
| 
 | ||||
|         <DismissableBanner id='community_timeline'> | ||||
|           <FormattedMessage id='dismissable_banner.community_timeline' defaultMessage='These are the most recent public posts from people whose accounts are hosted by {domain}.' values={{ domain }} /> | ||||
|         </DismissableBanner> | ||||
| 
 | ||||
|         <StatusListContainer | ||||
|           trackScroll={!pinned} | ||||
|           scrollKey={`community_timeline-${columnId}`} | ||||
|  | ||||
| @ -6,6 +6,7 @@ import LoadingIndicator from 'mastodon/components/loading_indicator'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { fetchTrendingLinks } from 'mastodon/actions/trends'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import DismissableBanner from 'mastodon/components/dismissable_banner'; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   links: state.getIn(['trends', 'links', 'items']), | ||||
| @ -29,9 +30,17 @@ class Links extends React.PureComponent { | ||||
|   render () { | ||||
|     const { isLoading, links } = this.props; | ||||
| 
 | ||||
|     const banner = ( | ||||
|       <DismissableBanner id='explore/links'> | ||||
|         <FormattedMessage id='dismissable_banner.explore_links' defaultMessage='These news stories are being talked about by people on this and other servers of the decentralized network right now.' /> | ||||
|       </DismissableBanner> | ||||
|     ); | ||||
| 
 | ||||
|     if (!isLoading && links.isEmpty()) { | ||||
|       return ( | ||||
|         <div className='explore__links scrollable scrollable--flex'> | ||||
|           {banner} | ||||
| 
 | ||||
|           <div className='empty-column-indicator'> | ||||
|             <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' /> | ||||
|           </div> | ||||
| @ -41,6 +50,8 @@ class Links extends React.PureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='explore__links'> | ||||
|         {banner} | ||||
| 
 | ||||
|         {isLoading ? (<LoadingIndicator />) : links.map(link => ( | ||||
|           <Story | ||||
|             key={link.get('id')} | ||||
|  | ||||
| @ -6,6 +6,7 @@ import { FormattedMessage } from 'react-intl'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { fetchTrendingStatuses, expandTrendingStatuses } from 'mastodon/actions/trends'; | ||||
| import { debounce } from 'lodash'; | ||||
| import DismissableBanner from 'mastodon/components/dismissable_banner'; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   statusIds: state.getIn(['status_lists', 'trending', 'items']), | ||||
| @ -40,17 +41,23 @@ class Statuses extends React.PureComponent { | ||||
|     const emptyMessage = <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' />; | ||||
| 
 | ||||
|     return ( | ||||
|       <StatusList | ||||
|         trackScroll | ||||
|         statusIds={statusIds} | ||||
|         scrollKey='explore-statuses' | ||||
|         hasMore={hasMore} | ||||
|         isLoading={isLoading} | ||||
|         onLoadMore={this.handleLoadMore} | ||||
|         emptyMessage={emptyMessage} | ||||
|         bindToDocument={!multiColumn} | ||||
|         withCounters | ||||
|       /> | ||||
|       <> | ||||
|         <DismissableBanner id='explore/statuses'> | ||||
|           <FormattedMessage id='dismissable_banner.explore_statuses' defaultMessage='These posts from this and other servers in the decentralized network are gaining traction on this server right now.' /> | ||||
|         </DismissableBanner> | ||||
| 
 | ||||
|         <StatusList | ||||
|           trackScroll | ||||
|           statusIds={statusIds} | ||||
|           scrollKey='explore-statuses' | ||||
|           hasMore={hasMore} | ||||
|           isLoading={isLoading} | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|           emptyMessage={emptyMessage} | ||||
|           bindToDocument={!multiColumn} | ||||
|           withCounters | ||||
|         /> | ||||
|       </> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,7 @@ import LoadingIndicator from 'mastodon/components/loading_indicator'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { fetchTrendingHashtags } from 'mastodon/actions/trends'; | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| import DismissableBanner from 'mastodon/components/dismissable_banner'; | ||||
| 
 | ||||
| const mapStateToProps = state => ({ | ||||
|   hashtags: state.getIn(['trends', 'tags', 'items']), | ||||
| @ -29,9 +30,17 @@ class Tags extends React.PureComponent { | ||||
|   render () { | ||||
|     const { isLoading, hashtags } = this.props; | ||||
| 
 | ||||
|     const banner = ( | ||||
|       <DismissableBanner id='explore/tags'> | ||||
|         <FormattedMessage id='dismissable_banner.explore_tags' defaultMessage='These hashtags are gaining traction among people on this and other servers of the decentralized network right now.' /> | ||||
|       </DismissableBanner> | ||||
|     ); | ||||
| 
 | ||||
|     if (!isLoading && hashtags.isEmpty()) { | ||||
|       return ( | ||||
|         <div className='explore__links scrollable scrollable--flex'> | ||||
|           {banner} | ||||
| 
 | ||||
|           <div className='empty-column-indicator'> | ||||
|             <FormattedMessage id='empty_column.explore_statuses' defaultMessage='Nothing is trending right now. Check back later!' /> | ||||
|           </div> | ||||
| @ -41,6 +50,8 @@ class Tags extends React.PureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='explore__links'> | ||||
|         {banner} | ||||
| 
 | ||||
|         {isLoading ? (<LoadingIndicator />) : hashtags.map(hashtag => ( | ||||
|           <Hashtag key={hashtag.get('name')} hashtag={hashtag} /> | ||||
|         ))} | ||||
|  | ||||
| @ -10,6 +10,7 @@ import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; | ||||
| import ColumnSettingsContainer from './containers/column_settings_container'; | ||||
| import { connectPublicStream } from '../../actions/streaming'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| import DismissableBanner from 'mastodon/components/dismissable_banner'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   title: { id: 'column.public', defaultMessage: 'Federated timeline' }, | ||||
| @ -137,6 +138,10 @@ class PublicTimeline extends React.PureComponent { | ||||
|           <ColumnSettingsContainer columnId={columnId} /> | ||||
|         </ColumnHeader> | ||||
| 
 | ||||
|         <DismissableBanner id='public_timeline'> | ||||
|           <FormattedMessage id='dismissable_banner.public_timeline' defaultMessage='These are the most recent public posts from people on this and other servers of the decentralized network that this server knows about.' /> | ||||
|         </DismissableBanner> | ||||
| 
 | ||||
|         <StatusListContainer | ||||
|           timelineId={`public${onlyRemote ? ':remote' : ''}${onlyMedia ? ':media' : ''}`} | ||||
|           onLoadMore={this.handleLoadMore} | ||||
|  | ||||
| @ -45,3 +45,4 @@ export default class Settings { | ||||
| 
 | ||||
| export const pushNotificationsSetting = new Settings('mastodon_push_notification_data'); | ||||
| export const tagHistory = new Settings('mastodon_tag_history'); | ||||
| export const bannerSettings = new Settings('mastodon_banner_settings'); | ||||
|  | ||||
| @ -8312,3 +8312,28 @@ noscript { | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .dismissable-banner { | ||||
|   background: $ui-base-color; | ||||
|   border-bottom: 1px solid lighten($ui-base-color, 8%); | ||||
|   display: flex; | ||||
|   align-items: center; | ||||
|   gap: 30px; | ||||
| 
 | ||||
|   &__message { | ||||
|     flex: 1 1 auto; | ||||
|     padding: 20px 15px; | ||||
|     cursor: default; | ||||
|     font-size: 14px; | ||||
|     line-height: 18px; | ||||
|     color: $primary-text-color; | ||||
|   } | ||||
| 
 | ||||
|   &__action { | ||||
|     padding: 15px; | ||||
|     flex: 0 0 auto; | ||||
|     display: flex; | ||||
|     align-items: center; | ||||
|     justify-content: center; | ||||
|   } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user