diff options
12 files changed, 65 insertions, 28 deletions
diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/components/media_gallery.jsx index 325fd8157..c6c726a4e 100644 --- a/app/assets/javascripts/components/components/media_gallery.jsx +++ b/app/assets/javascripts/components/components/media_gallery.jsx @@ -78,7 +78,8 @@ const Item = React.createClass({ attachment: ImmutablePropTypes.map.isRequired, index: React.PropTypes.number.isRequired, size: React.PropTypes.number.isRequired, - onClick: React.PropTypes.func.isRequired + onClick: React.PropTypes.func.isRequired, + autoPlayGif: React.PropTypes.bool.isRequired }, mixins: [PureRenderMixin], @@ -158,16 +159,24 @@ const Item = React.createClass({ /> ); } else if (attachment.get('type') === 'gifv') { - thumbnail = ( - <video - src={attachment.get('url')} - onClick={this.handleClick} - autoPlay={!isIOS()} - loop={true} - muted={true} - style={gifvThumbStyle} - /> - ); + if (isIOS() || !this.props.autoPlayGif) { + return ( + <div key={attachment.get('id')} style={{ ...itemStyle, background: `url(${attachment.get('preview_url')}) no-repeat center`, left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }} onClick={this.handleClick}> + <div style={{ position: 'absolute', top: '50%', left: '50%', fontSize: '36px', transform: 'translate(-50%, -50%)', padding: '5px', borderRadius: '100px', color: 'rgba(255, 255, 255, 0.8)' }}><i className='fa fa-play' /></div> + </div> + ); + } else { + thumbnail = ( + <video + src={attachment.get('url')} + onClick={this.handleClick} + autoPlay + loop={true} + muted={true} + style={gifvThumbStyle} + /> + ); + } } return ( @@ -192,7 +201,8 @@ const MediaGallery = React.createClass({ media: ImmutablePropTypes.list.isRequired, height: React.PropTypes.number.isRequired, onOpenMedia: React.PropTypes.func.isRequired, - intl: React.PropTypes.object.isRequired + intl: React.PropTypes.object.isRequired, + autoPlayGif: React.PropTypes.bool.isRequired }, mixins: [PureRenderMixin], @@ -227,7 +237,7 @@ const MediaGallery = React.createClass({ ); } else { const size = media.take(4).size; - children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} />); + 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} />); } return ( diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx index d2d2aaf20..abc123f26 100644 --- a/app/assets/javascripts/components/components/status.jsx +++ b/app/assets/javascripts/components/components/status.jsx @@ -29,6 +29,7 @@ const Status = React.createClass({ onBlock: React.PropTypes.func, me: React.PropTypes.number, boostModal: React.PropTypes.bool, + autoPlayGif: React.PropTypes.bool, muted: React.PropTypes.bool }, @@ -79,7 +80,7 @@ const Status = React.createClass({ if (status.getIn(['media_attachments', 0, 'type']) === 'video') { media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} onOpenVideo={this.props.onOpenVideo} />; } else { - media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} />; + media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />; } } diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/assets/javascripts/components/containers/status_container.jsx index f704ac722..df091de04 100644 --- a/app/assets/javascripts/components/containers/status_container.jsx +++ b/app/assets/javascripts/components/containers/status_container.jsx @@ -27,7 +27,8 @@ const makeMapStateToProps = () => { const mapStateToProps = (state, props) => ({ status: getStatus(state, props.id), me: state.getIn(['meta', 'me']), - boostModal: state.getIn(['meta', 'boost_modal']) + boostModal: state.getIn(['meta', 'boost_modal']), + autoPlayGif: state.getIn(['meta', 'auto_play_gif']) }); return mapStateToProps; diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx index c4619a3c7..c097fbbd6 100644 --- a/app/assets/javascripts/components/features/account/components/header.jsx +++ b/app/assets/javascripts/components/features/account/components/header.jsx @@ -5,6 +5,7 @@ import escapeTextContentForBrowser from 'escape-html'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import IconButton from '../../../components/icon_button'; import { Motion, spring } from 'react-motion'; +import { connect } from 'react-redux'; const messages = defineMessages({ unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -12,10 +13,19 @@ const messages = defineMessages({ requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' } }); +const makeMapStateToProps = () => { + const mapStateToProps = (state, props) => ({ + autoPlayGif: state.getIn(['meta', 'auto_play_gif']) + }); + + return mapStateToProps; +}; + const Avatar = React.createClass({ propTypes: { - account: ImmutablePropTypes.map.isRequired + account: ImmutablePropTypes.map.isRequired, + autoPlayGif: React.PropTypes.bool.isRequired }, getInitialState () { @@ -37,7 +47,7 @@ const Avatar = React.createClass({ }, render () { - const { account } = this.props; + const { account, autoPlayGif } = this.props; const { isHovered } = this.state; return ( @@ -53,7 +63,7 @@ const Avatar = React.createClass({ onMouseOut={this.handleMouseOut} onFocus={this.handleMouseOver} onBlur={this.handleMouseOut}> - <img src={account.get('avatar')} alt={account.get('acct')} style={{ display: 'block', width: '90px', height: '90px' }} /> + <img src={autoPlayGif || isHovered ? account.get('avatar') : account.get('avatar_static')} alt={account.get('acct')} style={{ display: 'block', width: '90px', height: '90px' }} /> </a> } </Motion> @@ -68,7 +78,8 @@ const Header = React.createClass({ account: ImmutablePropTypes.map, me: React.PropTypes.number.isRequired, onFollow: React.PropTypes.func.isRequired, - intl: React.PropTypes.object.isRequired + intl: React.PropTypes.object.isRequired, + autoPlayGif: React.PropTypes.bool.isRequired }, mixins: [PureRenderMixin], @@ -119,7 +130,7 @@ const Header = React.createClass({ return ( <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}> <div style={{ padding: '20px 10px' }}> - <Avatar account={account} /> + <Avatar account={account} autoPlayGif={this.props.autoPlayGif} /> <span style={{ display: 'inline-block', fontSize: '20px', lineHeight: '27px', fontWeight: '500' }} className='account__header__display-name' dangerouslySetInnerHTML={displayNameHTML} /> <span className='account__header__username' style={{ fontSize: '14px', fontWeight: '400', display: 'block', marginBottom: '10px' }}>@{account.get('acct')} {lockedIcon}</span> @@ -134,4 +145,4 @@ const Header = React.createClass({ }); -export default injectIntl(Header); +export default connect(makeMapStateToProps)(injectIntl(Header)); 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 ceafc1a32..bd386b251 100644 --- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx +++ b/app/assets/javascripts/components/features/status/components/detailed_status.jsx @@ -19,6 +19,7 @@ const DetailedStatus = React.createClass({ status: ImmutablePropTypes.map.isRequired, onOpenMedia: React.PropTypes.func.isRequired, onOpenVideo: React.PropTypes.func.isRequired, + autoPlayGif: React.PropTypes.bool, }, mixins: [PureRenderMixin], @@ -42,7 +43,7 @@ const DetailedStatus = React.createClass({ if (status.getIn(['media_attachments', 0, 'type']) === 'video') { media = <VideoPlayer sensitive={status.get('sensitive')} media={status.getIn(['media_attachments', 0])} width={300} height={150} onOpenVideo={this.props.onOpenVideo} autoplay />; } else { - media = <MediaGallery sensitive={status.get('sensitive')} 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} autoPlayGif={this.props.autoPlayGif} />; } } else { media = <CardContainer statusId={status.get('id')} />; diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx index 7ead68807..ca6e08cdc 100644 --- a/app/assets/javascripts/components/features/status/index.jsx +++ b/app/assets/javascripts/components/features/status/index.jsx @@ -39,7 +39,8 @@ const makeMapStateToProps = () => { ancestorsIds: state.getIn(['timelines', 'ancestors', Number(props.params.statusId)]), descendantsIds: state.getIn(['timelines', 'descendants', Number(props.params.statusId)]), me: state.getIn(['meta', 'me']), - boostModal: state.getIn(['meta', 'boost_modal']) + boostModal: state.getIn(['meta', 'boost_modal']), + autoPlayGif: state.getIn(['meta', 'auto_play_gif']) }); return mapStateToProps; @@ -57,7 +58,8 @@ const Status = React.createClass({ ancestorsIds: ImmutablePropTypes.list, descendantsIds: ImmutablePropTypes.list, me: React.PropTypes.number, - boostModal: React.PropTypes.bool + boostModal: React.PropTypes.bool, + autoPlayGif: React.PropTypes.bool }, mixins: [PureRenderMixin], @@ -126,7 +128,7 @@ const Status = React.createClass({ render () { let ancestors, descendants; - const { status, ancestorsIds, descendantsIds, me } = this.props; + const { status, ancestorsIds, descendantsIds, me, autoPlayGif } = this.props; if (status === null) { return ( @@ -155,7 +157,7 @@ const Status = React.createClass({ <div className='scrollable'> {ancestors} - <DetailedStatus status={status} me={me} onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} /> + <DetailedStatus status={status} autoPlayGif={autoPlayGif} me={me} onOpenVideo={this.handleOpenVideo} onOpenMedia={this.handleOpenMedia} /> <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} onMention={this.handleMentionClick} onReport={this.handleReport} /> {descendants} diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index c758e4ef2..f66eb9752 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -24,8 +24,9 @@ class Settings::PreferencesController < ApplicationController current_user.settings['default_privacy'] = user_params[:setting_default_privacy] current_user.settings['boost_modal'] = user_params[:setting_boost_modal] == '1' + current_user.settings['auto_play_gif'] = user_params[:setting_auto_play_gif] == '1' - if current_user.update(user_params.except(:notification_emails, :interactions, :setting_default_privacy, :setting_boost_modal)) + if current_user.update(user_params.except(:notification_emails, :interactions, :setting_default_privacy, :setting_boost_modal, :setting_auto_play_gif)) redirect_to settings_preferences_path, notice: I18n.t('generic.changes_saved_msg') else render action: :show @@ -35,6 +36,6 @@ class Settings::PreferencesController < ApplicationController private def user_params - params.require(:user).permit(:locale, :setting_default_privacy, :setting_boost_modal, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention, :digest], interactions: [:must_be_follower, :must_be_following]) + params.require(:user).permit(:locale, :setting_default_privacy, :setting_boost_modal, :setting_auto_play_gif, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention, :digest], interactions: [:must_be_follower, :must_be_following]) end end diff --git a/app/models/user.rb b/app/models/user.rb index 27a38674e..d50101baf 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -32,4 +32,8 @@ class User < ApplicationRecord def setting_boost_modal settings.boost_modal end + + def setting_auto_play_gif + settings.auto_play_gif + end end diff --git a/app/views/home/initial_state.json.rabl b/app/views/home/initial_state.json.rabl index ce7bfbd44..a2ab2d060 100644 --- a/app/views/home/initial_state.json.rabl +++ b/app/views/home/initial_state.json.rabl @@ -9,6 +9,7 @@ node(:meta) do me: current_account.id, admin: @admin.try(:id), boost_modal: current_account.user.setting_boost_modal, + auto_play_gif: current_account.user.setting_auto_play_gif, } end diff --git a/app/views/settings/preferences/show.html.haml b/app/views/settings/preferences/show.html.haml index e819429b6..3fdcca041 100644 --- a/app/views/settings/preferences/show.html.haml +++ b/app/views/settings/preferences/show.html.haml @@ -25,5 +25,8 @@ .fields-group = f.input :setting_boost_modal, as: :boolean, wrapper: :with_label + .fields-group + = f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label + .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index c25407f2b..5335b0927 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -28,6 +28,7 @@ en: note: Bio otp_attempt: Two-factor code password: Password + setting_auto_play_gif: Auto-play animated GIFs setting_boost_modal: Show confirmation dialog before boosting setting_default_privacy: Post privacy severity: Severity diff --git a/config/settings.yml b/config/settings.yml index 04213fd0b..9813963b2 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -15,6 +15,7 @@ defaults: &defaults open_registrations: true closed_registrations_message: '' boost_modal: false + auto_play_gif: true notification_emails: follow: false reblog: false |