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:
parent
e391f71c04
commit
08476f17ff
|
@ -0,0 +1,3 @@
|
|||
import templateOnly from "@ember/component/template-only";
|
||||
|
||||
export default templateOnly();
|
|
@ -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);
|
||||
},
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}}
|
||||
|
||||
|
|
|
@ -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"}}
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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}}
|
||||
|
|
|
@ -116,7 +116,7 @@
|
|||
width: 100%;
|
||||
height: inherit;
|
||||
max-width: initial;
|
||||
max-height: initial;
|
||||
max-height: var(--max-height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
#
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -29,6 +29,7 @@ class UploadSecurity
|
|||
profile_background
|
||||
card_background
|
||||
category_logo
|
||||
category_logo_dark
|
||||
category_background
|
||||
group_flair
|
||||
badge_image
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue