Refactor initial state: reduce_motion and auto_play_gif (#5501)
This commit is contained in:
		
							parent
							
								
									e4080772b5
								
							
						
					
					
						commit
						3de22a82bf
					
				
					 13 changed files with 27 additions and 52 deletions
				
			
		|  | @ -6,6 +6,7 @@ import IconButton from './icon_button'; | |||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | ||||
| import { isIOS } from '../is_mobile'; | ||||
| import classNames from 'classnames'; | ||||
| import { autoPlayGif } from '../initial_state'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   toggle_visible: { id: 'media_gallery.toggle_visible', defaultMessage: 'Toggle visibility' }, | ||||
|  | @ -23,11 +24,9 @@ class Item extends React.PureComponent { | |||
|     index: PropTypes.number.isRequired, | ||||
|     size: PropTypes.number.isRequired, | ||||
|     onClick: PropTypes.func.isRequired, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     autoPlayGif: false, | ||||
|     standalone: false, | ||||
|     index: 0, | ||||
|     size: 1, | ||||
|  | @ -47,7 +46,7 @@ class Item extends React.PureComponent { | |||
|   } | ||||
| 
 | ||||
|   hoverToPlay () { | ||||
|     const { attachment, autoPlayGif } = this.props; | ||||
|     const { attachment } = this.props; | ||||
|     return !autoPlayGif && attachment.get('type') === 'gifv'; | ||||
|   } | ||||
| 
 | ||||
|  | @ -139,7 +138,7 @@ class Item extends React.PureComponent { | |||
|         </a> | ||||
|       ); | ||||
|     } else if (attachment.get('type') === 'gifv') { | ||||
|       const autoPlay = !isIOS() && this.props.autoPlayGif; | ||||
|       const autoPlay = !isIOS() && autoPlayGif; | ||||
| 
 | ||||
|       thumbnail = ( | ||||
|         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> | ||||
|  | @ -181,11 +180,9 @@ export default class MediaGallery extends React.PureComponent { | |||
|     height: PropTypes.number.isRequired, | ||||
|     onOpenMedia: PropTypes.func.isRequired, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     autoPlayGif: false, | ||||
|     standalone: false, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -261,9 +258,9 @@ export default class MediaGallery extends React.PureComponent { | |||
|       const size = media.take(4).size; | ||||
| 
 | ||||
|       if (this.isStandaloneEligible()) { | ||||
|         children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} autoPlayGif={this.props.autoPlayGif} />; | ||||
|         children = <Item standalone onClick={this.handleClick} attachment={media.get(0)} />; | ||||
|       } else { | ||||
|         children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} autoPlayGif={this.props.autoPlayGif} index={i} size={size} />); | ||||
|         children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -38,7 +38,6 @@ export default class Status extends ImmutablePureComponent { | |||
|     onHeightChange: PropTypes.func, | ||||
|     me: PropTypes.string, | ||||
|     boostModal: PropTypes.bool, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|     muted: PropTypes.bool, | ||||
|     hidden: PropTypes.bool, | ||||
|     onMoveUp: PropTypes.func, | ||||
|  | @ -56,7 +55,6 @@ export default class Status extends ImmutablePureComponent { | |||
|     'account', | ||||
|     'me', | ||||
|     'boostModal', | ||||
|     'autoPlayGif', | ||||
|     'muted', | ||||
|     'hidden', | ||||
|   ] | ||||
|  | @ -197,7 +195,7 @@ export default class Status extends ImmutablePureComponent { | |||
|       } else { | ||||
|         media = ( | ||||
|           <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > | ||||
|             {Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />} | ||||
|             {Component => <Component media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} />} | ||||
|           </Bundle> | ||||
|         ); | ||||
|       } | ||||
|  |  | |||
|  | @ -6,15 +6,14 @@ import { hydrateStore } from '../actions/store'; | |||
| import { IntlProvider, addLocaleData } from 'react-intl'; | ||||
| import { getLocale } from '../locales'; | ||||
| import Compose from '../features/standalone/compose'; | ||||
| import initialState from '../initial_state'; | ||||
| 
 | ||||
| const { localeData, messages } = getLocale(); | ||||
| addLocaleData(localeData); | ||||
| 
 | ||||
| const store = configureStore(); | ||||
| const initialStateContainer = document.getElementById('initial-state'); | ||||
| 
 | ||||
| if (initialStateContainer !== null) { | ||||
|   const initialState = JSON.parse(initialStateContainer.textContent); | ||||
| if (initialState) { | ||||
|   store.dispatch(hydrateStore(initialState)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,12 +10,13 @@ import { hydrateStore } from '../actions/store'; | |||
| import { connectUserStream } from '../actions/streaming'; | ||||
| import { IntlProvider, addLocaleData } from 'react-intl'; | ||||
| import { getLocale } from '../locales'; | ||||
| import initialState from '../initial_state'; | ||||
| 
 | ||||
| const { localeData, messages } = getLocale(); | ||||
| addLocaleData(localeData); | ||||
| 
 | ||||
| export const store = configureStore(); | ||||
| const hydrateAction = hydrateStore(JSON.parse(document.getElementById('initial-state').textContent)); | ||||
| const hydrateAction = hydrateStore(initialState); | ||||
| store.dispatch(hydrateAction); | ||||
| 
 | ||||
| export default class Mastodon extends React.PureComponent { | ||||
|  |  | |||
|  | @ -38,7 +38,6 @@ const makeMapStateToProps = () => { | |||
|     me: state.getIn(['meta', 'me']), | ||||
|     boostModal: state.getIn(['meta', 'boost_modal']), | ||||
|     deleteModal: state.getIn(['meta', 'delete_modal']), | ||||
|     autoPlayGif: state.getIn(['meta', 'auto_play_gif']), | ||||
|   }); | ||||
| 
 | ||||
|   return mapStateToProps; | ||||
|  |  | |||
|  | @ -7,15 +7,14 @@ import { IntlProvider, addLocaleData } from 'react-intl'; | |||
| import { getLocale } from '../locales'; | ||||
| import PublicTimeline from '../features/standalone/public_timeline'; | ||||
| import HashtagTimeline from '../features/standalone/hashtag_timeline'; | ||||
| import initialState from '../initial_state'; | ||||
| 
 | ||||
| const { localeData, messages } = getLocale(); | ||||
| addLocaleData(localeData); | ||||
| 
 | ||||
| const store = configureStore(); | ||||
| const initialStateContainer = document.getElementById('initial-state'); | ||||
| 
 | ||||
| if (initialStateContainer !== null) { | ||||
|   const initialState = JSON.parse(initialStateContainer.textContent); | ||||
| if (initialState) { | ||||
|   store.dispatch(hydrateStore(initialState)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,8 +5,8 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||
| import IconButton from '../../../components/icon_button'; | ||||
| import Motion from '../../ui/util/optional_motion'; | ||||
| import spring from 'react-motion/lib/spring'; | ||||
| import { connect } from 'react-redux'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { autoPlayGif } from '../../../initial_state'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, | ||||
|  | @ -14,19 +14,10 @@ const messages = defineMessages({ | |||
|   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval. Click to cancel follow request' }, | ||||
| }); | ||||
| 
 | ||||
| const makeMapStateToProps = () => { | ||||
|   const mapStateToProps = state => ({ | ||||
|     autoPlayGif: state.getIn(['meta', 'auto_play_gif']), | ||||
|   }); | ||||
| 
 | ||||
|   return mapStateToProps; | ||||
| }; | ||||
| 
 | ||||
| class Avatar extends ImmutablePureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     account: ImmutablePropTypes.map.isRequired, | ||||
|     autoPlayGif: PropTypes.bool.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|   state = { | ||||
|  | @ -44,7 +35,7 @@ class Avatar extends ImmutablePureComponent { | |||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { account, autoPlayGif }   = this.props; | ||||
|     const { account }   = this.props; | ||||
|     const { isHovered } = this.state; | ||||
| 
 | ||||
|     return ( | ||||
|  | @ -71,7 +62,6 @@ class Avatar extends ImmutablePureComponent { | |||
| 
 | ||||
| } | ||||
| 
 | ||||
| @connect(makeMapStateToProps) | ||||
| @injectIntl | ||||
| export default class Header extends ImmutablePureComponent { | ||||
| 
 | ||||
|  | @ -80,7 +70,6 @@ export default class Header extends ImmutablePureComponent { | |||
|     me: PropTypes.string.isRequired, | ||||
|     onFollow: PropTypes.func.isRequired, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     autoPlayGif: PropTypes.bool.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|  | @ -124,7 +113,7 @@ export default class Header extends ImmutablePureComponent { | |||
|     return ( | ||||
|       <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}> | ||||
|         <div> | ||||
|           <Avatar account={account} autoPlayGif={this.props.autoPlayGif} /> | ||||
|           <Avatar account={account} /> | ||||
| 
 | ||||
|           <span className='account__header__display-name' dangerouslySetInnerHTML={displayNameHtml} /> | ||||
|           <span className='account__header__username'>@{account.get('acct')} {lockedIcon}</span> | ||||
|  |  | |||
|  | @ -19,7 +19,6 @@ const mapStateToProps = (state, props) => ({ | |||
|   medias: getAccountGallery(state, props.params.accountId), | ||||
|   isLoading: state.getIn(['timelines', `account:${props.params.accountId}:media`, 'isLoading']), | ||||
|   hasMore: !!state.getIn(['timelines', `account:${props.params.accountId}:media`, 'next']), | ||||
|   autoPlayGif: state.getIn(['meta', 'auto_play_gif']), | ||||
| }); | ||||
| 
 | ||||
| @connect(mapStateToProps) | ||||
|  | @ -31,7 +30,6 @@ export default class AccountGallery extends ImmutablePureComponent { | |||
|     medias: ImmutablePropTypes.list.isRequired, | ||||
|     isLoading: PropTypes.bool, | ||||
|     hasMore: PropTypes.bool, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|   }; | ||||
| 
 | ||||
|   componentDidMount () { | ||||
|  | @ -67,7 +65,7 @@ export default class AccountGallery extends ImmutablePureComponent { | |||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { medias, autoPlayGif, isLoading, hasMore } = this.props; | ||||
|     const { medias, isLoading, hasMore } = this.props; | ||||
| 
 | ||||
|     let loadMore = null; | ||||
| 
 | ||||
|  | @ -100,7 +98,6 @@ export default class AccountGallery extends ImmutablePureComponent { | |||
|                 <MediaItem | ||||
|                   key={media.get('id')} | ||||
|                   media={media} | ||||
|                   autoPlayGif={autoPlayGif} | ||||
|                 /> | ||||
|               )} | ||||
|               {loadMore} | ||||
|  |  | |||
|  | @ -22,7 +22,6 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||
|     status: ImmutablePropTypes.map.isRequired, | ||||
|     onOpenMedia: PropTypes.func.isRequired, | ||||
|     onOpenVideo: PropTypes.func.isRequired, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|   }; | ||||
| 
 | ||||
|   handleAccountClick = (e) => { | ||||
|  | @ -70,7 +69,6 @@ export default class DetailedStatus extends ImmutablePureComponent { | |||
|             media={status.get('media_attachments')} | ||||
|             height={300} | ||||
|             onOpenMedia={this.props.onOpenMedia} | ||||
|             autoPlayGif={this.props.autoPlayGif} | ||||
|           /> | ||||
|         ); | ||||
|       } | ||||
|  |  | |||
|  | @ -45,7 +45,6 @@ const makeMapStateToProps = () => { | |||
|     me: state.getIn(['meta', 'me']), | ||||
|     boostModal: state.getIn(['meta', 'boost_modal']), | ||||
|     deleteModal: state.getIn(['meta', 'delete_modal']), | ||||
|     autoPlayGif: state.getIn(['meta', 'auto_play_gif']), | ||||
|   }); | ||||
| 
 | ||||
|   return mapStateToProps; | ||||
|  | @ -68,7 +67,6 @@ export default class Status extends ImmutablePureComponent { | |||
|     me: PropTypes.string, | ||||
|     boostModal: PropTypes.bool, | ||||
|     deleteModal: PropTypes.bool, | ||||
|     autoPlayGif: PropTypes.bool, | ||||
|     intl: PropTypes.object.isRequired, | ||||
|   }; | ||||
| 
 | ||||
|  | @ -257,7 +255,7 @@ export default class Status extends ImmutablePureComponent { | |||
| 
 | ||||
|   render () { | ||||
|     let ancestors, descendants; | ||||
|     const { status, ancestorsIds, descendantsIds, me, autoPlayGif } = this.props; | ||||
|     const { status, ancestorsIds, descendantsIds, me } = this.props; | ||||
| 
 | ||||
|     if (status === null) { | ||||
|       return ( | ||||
|  | @ -298,7 +296,6 @@ export default class Status extends ImmutablePureComponent { | |||
|               <div className='focusable' tabIndex='0'> | ||||
|                 <DetailedStatus | ||||
|                   status={status} | ||||
|                   autoPlayGif={autoPlayGif} | ||||
|                   me={me} | ||||
|                   onOpenVideo={this.handleOpenVideo} | ||||
|                   onOpenMedia={this.handleOpenMedia} | ||||
|  |  | |||
|  | @ -4,11 +4,10 @@ | |||
| import React from 'react'; | ||||
| import Motion from 'react-motion/lib/Motion'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { reduceMotion } from '../../../initial_state'; | ||||
| 
 | ||||
| const stylesToKeep = ['opacity', 'backgroundOpacity']; | ||||
| 
 | ||||
| let reduceMotion; | ||||
| 
 | ||||
| const extractValue = (value) => { | ||||
|   // This is either an object with a "val" property or it's a number
 | ||||
|   return (typeof value === 'object' && value && 'val' in value) ? value.val : value; | ||||
|  | @ -26,12 +25,6 @@ class OptionalMotion extends React.Component { | |||
| 
 | ||||
|     const { style, defaultStyle, children } = this.props; | ||||
| 
 | ||||
|     if (typeof reduceMotion !== 'boolean') { | ||||
|       // This never changes without a page reload, so we can just grab it
 | ||||
|       // once from the body classes as opposed to using Redux's connect(),
 | ||||
|       // which would unnecessarily update every state change
 | ||||
|       reduceMotion = document.body.classList.contains('reduce-motion'); | ||||
|     } | ||||
|     if (reduceMotion) { | ||||
|       Object.keys(style).forEach(key => { | ||||
|         if (stylesToKeep.includes(key)) { | ||||
|  |  | |||
							
								
								
									
										9
									
								
								app/javascript/mastodon/initial_state.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								app/javascript/mastodon/initial_state.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| const element = document.getElementById('initial-state'); | ||||
| const initialState = element && JSON.parse(element.textContent); | ||||
| 
 | ||||
| const getMeta = (prop) => initialState && initialState.meta && initialState.meta[prop]; | ||||
| 
 | ||||
| export const reduceMotion = getMeta('reduce_motion'); | ||||
| export const autoPlayGif = getMeta('auto_play_gif'); | ||||
| 
 | ||||
| export default initialState; | ||||
|  | @ -27,7 +27,6 @@ | |||
|     = yield :header_tags | ||||
| 
 | ||||
|   - body_classes ||= @body_classes || '' | ||||
|   - body_classes += ' reduce-motion' if current_account&.user&.setting_reduce_motion | ||||
|   - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui | ||||
| 
 | ||||
|   %body{ class: add_rtl_body_class(body_classes) } | ||||
|  |  | |||
		Reference in a new issue