diff options
author | Yamagishi Kazutoshi <ykzts@desire.sh> | 2017-04-22 03:05:35 +0900 |
---|---|---|
committer | Eugen <eugen@zeonfederated.com> | 2017-04-21 20:05:35 +0200 |
commit | 1948f9e767c5c8f7cb52337ce777a61b5ad1a599 (patch) | |
tree | 4d5f3549f6e5fd88340bb3543e1eb2eafc47952c /app/assets/javascripts/components/components | |
parent | 27ea2a88c12835b491ea5537f934983470faf781 (diff) |
Remove deprecated features at React v15.5 (#1905)
* Remove deprecated features at React v15.5 - [x] React.PropTypes - [x] react-addons-pure-render-mixin - [x] react-addons-test-utils * Uncommented out & Add browserify_rails options * re-add react-addons-shallow * Fix syntax error from resolve conflicts * follow up 59a77923b368d48c590cd9f4a0c6b73ce972d33f
Diffstat (limited to 'app/assets/javascripts/components/components')
22 files changed, 466 insertions, 403 deletions
diff --git a/app/assets/javascripts/components/components/account.jsx b/app/assets/javascripts/components/components/account.jsx index 6fda5915e..18197380e 100644 --- a/app/assets/javascripts/components/components/account.jsx +++ b/app/assets/javascripts/components/components/account.jsx @@ -1,5 +1,5 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; import Avatar from './avatar'; import DisplayName from './display_name'; import Permalink from './permalink'; @@ -19,30 +19,26 @@ const buttonsStyle = { height: '18px' }; -const Account = React.createClass({ +class Account extends React.PureComponent { - propTypes: { - account: ImmutablePropTypes.map.isRequired, - me: React.PropTypes.number.isRequired, - onFollow: React.PropTypes.func.isRequired, - onBlock: React.PropTypes.func.isRequired, - onMute: React.PropTypes.func.isRequired, - intl: React.PropTypes.object.isRequired - }, - - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleFollow = this.handleFollow.bind(this); + this.handleBlock = this.handleBlock.bind(this); + this.handleMute = this.handleMute.bind(this); + } handleFollow () { this.props.onFollow(this.props.account); - }, + } handleBlock () { this.props.onBlock(this.props.account); - }, + } handleMute () { this.props.onMute(this.props.account); - }, + } render () { const { account, me, intl } = this.props; @@ -86,6 +82,15 @@ const Account = React.createClass({ ); } -}); +} + +Account.propTypes = { + account: ImmutablePropTypes.map.isRequired, + me: PropTypes.number.isRequired, + onFollow: PropTypes.func.isRequired, + onBlock: PropTypes.func.isRequired, + onMute: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired +} export default injectIntl(Account); diff --git a/app/assets/javascripts/components/components/attachment_list.jsx b/app/assets/javascripts/components/components/attachment_list.jsx index 56238fe19..54841fa51 100644 --- a/app/assets/javascripts/components/components/attachment_list.jsx +++ b/app/assets/javascripts/components/components/attachment_list.jsx @@ -1,14 +1,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; -const AttachmentList = React.createClass({ - propTypes: { - media: ImmutablePropTypes.list.isRequired - }, - - mixins: [PureRenderMixin], +class AttachmentList extends React.PureComponent { render () { const { media } = this.props; @@ -29,6 +23,10 @@ const AttachmentList = React.createClass({ </div> ); } -}); +} + +AttachmentList.propTypes = { + media: ImmutablePropTypes.list.isRequired +}; export default AttachmentList; diff --git a/app/assets/javascripts/components/components/autosuggest_textarea.jsx b/app/assets/javascripts/components/components/autosuggest_textarea.jsx index 7c068955a..90eb5b6a1 100644 --- a/app/assets/javascripts/components/components/autosuggest_textarea.jsx +++ b/app/assets/javascripts/components/components/autosuggest_textarea.jsx @@ -1,5 +1,6 @@ import AutosuggestAccountContainer from '../features/compose/containers/autosuggest_account_container'; import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; import { isRtl } from '../rtl'; const textAtCursorMatchesToken = (str, caretPosition) => { @@ -27,30 +28,23 @@ const textAtCursorMatchesToken = (str, caretPosition) => { } }; -const AutosuggestTextarea = React.createClass({ - - propTypes: { - value: React.PropTypes.string, - suggestions: ImmutablePropTypes.list, - disabled: React.PropTypes.bool, - placeholder: React.PropTypes.string, - onSuggestionSelected: React.PropTypes.func.isRequired, - onSuggestionsClearRequested: React.PropTypes.func.isRequired, - onSuggestionsFetchRequested: React.PropTypes.func.isRequired, - onChange: React.PropTypes.func.isRequired, - onKeyUp: React.PropTypes.func, - onKeyDown: React.PropTypes.func, - onPaste: React.PropTypes.func.isRequired, - }, - - getInitialState () { - return { +class AutosuggestTextarea extends React.Component { + + constructor (props, context) { + super(props, context); + this.state = { suggestionsHidden: false, selectedSuggestion: 0, lastToken: null, tokenStart: 0 }; - }, + this.onChange = this.onChange.bind(this); + this.onKeyDown = this.onKeyDown.bind(this); + this.onBlur = this.onBlur.bind(this); + this.onSuggestionClick = this.onSuggestionClick.bind(this); + this.setTextarea = this.setTextarea.bind(this); + this.onPaste = this.onPaste.bind(this); + } onChange (e) { const [ tokenStart, token ] = textAtCursorMatchesToken(e.target.value, e.target.selectionStart); @@ -68,7 +62,7 @@ const AutosuggestTextarea = React.createClass({ e.target.style.height = `${e.target.scrollHeight}px`; this.props.onChange(e); - }, + } onKeyDown (e) { const { suggestions, disabled } = this.props; @@ -118,7 +112,7 @@ const AutosuggestTextarea = React.createClass({ } this.props.onKeyDown(e); - }, + } onBlur () { // If we hide the suggestions immediately, then this will prevent the @@ -128,30 +122,30 @@ const AutosuggestTextarea = React.createClass({ setTimeout(() => { this.setState({ suggestionsHidden: true }); }, 100); - }, + } onSuggestionClick (suggestion, e) { e.preventDefault(); this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); this.textarea.focus(); - }, + } componentWillReceiveProps (nextProps) { if (nextProps.suggestions !== this.props.suggestions && nextProps.suggestions.size > 0 && this.state.suggestionsHidden) { this.setState({ suggestionsHidden: false }); } - }, + } setTextarea (c) { this.textarea = c; - }, + } onPaste (e) { if (e.clipboardData && e.clipboardData.files.length === 1) { this.props.onPaste(e.clipboardData.files) e.preventDefault(); } - }, + } render () { const { value, suggestions, disabled, placeholder, onKeyUp } = this.props; @@ -196,6 +190,20 @@ const AutosuggestTextarea = React.createClass({ ); } -}); +}; + +AutosuggestTextarea.propTypes = { + value: PropTypes.string, + suggestions: ImmutablePropTypes.list, + disabled: PropTypes.bool, + placeholder: PropTypes.string, + onSuggestionSelected: PropTypes.func.isRequired, + onSuggestionsClearRequested: PropTypes.func.isRequired, + onSuggestionsFetchRequested: PropTypes.func.isRequired, + onChange: PropTypes.func.isRequired, + onKeyUp: PropTypes.func, + onKeyDown: PropTypes.func, + onPaste: PropTypes.func.isRequired, +}; export default AutosuggestTextarea; diff --git a/app/assets/javascripts/components/components/avatar.jsx b/app/assets/javascripts/components/components/avatar.jsx index 673b1a247..54f96b3a7 100644 --- a/app/assets/javascripts/components/components/avatar.jsx +++ b/app/assets/javascripts/components/components/avatar.jsx @@ -1,36 +1,23 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; -const Avatar = React.createClass({ +class Avatar extends React.PureComponent { - propTypes: { - src: React.PropTypes.string.isRequired, - staticSrc: React.PropTypes.string, - size: React.PropTypes.number.isRequired, - style: React.PropTypes.object, - animate: React.PropTypes.bool - }, - - getDefaultProps () { - return { - animate: false - }; - }, - - getInitialState () { - return { + constructor (props, context) { + super(props, context); + this.state = { hovering: false }; - }, - - mixins: [PureRenderMixin], + this.handleMouseEnter = this.handleMouseEnter.bind(this); + this.handleMouseLeave = this.handleMouseLeave.bind(this); + } handleMouseEnter () { this.setState({ hovering: true }); - }, + } handleMouseLeave () { this.setState({ hovering: false }); - }, + } render () { const { src, size, staticSrc, animate } = this.props; @@ -59,6 +46,18 @@ const Avatar = React.createClass({ ); } -}); +} + +Avatar.propTypes = { + src: PropTypes.string.isRequired, + staticSrc: PropTypes.string, + size: PropTypes.number.isRequired, + style: PropTypes.object, + animate: PropTypes.bool +}; + +Avatar.defaultProps = { + animate: false +}; export default Avatar; diff --git a/app/assets/javascripts/components/components/button.jsx b/app/assets/javascripts/components/components/button.jsx index 6c3da10fe..8d0e49aee 100644 --- a/app/assets/javascripts/components/components/button.jsx +++ b/app/assets/javascripts/components/components/button.jsx @@ -1,31 +1,17 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; - -const Button = React.createClass({ - - propTypes: { - text: React.PropTypes.node, - onClick: React.PropTypes.func, - disabled: React.PropTypes.bool, - block: React.PropTypes.bool, - secondary: React.PropTypes.bool, - size: React.PropTypes.number, - style: React.PropTypes.object, - children: React.PropTypes.node - }, - - getDefaultProps () { - return { - size: 36 - }; - }, +import PropTypes from 'prop-types'; + +class Button extends React.PureComponent { - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick (e) { if (!this.props.disabled) { this.props.onClick(); } - }, + } render () { const style = { @@ -57,6 +43,21 @@ const Button = React.createClass({ ); } -}); +} + +Button.propTypes = { + text: PropTypes.node, + onClick: PropTypes.func, + disabled: PropTypes.bool, + block: PropTypes.bool, + secondary: PropTypes.bool, + size: PropTypes.number, + style: PropTypes.object, + children: PropTypes.node +}; + +Button.defaultProps = { + size: 36 +}; export default Button; diff --git a/app/assets/javascripts/components/components/collapsable.jsx b/app/assets/javascripts/components/components/collapsable.jsx index aeebb4b0f..0810768f0 100644 --- a/app/assets/javascripts/components/components/collapsable.jsx +++ b/app/assets/javascripts/components/components/collapsable.jsx @@ -1,4 +1,5 @@ import { Motion, spring } from 'react-motion'; +import PropTypes from 'prop-types'; const Collapsable = ({ fullHeight, isVisible, children }) => ( <Motion defaultStyle={{ opacity: !isVisible ? 0 : 100, height: isVisible ? fullHeight : 0 }} style={{ opacity: spring(!isVisible ? 0 : 100), height: spring(!isVisible ? 0 : fullHeight) }}> @@ -11,9 +12,9 @@ const Collapsable = ({ fullHeight, isVisible, children }) => ( ); Collapsable.propTypes = { - fullHeight: React.PropTypes.number.isRequired, - isVisible: React.PropTypes.bool.isRequired, - children: React.PropTypes.node.isRequired + fullHeight: PropTypes.number.isRequired, + isVisible: PropTypes.bool.isRequired, + children: PropTypes.node.isRequired }; export default Collapsable; diff --git a/app/assets/javascripts/components/components/column_back_button.jsx b/app/assets/javascripts/components/components/column_back_button.jsx index 91c3b92be..d891b6829 100644 --- a/app/assets/javascripts/components/components/column_back_button.jsx +++ b/app/assets/javascripts/components/components/column_back_button.jsx @@ -1,23 +1,22 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; const iconStyle = { display: 'inline-block', marginRight: '5px' }; -const ColumnBackButton = React.createClass({ +class ColumnBackButton extends React.PureComponent { - contextTypes: { - router: React.PropTypes.object - }, - - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick () { if (window.history && window.history.length === 1) this.context.router.push("/"); else this.context.router.goBack(); - }, + } render () { return ( @@ -28,6 +27,10 @@ const ColumnBackButton = React.createClass({ ); } -}); +}; + +ColumnBackButton.contextTypes = { + router: PropTypes.object +}; export default ColumnBackButton; diff --git a/app/assets/javascripts/components/components/column_back_button_slim.jsx b/app/assets/javascripts/components/components/column_back_button_slim.jsx index 536964b01..0c753436b 100644 --- a/app/assets/javascripts/components/components/column_back_button_slim.jsx +++ b/app/assets/javascripts/components/components/column_back_button_slim.jsx @@ -1,5 +1,5 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; const outerStyle = { position: 'absolute', @@ -16,17 +16,16 @@ const iconStyle = { marginRight: '5px' }; -const ColumnBackButtonSlim = React.createClass({ +class ColumnBackButtonSlim extends React.PureComponent { - contextTypes: { - router: React.PropTypes.object - }, - - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick () { this.context.router.push('/'); - }, + } render () { return ( @@ -39,6 +38,10 @@ const ColumnBackButtonSlim = React.createClass({ ); } -}); +} + +ColumnBackButtonSlim.contextTypes = { + router: PropTypes.object +}; export default ColumnBackButtonSlim; diff --git a/app/assets/javascripts/components/components/column_collapsable.jsx b/app/assets/javascripts/components/components/column_collapsable.jsx index 75dfc8a4e..62b645783 100644 --- a/app/assets/javascripts/components/components/column_collapsable.jsx +++ b/app/assets/javascripts/components/components/column_collapsable.jsx @@ -1,5 +1,5 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; import { Motion, spring } from 'react-motion'; +import PropTypes from 'prop-types'; const iconStyle = { fontSize: '16px', @@ -11,23 +11,16 @@ const iconStyle = { zIndex: '3' }; -const ColumnCollapsable = React.createClass({ +class ColumnCollapsable extends React.PureComponent { - propTypes: { - icon: React.PropTypes.string.isRequired, - title: React.PropTypes.string, - fullHeight: React.PropTypes.number.isRequired, - children: React.PropTypes.node, - onCollapse: React.PropTypes.func - }, - - getInitialState () { - return { + constructor (props, context) { + super(props, context); + this.state = { collapsed: true }; - }, - mixins: [PureRenderMixin], + this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this); + } handleToggleCollapsed () { const currentState = this.state.collapsed; @@ -37,7 +30,7 @@ const ColumnCollapsable = React.createClass({ if (!currentState && this.props.onCollapse) { this.props.onCollapse(); } - }, + } render () { const { icon, title, fullHeight, children } = this.props; @@ -60,6 +53,14 @@ const ColumnCollapsable = React.createClass({ </div> ); } -}); +} + +ColumnCollapsable.propTypes = { + icon: PropTypes.string.isRequired, + title: PropTypes.string, + fullHeight: PropTypes.number.isRequired, + children: PropTypes.node, + onCollapse: PropTypes.func +}; export default ColumnCollapsable; diff --git a/app/assets/javascripts/components/components/display_name.jsx b/app/assets/javascripts/components/components/display_name.jsx index aa48608d3..9a6e7a9a5 100644 --- a/app/assets/javascripts/components/components/display_name.jsx +++ b/app/assets/javascripts/components/components/display_name.jsx @@ -1,15 +1,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; import escapeTextContentForBrowser from 'escape-html'; import emojify from '../emoji'; -const DisplayName = React.createClass({ - - propTypes: { - account: ImmutablePropTypes.map.isRequired - }, - - mixins: [PureRenderMixin], +class DisplayName extends React.PureComponent { render () { const displayName = this.props.account.get('display_name').length === 0 ? this.props.account.get('username') : this.props.account.get('display_name'); @@ -22,6 +15,10 @@ const DisplayName = React.createClass({ ); } -}); +}; + +DisplayName.propTypes = { + account: ImmutablePropTypes.map.isRequired +} export default DisplayName; diff --git a/app/assets/javascripts/components/components/dropdown_menu.jsx b/app/assets/javascripts/components/components/dropdown_menu.jsx index 2b42eaa60..2b0929e05 100644 --- a/app/assets/javascripts/components/components/dropdown_menu.jsx +++ b/app/assets/javascripts/components/components/dropdown_menu.jsx @@ -1,26 +1,20 @@ import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; -const DropdownMenu = React.createClass({ +class DropdownMenu extends React.PureComponent { - propTypes: { - icon: React.PropTypes.string.isRequired, - items: React.PropTypes.array.isRequired, - size: React.PropTypes.number.isRequired, - direction: React.PropTypes.string - }, - - getDefaultProps () { - return { + constructor (props, context) { + super(props, context); + this.state = { direction: 'left' }; - }, - - mixins: [PureRenderMixin], + this.setRef = this.setRef.bind(this); + this.renderItem = this.renderItem.bind(this); + } setRef (c) { this.dropdown = c; - }, + } handleClick (i, e) { const { action } = this.props.items[i]; @@ -30,7 +24,7 @@ const DropdownMenu = React.createClass({ action(); this.dropdown.hide(); } - }, + } renderItem (item, i) { if (item === null) { @@ -46,7 +40,7 @@ const DropdownMenu = React.createClass({ </a> </li> ); - }, + } render () { const { icon, items, size, direction } = this.props; @@ -67,6 +61,13 @@ const DropdownMenu = React.createClass({ ); } -}); +} + +DropdownMenu.propTypes = { + icon: PropTypes.string.isRequired, + items: PropTypes.array.isRequired, + size: PropTypes.number.isRequired, + direction: PropTypes.string +}; export default DropdownMenu; diff --git a/app/assets/javascripts/components/components/extended_video_player.jsx b/app/assets/javascripts/components/components/extended_video_player.jsx index a64515583..bbbe09da8 100644 --- a/app/assets/javascripts/components/components/extended_video_player.jsx +++ b/app/assets/javascripts/components/components/extended_video_player.jsx @@ -1,33 +1,30 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; -const ExtendedVideoPlayer = React.createClass({ +class ExtendedVideoPlayer extends React.PureComponent { - propTypes: { - src: React.PropTypes.string.isRequired, - time: React.PropTypes.number, - controls: React.PropTypes.bool.isRequired, - muted: React.PropTypes.bool.isRequired - }, - - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleLoadedData = this.handleLoadedData.bind(this); + this.setRef = this.setRef.bind(this); + } handleLoadedData () { if (this.props.time) { this.video.currentTime = this.props.time; } - }, + } componentDidMount () { this.video.addEventListener('loadeddata', this.handleLoadedData); - }, + } componentWillUnmount () { this.video.removeEventListener('loadeddata', this.handleLoadedData); - }, + } setRef (c) { this.video = c; - }, + } render () { return ( @@ -42,8 +39,15 @@ const ExtendedVideoPlayer = React.createClass({ /> </div> ); - }, + } + +} -}); +ExtendedVideoPlayer.propTypes = { + src: PropTypes.string.isRequired, + time: PropTypes.number, + controls: PropTypes.bool.isRequired, + muted: PropTypes.bool.isRequired +}; export default ExtendedVideoPlayer; diff --git a/app/assets/javascripts/components/components/icon_button.jsx b/app/assets/javascripts/components/components/icon_button.jsx index 0c683db5d..e2059fc4f 100644 --- a/app/assets/javascripts/components/components/icon_button.jsx +++ b/app/assets/javascripts/components/components/icon_button.jsx @@ -1,33 +1,12 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; import { Motion, spring } from 'react-motion'; +import PropTypes from 'prop-types'; -const IconButton = React.createClass({ - - propTypes: { - title: React.PropTypes.string.isRequired, - icon: React.PropTypes.string.isRequired, - onClick: React.PropTypes.func, - size: React.PropTypes.number, - active: React.PropTypes.bool, - style: React.PropTypes.object, - activeStyle: React.PropTypes.object, - disabled: React.PropTypes.bool, - inverted: React.PropTypes.bool, - animate: React.PropTypes.bool, - overlay: React.PropTypes.bool - }, - - getDefaultProps () { - return { - size: 18, - active: false, - disabled: false, - animate: false, - overlay: false - }; - }, +class IconButton extends React.PureComponent { - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick (e) { e.preventDefault(); @@ -35,7 +14,7 @@ const IconButton = React.createClass({ if (!this.props.disabled) { this.props.onClick(e); } - }, + } render () { let style = { @@ -84,6 +63,28 @@ const IconButton = React.createClass({ ); } -}); +} + +IconButton.propTypes = { + title: PropTypes.string.isRequired, + icon: PropTypes.string.isRequired, + onClick: PropTypes.func, + size: PropTypes.number, + active: PropTypes.bool, + style: PropTypes.object, + activeStyle: PropTypes.object, + disabled: PropTypes.bool, + inverted: PropTypes.bool, + animate: PropTypes.bool, + overlay: PropTypes.bool +}; + +IconButton.defaultProps = { + size: 18, + active: false, + disabled: false, + animate: false, + overlay: false +}; export default IconButton; diff --git a/app/assets/javascripts/components/components/load_more.jsx b/app/assets/javascripts/components/components/load_more.jsx index b4bc8b711..f59ff1103 100644 --- a/app/assets/javascripts/components/components/load_more.jsx +++ b/app/assets/javascripts/components/components/load_more.jsx @@ -1,4 +1,5 @@ import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; const LoadMore = ({ onClick }) => ( <a href="#" className='load-more' role='button' onClick={onClick}> @@ -7,7 +8,7 @@ const LoadMore = ({ onClick }) => ( ); LoadMore.propTypes = { - onClick: React.PropTypes.func + onClick: PropTypes.func }; export default LoadMore; diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/components/media_gallery.jsx index f334af9cf..ebc6e709d 100644 --- a/app/assets/javascripts/components/components/media_gallery.jsx +++ b/app/assets/javascripts/components/components/media_gallery.jsx @@ -1,5 +1,5 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; import IconButton from './icon_button'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from '../is_mobile'; @@ -72,17 +72,12 @@ const gifvThumbStyle = { cursor: 'zoom-in' }; -const Item = React.createClass({ +class Item extends React.PureComponent { - propTypes: { - attachment: ImmutablePropTypes.map.isRequired, - index: React.PropTypes.number.isRequired, - size: React.PropTypes.number.isRequired, - onClick: React.PropTypes.func.isRequired, - autoPlayGif: React.PropTypes.bool.isRequired - }, - - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick (e) { const { index, onClick } = this.props; @@ -93,7 +88,7 @@ const Item = React.createClass({ } e.stopPropagation(); - }, + } render () { const { attachment, index, size } = this.props; @@ -184,34 +179,34 @@ const Item = React.createClass({ ); } -}); +} -const MediaGallery = React.createClass({ - - getInitialState () { - return { - visible: !this.props.sensitive - }; - }, +Item.propTypes = { + attachment: ImmutablePropTypes.map.isRequired, + index: PropTypes.number.isRequired, + size: PropTypes.number.isRequired, + onClick: PropTypes.func.isRequired, + autoPlayGif: PropTypes.bool.isRequired +}; - propTypes: { - sensitive: React.PropTypes.bool, - media: ImmutablePropTypes.list.isRequired, - height: React.PropTypes.number.isRequired, - onOpenMedia: React.PropTypes.func.isRequired, - intl: React.PropTypes.object.isRequired, - autoPlayGif: React.PropTypes.bool.isRequired - }, +class MediaGallery extends React.PureComponent { - mixins: [PureRenderMixin], + constructor (props, context) { + super(props, context); + this.state = { + visible: !props.sensitive + }; + this.handleOpen = this.handleOpen.bind(this); + this.handleClick = this.handleClick.bind(this); + } handleOpen (e) { this.setState({ visible: !this.state.visible }); - }, + } handleClick (index) { this.props.onOpenMedia(this.props.media, index); - }, + } render () { const { media, intl, sensitive } = this.props; @@ -249,6 +244,15 @@ const MediaGallery = React.createClass({ ); } -}); +} + +MediaGallery.propTypes = { + sensitive: PropTypes.bool, + media: ImmutablePropTypes.list.isRequired, + height: PropTypes.number.isRequired, + onOpenMedia: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + autoPlayGif: PropTypes.bool.isRequired +}; export default injectIntl(MediaGallery); diff --git a/app/assets/javascripts/components/components/permalink.jsx b/app/assets/javascripts/components/components/permalink.jsx index c39546b53..0ad477db7 100644 --- a/app/assets/javascripts/components/components/permalink.jsx +++ b/app/assets/javascripts/components/components/permalink.jsx @@ -1,21 +1,18 @@ -const Permalink = React.createClass({ +import PropTypes from 'prop-types'; - contextTypes: { - router: React.PropTypes.object - }, +class Permalink extends React.Component { - propTypes: { - href: React.PropTypes.string.isRequired, - to: React.PropTypes.string.isRequired, - children: React.PropTypes.node - }, + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + } handleClick (e) { if (e.button === 0) { e.preventDefault(); this.context.router.push(this.props.to); } - }, + } render () { const { href, children, ...other } = this.props; @@ -23,6 +20,16 @@ const Permalink = React.createClass({ return <a href={href} onClick={this.handleClick} {...other}>{children}</a>; } -}); +} + +Permalink.contextTypes = { + router: PropTypes.object +}; + +Permalink.propTypes = { + href: PropTypes.string.isRequired, + to: PropTypes.string.isRequired, + children: PropTypes.node +}; export default Permalink; diff --git a/app/assets/javascripts/components/components/relative_timestamp.jsx b/app/assets/javascripts/components/components/relative_timestamp.jsx index 3b012b184..9ab472e2c 100644 --- a/app/assets/javascripts/components/components/relative_timestamp.jsx +++ b/app/assets/javascripts/components/components/relative_timestamp.jsx @@ -1,4 +1,5 @@ import { injectIntl, FormattedRelative } from 'react-intl'; +import PropTypes from 'prop-types'; const RelativeTimestamp = ({ intl, timestamp }) => { const date = new Date(timestamp); @@ -11,8 +12,8 @@ const RelativeTimestamp = ({ intl, timestamp }) => { }; RelativeTimestamp.propTypes = { - intl: React.PropTypes.object.isRequired, - timestamp: React.PropTypes.string.isRequired + intl: PropTypes.object.isRequired, + timestamp: PropTypes.string.isRequired }; export default injectIntl(RelativeTimestamp); diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx index a5b9ad87c..c57ee5c89 100644 --- a/app/assets/javascripts/components/components/status.jsx +++ b/app/assets/javascripts/components/components/status.jsx @@ -1,7 +1,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; import Avatar from './avatar'; import RelativeTimestamp from './relative_timestamp'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; import DisplayName from './display_name'; import MediaGallery from './media_gallery'; import VideoPlayer from './video_player'; @@ -12,41 +12,25 @@ import { FormattedMessage } from 'react-intl'; import emojify from '../emoji'; import escapeTextContentForBrowser from 'escape-html'; -const Status = React.createClass({ - - contextTypes: { - router: React.PropTypes.object - }, - - propTypes: { - status: ImmutablePropTypes.map, - wrapped: React.PropTypes.bool, - onReply: React.PropTypes.func, - onFavourite: React.PropTypes.func, - onReblog: React.PropTypes.func, - onDelete: React.PropTypes.func, - onOpenMedia: React.PropTypes.func, - onOpenVideo: React.PropTypes.func, - onBlock: React.PropTypes.func, - me: React.PropTypes.number, - boostModal: React.PropTypes.bool, - autoPlayGif: React.PropTypes.bool, - muted: React.PropTypes.bool - }, - - mixins: [PureRenderMixin], +class Status extends React.PureComponent { + + constructor (props, context) { + super(props, context); + this.handleClick = this.handleClick.bind(this); + this.handleAccountClick = this.handleAccountClick.bind(this); + } handleClick () { const { status } = this.props; this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`); - }, + } handleAccountClick (id, e) { if (e.button === 0) { e.preventDefault(); this.context.router.push(`/accounts/${id}`); } - }, + } render () { let media = ''; @@ -112,6 +96,26 @@ const Status = React.createClass({ ); } -}); +} + +Status.contextTypes = { + router: PropTypes.object +}; + +Status.propTypes = { + status: ImmutablePropTypes.map, + wrapped: PropTypes.bool, + onReply: PropTypes.func, + onFavourite: PropTypes.func, + onReblog: PropTypes.func, + onDelete: PropTypes.func, + onOpenMedia: PropTypes.func, + onOpenVideo: PropTypes.func, + onBlock: PropTypes.func, + me: PropTypes.number, + boostModal: PropTypes.bool, + autoPlayGif: PropTypes.bool, + muted: PropTypes.bool +}; export default Status; diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx index 213aa7743..b452cb8cf 100644 --- a/app/assets/javascripts/components/components/status_action_bar.jsx +++ b/app/assets/javascripts/components/components/status_action_bar.jsx @@ -1,5 +1,5 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; import IconButton from './icon_button'; import DropdownMenu from './dropdown_menu'; import { defineMessages, injectIntl } from 'react-intl'; @@ -17,64 +17,57 @@ const messages = defineMessages({ report: { id: 'status.report', defaultMessage: 'Report @{name}' } }); -const StatusActionBar = React.createClass({ - - contextTypes: { - router: React.PropTypes.object - }, - - propTypes: { - status: ImmutablePropTypes.map.isRequired, - onReply: React.PropTypes.func, - onFavourite: React.PropTypes.func, - onReblog: React.PropTypes.func, - onDelete: React.PropTypes.func, - onMention: React.PropTypes.func, - onMute: React.PropTypes.func, - onBlock: React.PropTypes.func, - onReport: React.PropTypes.func, - me: React.PropTypes.number.isRequired, - intl: React.PropTypes.object.isRequired - }, - - mixins: [PureRenderMixin], +class StatusActionBar extends React.PureComponent { + + constructor (props, context) { + super(props, context); + this.handleReplyClick = this.handleReplyClick.bind(this); + this.handleFavouriteClick = this.handleFavouriteClick.bind(this); + this.handleReblogClick = this.handleReblogClick.bind(this); + this.handleDeleteClick = this.handleDeleteClick.bind(this); + this.handleMentionClick = this.handleMentionClick.bind(this); + this.handleMuteClick = this.handleMuteClick.bind(this); + this.handleBlockClick = this.handleBlockClick.bind(this); + this.handleOpen = this.handleOpen.bind(this); + this.handleReport = this.handleReport.bind(this); + } handleReplyClick () { this.props.onReply(this.props.status, this.context.router); - }, + } handleFavouriteClick () { this.props.onFavourite(this.props.status); - }, + } handleReblogClick (e) { this.props.onReblog(this.props.status, e); - }, + } handleDeleteClick () { this.props.onDelete(this.props.status); - }, + } handleMentionClick () { this.props.onMention(this.props.status.get('account'), this.context.router); - }, + } handleMuteClick () { this.props.onMute(this.props.status.get('account')); - }, + } handleBlockClick () { this.props.onBlock(this.props.status.get('account')); - }, + } handleOpen () { this.context.router.push(`/statuses/${this.props.status.get('id')}`); - }, + } handleReport () { this.props.onReport(this.props.status); this.context.router.push('/report'); - }, + } render () { const { status, me, intl } = this.props; @@ -119,6 +112,24 @@ const StatusActionBar = React.createClass({ ); } -}); +} + +StatusActionBar.contextTypes = { + router: PropTypes.object +}; + +StatusActionBar.propTypes = { + status: ImmutablePropTypes.map.isRequired, + onReply: PropTypes.func, + onFavourite: PropTypes.func, + onReblog: PropTypes.func, + onDelete: PropTypes.func, + onMention: PropTypes.func, + onMute: PropTypes.func, + onBlock: PropTypes.func, + onReport: PropTypes.func, + me: PropTypes.number.isRequired, + intl: PropTypes.object.isRequired +}; export default injectIntl(StatusActionBar); diff --git a/app/assets/javascripts/components/components/status_content.jsx b/app/assets/javascripts/components/components/status_content.jsx index ce8ead41e..08370b189 100644 --- a/app/assets/javascripts/components/components/status_content.jsx +++ b/app/assets/javascripts/components/components/status_content.jsx @@ -1,29 +1,24 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; import escapeTextContentForBrowser from 'escape-html'; +import PropTypes from 'prop-types'; import emojify from '../emoji'; import { isRtl } from '../rtl'; import { FormattedMessage } from 'react-intl'; import Permalink from './permalink'; -const StatusContent = React.createClass({ +class StatusContent extends React.PureComponent { - contextTypes: { - router: React.PropTypes.object - }, - - propTypes: { - status: ImmutablePropTypes.map.isRequired, - onClick: React.PropTypes.func - }, - - getInitialState () { - return { + constructor (props, context) { + super(props, context); + this.state = { hidden: true }; - }, - - mixins: [PureRenderMixin], + this.onMentionClick = this.onMentionClick.bind(this); + this.onHashtagClick = this.onHashtagClick.bind(this); + this.handleMouseDown = this.handleMouseDown.bind(this) + this.handleMouseUp = this.handleMouseUp.bind(this); + this.handleSpoilerClick = this.handleSpoilerClick.bind(this); + }; componentDidMount () { const node = ReactDOM.findDOMNode(this); @@ -47,14 +42,14 @@ const StatusContent = React.createClass({ link.setAttribute('title', link.href); } } - }, + } onMentionClick (mention, e) { if (e.button === 0) { e.preventDefault(); this.context.router.push(`/accounts/${mention.get('id')}`); } - }, + } onHashtagClick (hashtag, e) { hashtag = hashtag.replace(/^#/, '').toLowerCase(); @@ -63,11 +58,11 @@ const StatusContent = React.createClass({ e.preventDefault(); this.context.router.push(`/timelines/tag/${hashtag}`); } - }, + } handleMouseDown (e) { this.startXY = [e.clientX, e.clientY]; - }, + } handleMouseUp (e) { const [ startX, startY ] = this.startXY; @@ -82,12 +77,12 @@ const StatusContent = React.createClass({ } this.startXY = null; - }, + } handleSpoilerClick (e) { e.preventDefault(); this.setState({ hidden: !this.state.hidden }); - }, + } render () { const { status } = this.props; @@ -146,8 +141,17 @@ const StatusContent = React.createClass({ /> ); } - }, + } + +} + +StatusContent.contextTypes = { + router: PropTypes.object +}; -}); +StatusContent.propTypes = { + status: ImmutablePropTypes.map.isRequired, + onClick: PropTypes.func +}; export default StatusContent; diff --git a/app/assets/javascripts/components/components/status_list.jsx b/app/assets/javascripts/components/components/status_list.jsx index 345944e4d..7ba8bad1d 100644 --- a/app/assets/javascripts/components/components/status_list.jsx +++ b/app/assets/javascripts/components/components/status_list.jsx @@ -1,32 +1,18 @@ import Status from './status'; import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; import { ScrollContainer } from 'react-router-scroll'; +import PropTypes from 'prop-types'; import StatusContainer from '../containers/status_container'; import LoadMore from './load_more'; -const StatusList = React.createClass({ - - propTypes: { - statusIds: ImmutablePropTypes.list.isRequired, - onScrollToBottom: React.PropTypes.func, - onScrollToTop: React.PropTypes.func, - onScroll: React.PropTypes.func, - trackScroll: React.PropTypes.bool, - isLoading: React.PropTypes.bool, - isUnread: React.PropTypes.bool, - hasMore: React.PropTypes.bool, - prepend: React.PropTypes.node, - emptyMessage: React.PropTypes.node - }, - - getDefaultProps () { - return { - trackScroll: true - }; - }, - - mixins: [PureRenderMixin], +class StatusList extends React.PureComponent { + + constructor (props, context) { + super(props, context); + this.handleScroll = this.handleScroll.bind(this); + this.setRef = this.setRef.bind(this); + this.handleLoadMore = this.handleLoadMore.bind(this); + } handleScroll (e) { const { scrollTop, scrollHeight, clientHeight } = e.target; @@ -40,38 +26,38 @@ const StatusList = React.createClass({ } else if (this.props.onScroll) { this.props.onScroll(); } - }, + } componentDidMount () { this.attachScrollListener(); - }, + } componentDidUpdate (prevProps) { if (this.node.scrollTop > 0 && (prevProps.statusIds.size < this.props.statusIds.size && prevProps.statusIds.first() !== this.props.statusIds.first() && !!this._oldScrollPosition)) { this.node.scrollTop = this.node.scrollHeight - this._oldScrollPosition; } - }, + } componentWillUnmount () { this.detachScrollListener(); - }, + } attachScrollListener () { this.node.addEventListener('scroll', this.handleScroll); - }, + } detachScrollListener () { this.node.removeEventListener('scroll', this.handleScroll); - }, + } setRef (c) { this.node = c; - }, + } handleLoadMore (e) { e.preventDefault(); this.props.onScrollToBottom(); - }, + } render () { const { statusIds, onScrollToBottom, trackScroll, isLoading, isUnread, hasMore, prepend, emptyMessage } = this.props; @@ -123,6 +109,23 @@ const StatusList = React.createClass({ } } -}); +} + +StatusList.propTypes = { + statusIds: ImmutablePropTypes.list.isRequired, + onScrollToBottom: PropTypes.func, + onScrollToTop: PropTypes.func, + onScroll: PropTypes.func, + trackScroll: PropTypes.bool, + isLoading: PropTypes.bool, + isUnread: PropTypes.bool, + hasMore: PropTypes.bool, + prepend: PropTypes.node, + emptyMessage: PropTypes.node +}; + +StatusList.defaultProps = { + trackScroll: true +}; export default StatusList; diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx index 0da544746..50a69a759 100644 --- a/app/assets/javascripts/components/components/video_player.jsx +++ b/app/assets/javascripts/components/components/video_player.jsx @@ -1,5 +1,5 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; -import PureRenderMixin from 'react-addons-pure-render-mixin'; +import PropTypes from 'prop-types'; import IconButton from './icon_button'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { isIOS } from '../is_mobile'; @@ -72,39 +72,30 @@ const expandButtonStyle = { zIndex: '100' }; -const VideoPlayer = React.createClass({ - propTypes: { - media: ImmutablePropTypes.map.isRequired, - width: React.PropTypes.number, - height: React.PropTypes.number, - sensitive: React.PropTypes.bool, - intl: React.PropTypes.object.isRequired, - autoplay: React.PropTypes.bool, - onOpenVideo: React.PropTypes.func.isRequired - }, - - getDefaultProps () { - return { - width: 239, - height: 110 - }; - }, +class VideoPlayer extends React.PureComponent { - getInitialState () { - return { + constructor (props, context) { + super(props, context); + this.state = { visible: !this.props.sensitive, preview: true, muted: true, hasAudio: true, videoError: false }; - }, - - mixins: [PureRenderMixin], + this.handleClick = this.handleClick.bind(this); + this.handleVideoClick = this.handleVideoClick.bind(this); + this.handleOpen = this.handleOpen.bind(this); + this.handleVisibility = this.handleVisibility.bind(this); + this.handleExpand = this.handleExpand.bind(this); + this.setRef = this.setRef.bind(this); + this.handleLoadedData = this.handleLoadedData.bind(this); + this.handleVideoError = this.handleVideoError.bind(this); + } handleClick () { this.setState({ muted: !this.state.muted }); - }, + } handleVideoClick (e) { e.stopPropagation(); @@ -116,37 +107,37 @@ const VideoPlayer = React.createClass({ } else { node.pause(); } - }, + } handleOpen () { this.setState({ preview: !this.state.preview }); - }, + } handleVisibility () { this.setState({ visible: !this.state.visible, preview: true }); - }, + } handleExpand () { this.video.pause(); this.props.onOpenVideo(this.props.media, this.video.currentTime); - }, + } setRef (c) { this.video = c; - }, + } handleLoadedData () { if (('WebkitAppearance' in document.documentElement.style && this.video.audioTracks.length === 0) || this.video.mozHasAudio === false) { this.setState({ hasAudio: false }); } - }, + } handleVideoError () { this.setState({ videoError: true }); - }, + } componentDidMount () { if (!this.video) { @@ -155,7 +146,7 @@ const VideoPlayer = React.createClass({ this.video.addEventListener('loadeddata', this.handleLoadedData); this.video.addEventListener('error', this.handleVideoError); - }, + } componentDidUpdate () { if (!this.video) { @@ -164,7 +155,7 @@ const VideoPlayer = React.createClass({ this.video.addEventListener('loadeddata', this.handleLoadedData); this.video.addEventListener('error', this.handleVideoError); - }, + } componentWillUnmount () { if (!this.video) { @@ -173,7 +164,7 @@ const VideoPlayer = React.createClass({ this.video.removeEventListener('loadeddata', this.handleLoadedData); this.video.removeEventListener('error', this.handleVideoError); - }, + } render () { const { media, intl, width, height, sensitive, autoplay } = this.props; @@ -247,6 +238,21 @@ const VideoPlayer = React.createClass({ ); } -}); +} + +VideoPlayer.propTypes = { + media: ImmutablePropTypes.map.isRequired, + width: PropTypes.number, + height: PropTypes.number, + sensitive: PropTypes.bool, + intl: PropTypes.object.isRequired, + autoplay: PropTypes.bool, + onOpenVideo: PropTypes.func.isRequired +}; + +VideoPlayer.defaultProps = { + width: 239, + height: 110 +}; export default injectIntl(VideoPlayer); |