diff options
Diffstat (limited to 'app/assets/javascripts/components/features')
9 files changed, 53 insertions, 121 deletions
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx index cd01de2e2..f09dea6ab 100644 --- a/app/assets/javascripts/components/features/account/components/action_bar.jsx +++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx @@ -58,10 +58,8 @@ const ActionBar = React.createClass({ } else if (account.getIn(['relationship', 'blocking'])) { menu.push({ text: intl.formatMessage(messages.unblock), action: this.props.onBlock }); } else if (account.getIn(['relationship', 'following'])) { - menu.push({ text: intl.formatMessage(messages.unfollow), action: this.props.onFollow }); menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); } else { - menu.push({ text: intl.formatMessage(messages.follow), action: this.props.onFollow }); menu.push({ text: intl.formatMessage(messages.block), action: this.props.onBlock }); } diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx index b3e9e2a9f..b890e15c1 100644 --- a/app/assets/javascripts/components/features/account/components/header.jsx +++ b/app/assets/javascripts/components/features/account/components/header.jsx @@ -2,22 +2,30 @@ import PureRenderMixin from 'react-addons-pure-render-mixin'; import ImmutablePropTypes from 'react-immutable-proptypes'; import emojify from '../../../emoji'; import escapeTextContentForBrowser from 'react/lib/escapeTextContentForBrowser'; -import { FormattedMessage } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import IconButton from '../../../components/icon_button'; + +const messages = defineMessages({ + unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, + follow: { id: 'account.follow', defaultMessage: 'Follow' }, +}); const Header = React.createClass({ propTypes: { account: ImmutablePropTypes.map.isRequired, - me: React.PropTypes.number.isRequired + me: React.PropTypes.number.isRequired, + onFollow: React.PropTypes.func.isRequired }, mixins: [PureRenderMixin], render () { - const { account, me } = this.props; + const { account, me, intl } = this.props; let displayName = account.get('display_name'); let info = ''; + let actionBtn = ''; if (displayName.length === 0) { displayName = account.get('username'); @@ -27,11 +35,19 @@ const Header = React.createClass({ info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}><FormattedMessage id='account.follows_you' defaultMessage='Follows you' /></span> } + if (me !== account.get('id')) { + actionBtn = ( + <div style={{ position: 'absolute', top: '10px', left: '20px' }}> + <IconButton size={26} icon={account.getIn(['relationship', 'following']) ? 'user-times' : 'user-plus'} active={account.getIn(['relationship', 'following'])} title={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={this.props.onFollow} /> + </div> + ); + } + const content = { __html: emojify(account.get('note')) }; const displayNameHTML = { __html: emojify(escapeTextContentForBrowser(displayName)) }; return ( - <div style={{ flex: '0 0 auto', background: '#2f3441', textAlign: 'center', backgroundImage: `url(${account.get('header')})`, backgroundSize: 'cover', position: 'relative' }}> + <div style={{ flex: '0 0 auto', background: '#2f3441', textAlign: 'center', backgroundImage: `url(${account.get('header')})`, backgroundSize: 'cover', backgroundPosition: 'center', position: 'relative' }}> <div style={{ background: 'rgba(47, 52, 65, 0.9)', padding: '20px 10px' }}> <a href={account.get('url')} target='_blank' rel='noopener' style={{ display: 'block', color: 'inherit', textDecoration: 'none' }}> <div style={{ width: '90px', margin: '0 auto', marginBottom: '10px' }}> @@ -45,6 +61,7 @@ const Header = React.createClass({ <div style={{ color: '#616b86', fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} /> {info} + {actionBtn} </div> </div> ); @@ -52,4 +69,4 @@ const Header = React.createClass({ }); -export default Header; +export default injectIntl(Header); diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx index 818979f8f..c2cc58bb2 100644 --- a/app/assets/javascripts/components/features/account/index.jsx +++ b/app/assets/javascripts/components/features/account/index.jsx @@ -87,9 +87,8 @@ const Account = React.createClass({ return ( <Column> <ColumnBackButton /> - <Header account={account} me={me} /> - - <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} onMention={this.handleMention} /> + <Header account={account} me={me} onFollow={this.handleFollow} /> + <ActionBar account={account} me={me} onBlock={this.handleBlock} onMention={this.handleMention} /> {this.props.children} </Column> diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index 32bdeaeca..b16731c05 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -8,7 +8,8 @@ import Autosuggest from 'react-autosuggest'; import AutosuggestAccountContainer from '../../compose/containers/autosuggest_account_container'; import { debounce } from 'react-decoration'; import UploadButtonContainer from '../containers/upload_button_container'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import Toggle from 'react-toggle'; const messages = defineMessages({ placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }, @@ -54,7 +55,8 @@ const textareaStyle = { padding: '10px', fontFamily: 'Roboto', fontSize: '14px', - margin: '0' + margin: '0', + resize: 'vertical' }; const renderInputComponent = inputProps => ( @@ -67,6 +69,7 @@ const ComposeForm = React.createClass({ text: React.PropTypes.string.isRequired, suggestion_token: React.PropTypes.string, suggestions: React.PropTypes.array, + sensitive: React.PropTypes.bool, is_submitting: React.PropTypes.bool, is_uploading: React.PropTypes.bool, in_reply_to: ImmutablePropTypes.map, @@ -75,7 +78,8 @@ const ComposeForm = React.createClass({ onCancelReply: React.PropTypes.func.isRequired, onClearSuggestions: React.PropTypes.func.isRequired, onFetchSuggestions: React.PropTypes.func.isRequired, - onSuggestionSelected: React.PropTypes.func.isRequired + onSuggestionSelected: React.PropTypes.func.isRequired, + onChangeSensitivity: React.PropTypes.func.isRequired }, mixins: [PureRenderMixin], @@ -139,6 +143,10 @@ const ComposeForm = React.createClass({ this.autosuggest = c; }, + handleChangeSensitivity (e) { + this.props.onChangeSensitivity(e.target.checked); + }, + render () { const { intl } = this.props; let replyArea = ''; @@ -178,6 +186,11 @@ const ComposeForm = React.createClass({ <div style={{ float: 'right', marginRight: '16px', lineHeight: '36px' }}><CharacterCounter max={500} text={this.props.text} /></div> <UploadButtonContainer style={{ paddingTop: '4px' }} /> </div> + + <label style={{ display: 'block', lineHeight: '24px', verticalAlign: 'middle', marginTop: '10px', borderTop: '1px solid #616b86', paddingTop: '10px' }}> + <Toggle checked={this.props.sensitive} onChange={this.handleChangeSensitivity} /> + <span style={{ display: 'inline-block', verticalAlign: 'middle', marginBottom: '14px', marginLeft: '8px', color: '#9baec8' }}><FormattedMessage id='compose_form.sensitive' defaultMessage='Mark content as sensitive' /></span> + </label> </div> ); } diff --git a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx b/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx deleted file mode 100644 index 6850629ba..000000000 --- a/app/assets/javascripts/components/features/compose/components/suggestions_box.jsx +++ /dev/null @@ -1,86 +0,0 @@ -import PureRenderMixin from 'react-addons-pure-render-mixin'; -import ImmutablePropTypes from 'react-immutable-proptypes'; -import AccountContainer from '../../../containers/account_container'; -import { FormattedMessage } from 'react-intl'; - -const outerStyle = { - position: 'relative' -}; - -const headerStyle = { - fontSize: '14px', - fontWeight: '500', - display: 'block', - padding: '10px', - color: '#9baec8', - background: '#454b5e', - overflow: 'hidden' -}; - -const nextStyle = { - display: 'inline-block', - float: 'right', - fontWeight: '400', - color: '#2b90d9' -}; - -const SuggestionsBox = React.createClass({ - - propTypes: { - accountIds: ImmutablePropTypes.list, - perWindow: React.PropTypes.number - }, - - getInitialState () { - return { - index: 0 - }; - }, - - getDefaultProps () { - return { - perWindow: 2 - }; - }, - - mixins: [PureRenderMixin], - - handleNextClick (e) { - e.preventDefault(); - - let newIndex = this.state.index + 1; - - if (this.props.accountIds.skip(this.props.perWindow * newIndex).size === 0) { - newIndex = 0; - } - - this.setState({ index: newIndex }); - }, - - render () { - const { accountIds, perWindow } = this.props; - - if (!accountIds || accountIds.size === 0) { - return <div />; - } - - let nextLink = ''; - - if (accountIds.size > perWindow) { - nextLink = <a href='#' style={nextStyle} onClick={this.handleNextClick}><FormattedMessage id='suggestions_box.refresh' defaultMessage='Refresh' /></a>; - } - - return ( - <div style={outerStyle}> - <strong style={headerStyle}> - <FormattedMessage id='suggestions_box.who_to_follow' defaultMessage='Who to follow' /> {nextLink} - </strong> - - {accountIds.skip(perWindow * this.state.index).take(perWindow).map(accountId => <AccountContainer key={accountId} id={accountId} withNote={false} />)} - </div> - ); - } - -}); - -export default SuggestionsBox; diff --git a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx index 87bcd6b99..9897f6505 100644 --- a/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx +++ b/app/assets/javascripts/components/features/compose/containers/compose_form_container.jsx @@ -6,7 +6,8 @@ import { cancelReplyCompose, clearComposeSuggestions, fetchComposeSuggestions, - selectComposeSuggestion + selectComposeSuggestion, + changeComposeSensitivity } from '../../../actions/compose'; import { makeGetStatus } from '../../../selectors'; @@ -18,6 +19,7 @@ const makeMapStateToProps = () => { text: state.getIn(['compose', 'text']), suggestion_token: state.getIn(['compose', 'suggestion_token']), suggestions: state.getIn(['compose', 'suggestions']).toJS(), + sensitive: state.getIn(['compose', 'sensitive']), is_submitting: state.getIn(['compose', 'is_submitting']), is_uploading: state.getIn(['compose', 'is_uploading']), in_reply_to: getStatus(state, state.getIn(['compose', 'in_reply_to'])) @@ -51,6 +53,10 @@ const mapDispatchToProps = function (dispatch) { onSuggestionSelected (position, accountId) { dispatch(selectComposeSuggestion(position, accountId)); + }, + + onChangeSensitivity (checked) { + dispatch(changeComposeSensitivity(checked)); } } }; diff --git a/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx b/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx deleted file mode 100644 index 944ceed85..000000000 --- a/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import { connect } from 'react-redux'; -import SuggestionsBox from '../components/suggestions_box'; - -const mapStateToProps = (state) => ({ - accountIds: state.getIn(['user_lists', 'suggestions']) -}); - -export default connect(mapStateToProps)(SuggestionsBox); diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx index 5c1b22e00..4017c8949 100644 --- a/app/assets/javascripts/components/features/compose/index.jsx +++ b/app/assets/javascripts/components/features/compose/index.jsx @@ -3,9 +3,7 @@ import ComposeFormContainer from './containers/compose_form_container'; import UploadFormContainer from './containers/upload_form_container'; import NavigationContainer from './containers/navigation_container'; import PureRenderMixin from 'react-addons-pure-render-mixin'; -import SuggestionsContainer from './containers/suggestions_container'; import SearchContainer from './containers/search_container'; -import { fetchSuggestions } from '../../actions/suggestions'; import { connect } from 'react-redux'; import { mountCompose, unmountCompose } from '../../actions/compose'; @@ -19,7 +17,6 @@ const Compose = React.createClass({ componentDidMount () { this.props.dispatch(mountCompose()); - this.props.dispatch(fetchSuggestions()); }, componentWillUnmount () { @@ -29,14 +26,10 @@ const Compose = React.createClass({ render () { return ( <Drawer> - <div style={{ flex: '1 1 auto' }}> - <SearchContainer /> - <NavigationContainer /> - <ComposeFormContainer /> - <UploadFormContainer /> - </div> - - <SuggestionsContainer /> + <SearchContainer /> + <NavigationContainer /> + <ComposeFormContainer /> + <UploadFormContainer /> </Drawer> ); } diff --git a/app/assets/javascripts/components/features/status/components/detailed_status.jsx b/app/assets/javascripts/components/features/status/components/detailed_status.jsx index 76ddafb3b..b967d966f 100644 --- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx +++ b/app/assets/javascripts/components/features/status/components/detailed_status.jsx @@ -36,9 +36,9 @@ const DetailedStatus = React.createClass({ if (status.get('media_attachments').size > 0) { if (status.getIn(['media_attachments', 0, 'type']) === 'video') { - media = <VideoPlayer media={status.getIn(['media_attachments', 0])} width={317} height={178} />; + media = <VideoPlayer sensitive={status.get('sensitive')} media={status.getIn(['media_attachments', 0])} width={317} height={178} />; } else { - media = <MediaGallery media={status.get('media_attachments')} height={300} onOpenMedia={this.props.onOpenMedia} />; + media = <MediaGallery sensitive={status.get('sensitive')} media={status.get('media_attachments')} height={300} onOpenMedia={this.props.onOpenMedia} />; } } |