diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index edf29947bb0..bac9b329d46 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -57,6 +57,7 @@ class Settings::PreferencesController < Settings::BaseController
:setting_use_blurhash,
:setting_use_pending_items,
:setting_trends,
+ :setting_crop_images,
notification_emails: %i(follow follow_request reblog favourite mention digest report pending_account trending_tag),
interactions: %i(must_be_follower must_be_following must_be_following_dm)
)
diff --git a/app/javascript/mastodon/components/media_gallery.js b/app/javascript/mastodon/components/media_gallery.js
index b8fca8bcb5e..12b7e5b6679 100644
--- a/app/javascript/mastodon/components/media_gallery.js
+++ b/app/javascript/mastodon/components/media_gallery.js
@@ -6,7 +6,7 @@ import IconButton from './icon_button';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { isIOS } from '../is_mobile';
import classNames from 'classnames';
-import { autoPlayGif, displayMedia, useBlurhash } from '../initial_state';
+import { autoPlayGif, cropImages, displayMedia, useBlurhash } from '../initial_state';
import { decode } from 'blurhash';
const messages = defineMessages({
@@ -281,7 +281,7 @@ class MediaGallery extends React.PureComponent {
}
handleRef = (node) => {
- if (node /*&& this.isStandaloneEligible()*/) {
+ if (node) {
// offsetWidth triggers a layout, so only calculate when we need to
if (this.props.cacheWidth) this.props.cacheWidth(node.offsetWidth);
@@ -291,13 +291,13 @@ class MediaGallery extends React.PureComponent {
}
}
- isStandaloneEligible() {
- const { media, standalone } = this.props;
- return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
+ isFullSizeEligible() {
+ const { media } = this.props;
+ return media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
}
render () {
- const { media, intl, sensitive, height, defaultWidth } = this.props;
+ const { media, intl, sensitive, height, defaultWidth, standalone } = this.props;
const { visible } = this.state;
const width = this.state.width || defaultWidth;
@@ -306,7 +306,7 @@ class MediaGallery extends React.PureComponent {
const style = {};
- if (this.isStandaloneEligible()) {
+ if (this.isFullSizeEligible() && (standalone || !cropImages)) {
if (width) {
style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']);
}
@@ -319,7 +319,7 @@ class MediaGallery extends React.PureComponent {
const size = media.take(4).size;
const uncached = media.every(attachment => attachment.get('type') === 'unknown');
- if (this.isStandaloneEligible()) {
+ if (standalone && this.isFullSizeEligible()) {
children = ;
} else {
children = media.take(4).map((attachment, i) => );
diff --git a/app/javascript/mastodon/initial_state.js b/app/javascript/mastodon/initial_state.js
index 56fb5854669..1134c55db42 100644
--- a/app/javascript/mastodon/initial_state.js
+++ b/app/javascript/mastodon/initial_state.js
@@ -24,5 +24,6 @@ export const useBlurhash = getMeta('use_blurhash');
export const usePendingItems = getMeta('use_pending_items');
export const showTrends = getMeta('trends');
export const title = getMeta('title');
+export const cropImages = getMeta('crop_images');
export default initialState;
diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb
index 3568a3e1167..fa8255faab1 100644
--- a/app/lib/user_settings_decorator.rb
+++ b/app/lib/user_settings_decorator.rb
@@ -37,6 +37,7 @@ class UserSettingsDecorator
user.settings['use_blurhash'] = use_blurhash_preference if change?('setting_use_blurhash')
user.settings['use_pending_items'] = use_pending_items_preference if change?('setting_use_pending_items')
user.settings['trends'] = trends_preference if change?('setting_trends')
+ user.settings['crop_images'] = crop_images_preference if change?('setting_crop_images')
end
def merged_notification_emails
@@ -127,6 +128,10 @@ class UserSettingsDecorator
boolean_cast_setting 'setting_trends'
end
+ def crop_images_preference
+ boolean_cast_setting 'setting_crop_images'
+ end
+
def boolean_cast_setting(key)
ActiveModel::Type::Boolean.new.cast(settings[key])
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 9a19a53b32d..7147a9a319f 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -108,7 +108,7 @@ class User < ApplicationRecord
delegate :auto_play_gif, :default_sensitive, :unfollow_modal, :boost_modal, :delete_modal,
:reduce_motion, :system_font_ui, :noindex, :theme, :display_media, :hide_network,
:expand_spoilers, :default_language, :aggregate_reblogs, :show_application,
- :advanced_layout, :use_blurhash, :use_pending_items, :trends,
+ :advanced_layout, :use_blurhash, :use_pending_items, :trends, :crop_images,
to: :settings, prefix: :setting, allow_nil: false
attr_reader :invite_code
diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb
index fb53ea3145e..392fc891af4 100644
--- a/app/serializers/initial_state_serializer.rb
+++ b/app/serializers/initial_state_serializer.rb
@@ -38,11 +38,13 @@ class InitialStateSerializer < ActiveModel::Serializer
store[:use_pending_items] = object.current_account.user.setting_use_pending_items
store[:is_staff] = object.current_account.user.staff?
store[:trends] = Setting.trends && object.current_account.user.setting_trends
+ store[:crop_images] = object.current_account.user.setting_crop_images
else
store[:auto_play_gif] = Setting.auto_play_gif
store[:display_media] = Setting.display_media
store[:reduce_motion] = Setting.reduce_motion
store[:use_blurhash] = Setting.use_blurhash
+ store[:crop_images] = Setting.crop_images
end
store
diff --git a/app/views/settings/preferences/appearance/show.html.haml b/app/views/settings/preferences/appearance/show.html.haml
index d6ee1933f86..9ed83fb930b 100644
--- a/app/views/settings/preferences/appearance/show.html.haml
+++ b/app/views/settings/preferences/appearance/show.html.haml
@@ -25,6 +25,11 @@
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
+ %h4= t 'appearance.toot_layout'
+
+ .fields-group
+ = f.input :setting_crop_images, as: :boolean, wrapper: :with_label
+
%h4= t 'appearance.discovery'
.fields-group
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 458524c3d3a..6fc191e1a5e 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -576,6 +576,7 @@ en:
confirmation_dialogs: Confirmation dialogs
discovery: Discovery
sensitive_content: Sensitive content
+ toot_layout: Toot layout
application_mailer:
notification_preferences: Change e-mail preferences
salutation: "%{name},"
diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml
index dc39ec92699..65951b73baf 100644
--- a/config/locales/simple_form.en.yml
+++ b/config/locales/simple_form.en.yml
@@ -113,6 +113,7 @@ en:
setting_aggregate_reblogs: Group boosts in timelines
setting_auto_play_gif: Auto-play animated GIFs
setting_boost_modal: Show confirmation dialog before boosting
+ setting_crop_images: Crop images in non-expanded toots to 16x9
setting_default_language: Posting language
setting_default_privacy: Posting privacy
setting_default_sensitive: Always mark media as sensitive
diff --git a/config/settings.yml b/config/settings.yml
index bd2f65b5e4d..f66e3922e2b 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -36,6 +36,7 @@ defaults: &defaults
use_pending_items: false
trends: true
trendable_by_default: false
+ crop_images: true
notification_emails:
follow: false
reblog: false