diff --git a/CHANGELOG.md b/CHANGELOG.md index 222b7411d4..f183b6f5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,39 @@ Changelog All notable changes to this project will be documented in this file. +## [2.8.4] - 2019-05-24 +### Fixed + +- Fix delivery not retrying on some inbox errors that should be retriable ([ThibG](https://github.com/tootsuite/mastodon/pull/10812)) +- Fix unnecessary 5 minute cooldowns on signature verifications in some cases ([ThibG](https://github.com/tootsuite/mastodon/pull/10813)) +- Fix possible race condition when processing statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10815)) + +### Security + +- Require specific OAuth scopes for specific endpoints of the streaming API, instead of merely requiring a token for all endpoints, and allow using WebSockets protocol negotiation to specify the access token instead of using a query string ([ThibG](https://github.com/tootsuite/mastodon/pull/10818)) + +## [2.8.3] - 2019-05-19 +### Added + +- Add `og:image:alt` OpenGraph tag ([BenLubar](https://github.com/tootsuite/mastodon/pull/10779)) +- Add clickable area below avatar in statuses in web UI ([Dar13](https://github.com/tootsuite/mastodon/pull/10766)) +- Add crossed-out eye icon on account gallery in web UI ([Kjwon15](https://github.com/tootsuite/mastodon/pull/10715)) +- Add media description tooltip to thumbnails in web UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10713)) + +### Changed + +- Change "mark as sensitive" button into a checkbox for clarity ([ThibG](https://github.com/tootsuite/mastodon/pull/10748)) + +### Fixed + +- Fix bug allowing users to publicly boost their private statuses ([ThibG](https://github.com/tootsuite/mastodon/pull/10775), [ThibG](https://github.com/tootsuite/mastodon/pull/10783)) +- Fix performance in formatter by a little ([ThibG](https://github.com/tootsuite/mastodon/pull/10765)) +- Fix some colors in the light theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10754)) +- Fix some colors of the high contrast theme ([yuzulabo](https://github.com/tootsuite/mastodon/pull/10711)) +- Fix ambivalent active state of poll refresh button in web UI ([MaciekBaron](https://github.com/tootsuite/mastodon/pull/10720)) +- Fix duplicate posting being possible from web UI ([hinaloe](https://github.com/tootsuite/mastodon/pull/10785)) +- Fix "invited by" not showing up in admin UI ([ThibG](https://github.com/tootsuite/mastodon/pull/10791)) + ## [2.8.2] - 2019-05-05 ### Added diff --git a/Dockerfile b/Dockerfile index 6373172fcb..46631cde41 100644 --- a/Dockerfile +++ b/Dockerfile @@ -86,7 +86,7 @@ RUN apt update && \ useradd -m -u $UID -g $GID -d /opt/mastodon mastodon && \ echo "mastodon:`head /dev/urandom | tr -dc A-Za-z0-9 | head -c 24 | mkpasswd -s -m sha-256`" | chpasswd -# Install masto runtime deps +# Install mastodon runtime deps RUN apt -y --no-install-recommends install \ libssl1.1 libpq5 imagemagick ffmpeg \ libicu60 libprotobuf10 libidn11 libyaml-0-2 \ @@ -95,7 +95,7 @@ RUN apt -y --no-install-recommends install \ ln -s /opt/mastodon /mastodon && \ gem install bundler && \ rm -rf /var/cache && \ - rm -rf /var/lib/apt + rm -rf /var/lib/apt/lists/* # Add tini ENV TINI_VERSION="0.18.0" @@ -104,11 +104,11 @@ ADD https://github.com/krallin/tini/releases/download/v${TINI_VERSION}/tini /tin RUN echo "$TINI_SUM tini" | sha256sum -c - RUN chmod +x /tini -# Copy over masto source, and dependencies from building, and set permissions +# Copy over mastodon source, and dependencies from building, and set permissions COPY --chown=mastodon:mastodon . /opt/mastodon COPY --from=build-dep --chown=mastodon:mastodon /opt/mastodon /opt/mastodon -# Run masto services in prod mode +# Run mastodon services in prod mode ENV RAILS_ENV="production" ENV NODE_ENV="production" diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb index 3d98d583c8..a713ee5586 100644 --- a/app/controllers/settings/preferences_controller.rb +++ b/app/controllers/settings/preferences_controller.rb @@ -46,6 +46,7 @@ class Settings::PreferencesController < Settings::BaseController :setting_hide_followers_count, :setting_aggregate_reblogs, :setting_show_application, + :setting_advanced_layout, :setting_default_content_type, notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account), interactions: %i(must_be_follower must_be_following) diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js index 194800d527..6ef101f115 100644 --- a/app/javascript/flavours/glitch/components/media_gallery.js +++ b/app/javascript/flavours/glitch/components/media_gallery.js @@ -257,7 +257,6 @@ export default class MediaGallery extends React.PureComponent { static propTypes = { sensitive: PropTypes.bool, - revealed: PropTypes.bool, standalone: PropTypes.bool, letterbox: PropTypes.bool, fullwidth: PropTypes.bool, @@ -268,6 +267,8 @@ export default class MediaGallery extends React.PureComponent { intl: PropTypes.object.isRequired, defaultWidth: PropTypes.number, cacheWidth: PropTypes.func, + visible: PropTypes.bool, + onToggleVisibility: PropTypes.func, }; static defaultProps = { @@ -275,13 +276,15 @@ export default class MediaGallery extends React.PureComponent { }; state = { - visible: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed, + visible: this.props.visible !== undefined ? this.props.visible : (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all'), width: this.props.defaultWidth, }; componentWillReceiveProps (nextProps) { - if (!is(nextProps.media, this.props.media) || nextProps.revealed === true) { - this.setState({ visible: nextProps.revealed === undefined ? (displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all') : nextProps.revealed }); + if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) { + this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' }); + } else if (!is(nextProps.visible, this.props.visible) && nextProps.visible !== undefined) { + this.setState({ visible: nextProps.visible }); } } @@ -294,7 +297,11 @@ export default class MediaGallery extends React.PureComponent { } handleOpen = () => { - this.setState({ visible: !this.state.visible }); + if (this.props.onToggleVisibility) { + this.props.onToggleVisibility(); + } else { + this.setState({ visible: !this.state.visible }); + } } handleClick = (index) => { diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js index 5f10e0c52b..7014cab17c 100644 --- a/app/javascript/flavours/glitch/components/status.js +++ b/app/javascript/flavours/glitch/components/status.js @@ -16,6 +16,7 @@ import NotificationOverlayContainer from 'flavours/glitch/features/notifications import classNames from 'classnames'; import { autoUnfoldCW } from 'flavours/glitch/util/content_warning'; import PollContainer from 'flavours/glitch/containers/poll_container'; +import { displayMedia } from 'flavours/glitch/util/initial_state'; // We use the component (and not the container) since we do not want // to use the progress bar to show download progress @@ -38,6 +39,22 @@ export const textForScreenReader = (intl, status, rebloggedByText = false, expan return values.join(', '); }; +export const defaultMediaVisibility = (status, settings) => { + if (!status) { + return undefined; + } + + if (status.get('reblog', null) !== null && typeof status.get('reblog') === 'object') { + status = status.get('reblog'); + } + + if (settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text')) { + return true; + } + + return (displayMedia !== 'hide_all' && !status.get('sensitive') || displayMedia === 'show_all'); +} + @injectIntl export default class Status extends ImmutablePureComponent { @@ -82,6 +99,9 @@ export default class Status extends ImmutablePureComponent { isCollapsed: false, autoCollapsed: false, isExpanded: undefined, + showMedia: undefined, + statusId: undefined, + revealBehindCW: undefined, } // Avoid checking props that are functions (and whose equality will always @@ -103,6 +123,7 @@ export default class Status extends ImmutablePureComponent { updateOnStates = [ 'isExpanded', 'isCollapsed', + 'showMedia', ] // If our settings have changed to disable collapsed statuses, then we @@ -160,6 +181,20 @@ export default class Status extends ImmutablePureComponent { } } + if (nextProps.status && nextProps.status.get('id') !== prevState.statusId) { + update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings); + update.statusId = nextProps.status.get('id'); + updated = true; + } + + if (nextProps.settings.getIn(['media', 'reveal_behind_cw']) !== prevState.revealBehindCW) { + update.revealBehindCW = nextProps.settings.getIn(['media', 'reveal_behind_cw']); + if (update.revealBehindCW) { + update.showMedia = defaultMediaVisibility(nextProps.status, nextProps.settings); + } + updated = true; + } + return updated ? update : null; } @@ -305,6 +340,10 @@ export default class Status extends ImmutablePureComponent { } } + handleToggleMediaVisibility = () => { + this.setState({ showMedia: !this.state.showMedia }); + } + handleAccountClick = (e) => { if (this.context.router && e.button === 0) { const id = e.currentTarget.getAttribute('data-id'); @@ -374,6 +413,9 @@ export default class Status extends ImmutablePureComponent { this.setCollapsed(!this.state.isCollapsed); } + handleHotkeyToggleSensitive = () => { + this.handleToggleMediaVisibility(); + } handleRef = c => { this.node = c; @@ -490,7 +532,8 @@ export default class Status extends ImmutablePureComponent { onOpenVideo={this.handleOpenVideo} width={this.props.cachedMediaWidth} cacheWidth={this.props.cacheMediaWidth} - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.state.showMedia} + onToggleVisibility={this.handleToggleMediaVisibility} />)} ); @@ -508,7 +551,8 @@ export default class Status extends ImmutablePureComponent { onOpenMedia={this.props.onOpenMedia} cacheWidth={this.props.cacheMediaWidth} defaultWidth={this.props.cachedMediaWidth} - revealed={settings.getIn(['media', 'reveal_behind_cw']) && !!status.get('spoiler_text') ? true : undefined} + visible={this.state.showMedia} + onToggleVisibility={this.handleToggleMediaVisibility} /> )} @@ -566,6 +610,7 @@ export default class Status extends ImmutablePureComponent { toggleSpoiler: this.handleExpandedToggle, bookmark: this.handleHotkeyBookmark, toggleCollapse: this.handleHotkeyCollapse, + toggleSensitive: this.handleHotkeyToggleSensitive, }; const computedClass = classNames('status', `status-${status.get('visibility')}`, { diff --git a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js index 2935a60214..f7b475f8d8 100644 --- a/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js +++ b/app/javascript/flavours/glitch/features/keyboard_shortcuts/index.js @@ -71,6 +71,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent {