FEATURE: Add dark mode option for category logos (#18460)

Adds a new upload field for a second dark mode category logo. 
This alternative will be used when the browser is in dark mode (similar to the global site setting for a dark logo).
This commit is contained in:
Jan Cernik 2022-10-07 12:00:44 -03:00 committed by GitHub
parent e391f71c04
commit 08476f17ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 110 additions and 24 deletions

View File

@ -0,0 +1,3 @@
import templateOnly from "@ember/component/template-only";
export default templateOnly();

View File

@ -13,6 +13,11 @@ export default buildCategoryPanel("images").extend({
return uploadedLogoUrl || "";
},
@discourseComputed("category.uploaded_logo_dark.url")
logoImageDarkUrl(uploadedLogoDarkUrl) {
return uploadedLogoDarkUrl || "";
},
actions: {
logoUploadDone(upload) {
this._setFromUpload("category.uploaded_logo", upload);
@ -22,6 +27,14 @@ export default buildCategoryPanel("images").extend({
this._deleteUpload("category.uploaded_logo");
},
logoDarkUploadDone(upload) {
this._setFromUpload("category.uploaded_logo_dark", upload);
},
logoDarkUploadDeleted() {
this._deleteUpload("category.uploaded_logo_dark");
},
backgroundUploadDone(upload) {
this._setFromUpload("category.uploaded_background", upload);
},

View File

@ -216,6 +216,7 @@ const Category = RestModel.extend({
mailinglist_mirror: this.mailinglist_mirror,
parent_category_id: this.parent_category_id,
uploaded_logo_id: this.get("uploaded_logo.id"),
uploaded_logo_dark_id: this.get("uploaded_logo_dark.id"),
uploaded_background_id: this.get("uploaded_background.id"),
allow_badges: this.allow_badges,
custom_fields: this.custom_fields,

View File

@ -5,7 +5,7 @@
<a href={{c.url}}>
{{#unless c.isMuted}}
{{#if c.uploaded_logo.url}}
<CdnImg @src={{c.uploaded_logo.url}} @class="logo" @width={{c.uploaded_logo.width}} @height={{c.uploaded_logo.height}} @alt="" />
<CategoryLogo @category={{c}} />
{{/if}}
{{/unless}}

View File

@ -6,7 +6,7 @@
{{#unless c.isMuted}}
<div class="category-logo">
{{#if c.uploaded_logo.url}}
<CdnImg @src={{c.uploaded_logo.url}} @class="logo" @width={{c.uploaded_logo.width}} @height={{c.uploaded_logo.height}} @alt="" />
<CategoryLogo @category={{c}} />
{{/if}}
</div>
{{/unless}}
@ -54,7 +54,9 @@
{{#each c.subcategories as |sc|}}
<a class="subcategory" href={{sc.url}}>
<span class="subcategory-image-placeholder">
<CdnImg @src={{sc.uploaded_logo.url}} @class="logo" @width={{sc.uploaded_logo.width}} @height={{sc.uploaded_logo.height}} @alt="" />
{{#if sc.uploaded_logo.url}}
<CategoryLogo @category={{sc}} />
{{/if}}
</span>
{{category-link sc hideParent="true"}}

View File

@ -0,0 +1,10 @@
<div class="category-logo aspect-image">
{{#if (and @category.uploaded_logo.url @category.uploaded_logo_dark.url)}}
<picture>
<source srcset={{@category.uploaded_logo_dark.url}} width={{@category.uploaded_logo_dark.width}} height={{@category.uploaded_logo_dark.height}} media="(prefers-color-scheme: dark)">
<CdnImg @src={{@category.uploaded_logo.url}} @width={{@category.uploaded_logo.width}} @height={{@category.uploaded_logo.height}}/>
</picture>
{{else if @category.uploaded_logo.url}}
<CdnImg @src={{@category.uploaded_logo.url}} @width={{@category.uploaded_logo.width}} @height={{@category.uploaded_logo.height}}/>
{{/if}}
</div>

View File

@ -7,6 +7,6 @@
<span class="category-name">{{dir-span this.category.name}}</span>
</div>
{{#if this.category.uploaded_logo.url}}
<CdnImg @src={{this.category.uploaded_logo.url}} @class="category-logo" @width={{this.category.uploaded_logo.width}} @height={{this.category.uploaded_logo.height}} @alt="" />
<CategoryLogo @category={{this.category}} />
{{/if}}
</a>

View File

@ -1,5 +1,3 @@
{{#if this.src}}
<div class="{{this.class}} aspect-image" style={{this.style}}>
<img src={{this.cdnSrc}} width={{this.width}} height={{this.height}} alt={{this.alt}}>
</div>
<img src={{this.cdnSrc}} width={{this.width}} height={{this.height}} style={{this.style}} alt="">
{{/if}}

View File

@ -3,6 +3,11 @@
<UppyImageUploader @imageUrl={{this.logoImageUrl}} @onUploadDone={{action "logoUploadDone"}} @onUploadDeleted={{action "logoUploadDeleted"}} @type="category_logo" @class="no-repeat contain-image" @id="category-logo-uploader" />
</section>
<section class="field category-logo">
<label>{{i18n "category.logo_dark"}}</label>
<UppyImageUploader @imageUrl={{this.logoImageDarkUrl}} @onUploadDone={{action "logoDarkUploadDone"}} @onUploadDeleted={{action "logoDarkUploadDeleted"}} @type="category_logo_dark" @class="no-repeat contain-image" @id="category-dark-logo-uploader" />
</section>
<section class="field category-background-image">
<label>{{i18n "category.background_image"}}</label>
<UppyImageUploader @imageUrl={{this.backgroundImageUrl}} @onUploadDone={{action "backgroundUploadDone"}} @onUploadDeleted={{action "backgroundUploadDeleted"}} @type="category_background" @id="category-background-uploader" />

View File

@ -2,8 +2,7 @@
<section class="category-heading">
{{#if this.category.uploaded_logo.url}}
<CdnImg @src={{this.category.uploaded_logo.url}} @class="category-logo" @width={{this.category.uploaded_logo.width}} @height={{this.category.uploaded_logo.height}} @alt="" />
<CategoryLogo @category={{this.category}} />
{{#if this.category.description}}
<p>{{dir-span this.category.description htmlSafe="true"}}</p>
{{/if}}

View File

@ -116,7 +116,7 @@
width: 100%;
height: inherit;
max-width: initial;
max-height: initial;
max-height: var(--max-height);
}
}
}

View File

@ -78,7 +78,7 @@
}
}
.logo.aspect-image img {
.category-logo.aspect-image img {
display: block;
width: auto;
height: 40px;
@ -86,7 +86,7 @@
}
@supports (--custom: property) {
.logo.aspect-image img {
.category-logo.aspect-image img {
--height: 40px;
height: var(--height);
width: calc(var(--height) * var(--aspect-ratio));
@ -110,7 +110,7 @@
}
&.no-logos {
.logo {
.category-logo {
display: none;
}
}
@ -241,7 +241,7 @@
min-width: 0;
@include ellipsis;
}
.logo img {
.category-logo img {
display: inline-block;
--height: 20px;
height: var(--height);

View File

@ -353,6 +353,7 @@ class CategoriesController < ApplicationController
:auto_close_hours,
:auto_close_based_on_last_post,
:uploaded_logo_id,
:uploaded_logo_dark_id,
:uploaded_background_id,
:slug,
:allow_badges,

View File

@ -35,6 +35,7 @@ class Category < ActiveRecord::Base
belongs_to :user
belongs_to :latest_post, class_name: "Post"
belongs_to :uploaded_logo, class_name: "Upload"
belongs_to :uploaded_logo_dark, class_name: "Upload"
belongs_to :uploaded_background, class_name: "Upload"
has_many :topics
@ -82,8 +83,8 @@ class Category < ActiveRecord::Base
after_save :update_reviewables
after_save do
if saved_change_to_uploaded_logo_id? || saved_change_to_uploaded_background_id?
upload_ids = [self.uploaded_logo_id, self.uploaded_background_id]
if saved_change_to_uploaded_logo_id? || saved_change_to_uploaded_logo_dark_id? || saved_change_to_uploaded_background_id?
upload_ids = [self.uploaded_logo_id, self.uploaded_logo_dark_id, self.uploaded_background_id]
UploadReference.ensure_exist!(upload_ids: upload_ids, target: self)
end
end
@ -1064,6 +1065,7 @@ end
# default_list_filter :string(20) default("all")
# allow_unlimited_owner_edits_on_first_post :boolean default(FALSE), not null
# default_slow_mode_seconds :integer
# uploaded_logo_dark_id :integer
#
# Indexes
#

View File

@ -99,6 +99,7 @@ class CategoryList
@categories = Category.includes(
:uploaded_background,
:uploaded_logo,
:uploaded_logo_dark,
:topic_only_relative_url,
subcategories: [:topic_only_relative_url]
).secured(@guardian)

View File

@ -69,7 +69,7 @@ class Site
# corresponding ActiveRecord callback to clear the categories cache.
Discourse.cache.fetch(categories_cache_key, expires_in: 30.minutes) do
categories = Category
.includes(:uploaded_logo, :uploaded_background, :tags, :tag_groups, category_required_tag_groups: :tag_group)
.includes(:uploaded_logo, :uploaded_logo_dark, :uploaded_background, :tags, :tag_groups, category_required_tag_groups: :tag_group)
.joins('LEFT JOIN topics t on t.id = categories.topic_id')
.select('categories.*, t.slug topic_slug')
.order(:position)

View File

@ -34,6 +34,7 @@ class BasicCategorySerializer < ApplicationSerializer
:custom_fields
has_one :uploaded_logo, embed: :object, serializer: CategoryUploadSerializer
has_one :uploaded_logo_dark, embed: :object, serializer: CategoryUploadSerializer
has_one :uploaded_background, embed: :object, serializer: CategoryUploadSerializer
def include_parent_category_id?

View File

@ -3450,6 +3450,7 @@ en:
name: "Category Name"
description: "Description"
logo: "Category Logo Image"
logo_dark: "Dark Mode Category Logo Image"
background_image: "Category Background Image"
badge_colors: "Badge colors"
background_color: "Background color"

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class AddDarkModeLogoToCategories < ActiveRecord::Migration[7.0]
def change
add_column :categories, :uploaded_logo_dark_id, :integer, index: true
end
end

View File

@ -210,6 +210,7 @@ task 'site:export_structure', [:zip_path] => :environment do |task, args|
sort_order: c.sort_order,
sort_ascending: c.sort_ascending,
uploaded_logo_id: data.set_upload(c.uploaded_logo_id),
uploaded_logo_dark_id: data.set_upload(c.uploaded_logo_dark_id),
uploaded_background_id: data.set_upload(c.uploaded_background_id),
topic_featured_link_allowed: c.topic_featured_link_allowed,
all_topics_wiki: c.all_topics_wiki,

View File

@ -905,6 +905,7 @@ def analyze_missing_s3
[:user_profiles, :profile_background_upload_id],
[:user_profiles, :card_background_upload_id],
[:categories, :uploaded_logo_id],
[:categories, :uploaded_logo_dark_id],
[:categories, :uploaded_background_id],
[:custom_emojis, :upload_id],
[:theme_fields, :upload_id],

View File

@ -29,6 +29,7 @@ class UploadSecurity
profile_background
card_background
category_logo
category_logo_dark
category_background
group_flair
badge_image

View File

@ -242,14 +242,24 @@ RSpec.describe Jobs::CleanUpUploads do
expect(Upload.exists?(id: category_logo_upload.id)).to eq(true)
end
it "does not delete category background url uploads" do
category_logo_upload = fabricate_upload
Fabricate(:category, uploaded_background: category_logo_upload)
it "does not delete category dark logo uploads" do
category_logo_dark_upload = fabricate_upload
Fabricate(:category, uploaded_logo_dark: category_logo_dark_upload)
Jobs::CleanUpUploads.new.execute(nil)
expect(Upload.exists?(id: expired_upload.id)).to eq(false)
expect(Upload.exists?(id: category_logo_upload.id)).to eq(true)
expect(Upload.exists?(id: category_logo_dark_upload.id)).to eq(true)
end
it "does not delete category background uploads" do
category_background_upload = fabricate_upload
Fabricate(:category, uploaded_background: category_background_upload)
Jobs::CleanUpUploads.new.execute(nil)
expect(Upload.exists?(id: expired_upload.id)).to eq(false)
expect(Upload.exists?(id: category_background_upload.id)).to eq(true)
end
it "does not delete post uploads" do

View File

@ -21,17 +21,18 @@ RSpec.describe UploadReference do
describe 'category uploads' do
fab!(:upload1) { Fabricate(:upload) }
fab!(:upload2) { Fabricate(:upload) }
fab!(:upload3) { Fabricate(:upload) }
it 'creates upload references' do
category = nil
expect { category = Fabricate(:category, uploaded_logo_id: upload1.id, uploaded_background_id: upload2.id) }
.to change { UploadReference.count }.by(2)
expect { category = Fabricate(:category, uploaded_logo_id: upload1.id, uploaded_logo_dark_id: upload2.id, uploaded_background_id: upload3.id) }
.to change { UploadReference.count }.by(3)
upload_reference = UploadReference.last
expect(upload_reference.target).to eq(category)
expect { category.destroy! }
.to change { UploadReference.count }.by(-2)
.to change { UploadReference.count }.by(-3)
end
end

View File

@ -239,6 +239,12 @@
"null"
]
},
"uploaded_logo_dark": {
"type": [
"string",
"null"
]
},
"uploaded_background": {
"type": [
"string",
@ -293,6 +299,7 @@
"topic_featured_link_allowed",
"search_priority",
"uploaded_logo",
"uploaded_logo_dark",
"uploaded_background"
]
}

View File

@ -156,6 +156,12 @@
"null"
]
},
"uploaded_logo_dark": {
"type": [
"string",
"null"
]
},
"uploaded_background": {
"type": [
"string",
@ -199,6 +205,7 @@
"topics_all_time",
"subcategory_ids",
"uploaded_logo",
"uploaded_logo_dark",
"uploaded_background"
]
}

View File

@ -242,6 +242,12 @@
"null"
]
},
"uploaded_logo_dark": {
"type": [
"string",
"null"
]
},
"uploaded_background": {
"type": [
"string",
@ -296,6 +302,7 @@
"topic_featured_link_allowed",
"search_priority",
"uploaded_logo",
"uploaded_logo_dark",
"uploaded_background"
]
}

View File

@ -629,6 +629,12 @@
"null"
]
},
"uploaded_logo_dark": {
"type": [
"string",
"null"
]
},
"uploaded_background": {
"type": [
"string",
@ -680,6 +686,7 @@
"required_tag_groups",
"read_only_banner",
"uploaded_logo",
"uploaded_logo_dark",
"uploaded_background",
"can_edit"
]