diff --git a/app/components/post_preview_component.rb b/app/components/post_preview_component.rb index ee2294406..bbd1df72c 100644 --- a/app/components/post_preview_component.rb +++ b/app/components/post_preview_component.rb @@ -7,7 +7,7 @@ class PostPreviewComponent < ApplicationComponent with_collection_parameter :post - attr_reader :post, :tags, :size, :show_deleted, :link_target, :pool, :similarity, :recommended, :show_votes, :fit, :show_size, :current_user, :options + attr_reader :post, :tags, :size, :show_deleted, :link_target, :pool, :similarity, :recommended, :show_votes, :fit, :show_size, :save_data, :current_user, :options delegate :external_link_to, :time_ago_in_words_tagged, :duration_to_hhmmss, :render_post_votes, :empty_heart_icon, :sound_icon, to: :helpers delegate :image_width, :image_height, :file_ext, :file_size, :duration, :is_animated?, to: :media_asset @@ -21,12 +21,14 @@ class PostPreviewComponent < ApplicationComponent # If false, hide thumbnails of deleted posts. # @param show_votes [Boolean] If true, show scores and vote buttons beneath the thumbnail. # @param show_size [Boolean] If true, show filesize and resolution beneath the thumbnail. + # @param save_data [Boolean] If true, save data by not serving higher quality thumbnails + # on 2x pixel density displays. Default: false. # @param link_target [ApplicationRecord] What the thumbnail links to (default: the post). # @param current_user [User] The current user. # @param fit [Symbol] If `:fixed`, make the thumbnail container a fixed size # (e.g. 180x180), even if the thumbnail image is smaller than that. If `:compact`, # make the thumbnail container shrink to the same size as the thumbnail image. - def initialize(post:, tags: "", size: DEFAULT_SIZE, show_deleted: false, show_votes: false, link_target: post, pool: nil, similarity: nil, recommended: nil, show_size: nil, fit: :compact, current_user: CurrentUser.user, **options) + def initialize(post:, tags: "", size: DEFAULT_SIZE, show_deleted: false, show_votes: false, link_target: post, pool: nil, similarity: nil, recommended: nil, show_size: nil, save_data: CurrentUser.save_data, fit: :compact, current_user: CurrentUser.user, **options) super @post = post @tags = tags.presence @@ -39,6 +41,7 @@ class PostPreviewComponent < ApplicationComponent @recommended = recommended @fit = fit @show_size = show_size + @save_data = save_data @current_user = current_user @options = options end diff --git a/app/components/post_preview_component/post_preview_component.html.erb b/app/components/post_preview_component/post_preview_component.html.erb index 558526a73..69c7a3a04 100644 --- a/app/components/post_preview_component/post_preview_component.html.erb +++ b/app/components/post_preview_component/post_preview_component.html.erb @@ -14,13 +14,15 @@ <% end %> - <% case size %> - <% when "150" %> - <%# no-op %> - <% when "180" %> - <%= tag.source type: "image/jpeg", srcset: "#{media_asset.variant("180x180").file_url} 1x, #{media_asset.variant("360x360").file_url} 2x" %> - <% else # 225 to 360 %> - <%= tag.source type: "image/webp", srcset: "#{media_asset.variant("360x360").file_url} 1x, #{media_asset.variant("720x720").file_url} 2x" %> + <% unless save_data %> + <% case size %> + <% when "150" %> + <%# no-op %> + <% when "180" %> + <%= tag.source type: "image/jpeg", srcset: "#{media_asset.variant("180x180").file_url} 1x, #{media_asset.variant("360x360").file_url} 2x" %> + <% else # 225 to 360 %> + <%= tag.source type: "image/webp", srcset: "#{media_asset.variant("360x360").file_url} 1x, #{media_asset.variant("720x720").file_url} 2x" %> + <% end %> <% end %> <%= tag.img src: variant.file_url, width: variant.width, height: variant.height, class: "post-preview-image", title: tooltip, alt: "post ##{post.id}", crossorigin: "anonymous" -%> diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 74d5da472..485f66863 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -252,7 +252,7 @@ module ApplicationHelper render "table_builder/table", table: table end - def body_attributes(user, params, current_item = nil) + def body_attributes(current_user, params, current_item = nil) controller_param = params[:controller].parameterize.dasherize action_param = params[:action].parameterize.dasherize @@ -265,7 +265,8 @@ module ApplicationHelper action: action_param, layout: controller.class.send(:_layout), "current-user-ip-addr": request.remote_ip, - **current_user_data_attributes(user), + "current-user-save-data": CurrentUser.save_data, + **current_user_data_attributes(current_user), **cookie_data_attributes, **current_item_data_attributes(current_item), } diff --git a/app/logical/current_user.rb b/app/logical/current_user.rb index 6846b3f52..0ca78ce4f 100644 --- a/app/logical/current_user.rb +++ b/app/logical/current_user.rb @@ -1,5 +1,6 @@ # A global variable containing the current user, the current IP address, the -# current user's country code, and whether safe mode is enabled. +# current user's country code, whether safe mode is enabled, and whether +# save-data mode is enabled. # # The current user is set during a request by {ApplicationController#set_current_user}, # which calls {SessionLoader#load}. The current user will not be set outside of @@ -11,7 +12,7 @@ # @see ApplicationController#set_current_user # @see SessionLoader#load class CurrentUser < ActiveSupport::CurrentAttributes - attribute :user, :ip_addr, :country, :safe_mode + attribute :user, :ip_addr, :country, :safe_mode, :save_data alias_method :safe_mode?, :safe_mode delegate :id, to: :user, allow_nil: true diff --git a/app/logical/session_loader.rb b/app/logical/session_loader.rb index 2fe8c62fa..636d6b550 100644 --- a/app/logical/session_loader.rb +++ b/app/logical/session_loader.rb @@ -80,6 +80,7 @@ class SessionLoader set_time_zone set_country set_safe_mode + set_save_data_mode initialize_session_cookies CurrentUser.user.unban! if CurrentUser.user.ban_expired? ensure @@ -177,6 +178,13 @@ class SessionLoader CurrentUser.safe_mode = safe_mode end + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Save-Data + # https://www.keycdn.com/blog/save-data + def set_save_data_mode + save_data = params[:save_data].presence || request.cookies[:save_data].presence || request.headers["Save-Data"].presence || "false" + CurrentUser.save_data = save_data.truthy? + end + def initialize_session_cookies session.options[:expire_after] = 20.years session[:started_at] ||= Time.now.utc.to_s