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 59a77923b3
This commit is contained in:
Yamagishi Kazutoshi 2017-04-22 03:05:35 +09:00 committed by Eugen
parent 27ea2a88c1
commit 1948f9e767
83 changed files with 1441 additions and 1291 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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({
class AutosuggestTextarea extends React.Component {
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 {
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;

View File

@ -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;

View File

@ -1,31 +1,17 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import PropTypes from 'prop-types';
const Button = React.createClass({
class Button extends React.PureComponent {
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
};
},
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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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({
class IconButton extends React.PureComponent {
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
};
},
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;

View File

@ -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;

View File

@ -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({
Item.propTypes = {
attachment: ImmutablePropTypes.map.isRequired,
index: PropTypes.number.isRequired,
size: PropTypes.number.isRequired,
onClick: PropTypes.func.isRequired,
autoPlayGif: PropTypes.bool.isRequired
};
getInitialState () {
return {
visible: !this.props.sensitive
class MediaGallery extends React.PureComponent {
constructor (props, context) {
super(props, context);
this.state = {
visible: !props.sensitive
};
},
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
},
mixins: [PureRenderMixin],
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);

View File

@ -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;

View File

@ -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);

View File

@ -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({
class Status extends React.PureComponent {
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],
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;

View File

@ -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({
class StatusActionBar extends React.PureComponent {
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],
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);

View File

@ -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;

View File

@ -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({
class StatusList extends React.PureComponent {
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],
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);
},
}