FEATURE: Allow users to opt out of automatic dark mode (#10377)
This commit is contained in:
parent
8c03868808
commit
6fdc711b4a
|
@ -33,6 +33,7 @@ export default Controller.extend({
|
||||||
let attrs = [
|
let attrs = [
|
||||||
"locale",
|
"locale",
|
||||||
"external_links_in_new_tab",
|
"external_links_in_new_tab",
|
||||||
|
"dark_scheme_id",
|
||||||
"dynamic_favicon",
|
"dynamic_favicon",
|
||||||
"enable_quoting",
|
"enable_quoting",
|
||||||
"enable_defer",
|
"enable_defer",
|
||||||
|
@ -149,6 +150,20 @@ export default Controller.extend({
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
showDarkModeToggle() {
|
||||||
|
return this.siteSettings.default_dark_mode_color_scheme_id > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
enableDarkMode: computed({
|
||||||
|
set(key, value) {
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
get() {
|
||||||
|
return this.get("model.user_option.dark_scheme_id") === -1 ? false : true;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
save() {
|
save() {
|
||||||
this.set("saved", false);
|
this.set("saved", false);
|
||||||
|
@ -162,6 +177,11 @@ export default Controller.extend({
|
||||||
this.set("model.user_option.text_size", this.textSize);
|
this.set("model.user_option.text_size", this.textSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.set(
|
||||||
|
"model.user_option.dark_scheme_id",
|
||||||
|
this.enableDarkMode ? null : -1
|
||||||
|
);
|
||||||
|
|
||||||
return this.model
|
return this.model
|
||||||
.save(this.saveAttrNames)
|
.save(this.saveAttrNames)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
|
|
|
@ -300,6 +300,7 @@ const User = RestModel.extend({
|
||||||
"email_messages_level",
|
"email_messages_level",
|
||||||
"email_level",
|
"email_level",
|
||||||
"email_previous_replies",
|
"email_previous_replies",
|
||||||
|
"dark_scheme_id",
|
||||||
"dynamic_favicon",
|
"dynamic_favicon",
|
||||||
"enable_quoting",
|
"enable_quoting",
|
||||||
"enable_defer",
|
"enable_defer",
|
||||||
|
|
|
@ -20,6 +20,15 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showDarkModeToggle}}
|
||||||
|
<div class="control-group dark-mode">
|
||||||
|
<label class="control-label">{{i18n "user.dark_mode"}}</label>
|
||||||
|
<div class="controls">
|
||||||
|
{{preference-checkbox labelKey="user.dark_mode_enable" checked=enableDarkMode}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class="control-group text-size">
|
<div class="control-group text-size">
|
||||||
<label class="control-label">{{i18n "user.text_size.title"}}</label>
|
<label class="control-label">{{i18n "user.text_size.title"}}</label>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
|
|
|
@ -454,7 +454,9 @@ module ApplicationHelper
|
||||||
result = +""
|
result = +""
|
||||||
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(scheme_id)
|
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(scheme_id)
|
||||||
|
|
||||||
dark_scheme_id = SiteSetting.default_dark_mode_color_scheme_id
|
user_dark_scheme_id = current_user&.user_option&.dark_scheme_id
|
||||||
|
dark_scheme_id = user_dark_scheme_id || SiteSetting.default_dark_mode_color_scheme_id
|
||||||
|
|
||||||
if dark_scheme_id != -1
|
if dark_scheme_id != -1
|
||||||
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(dark_scheme_id, '(prefers-color-scheme: dark)')
|
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(dark_scheme_id, '(prefers-color-scheme: dark)')
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,6 +8,7 @@ class UserOptionSerializer < ApplicationSerializer
|
||||||
:email_level,
|
:email_level,
|
||||||
:email_messages_level,
|
:email_messages_level,
|
||||||
:external_links_in_new_tab,
|
:external_links_in_new_tab,
|
||||||
|
:dark_scheme_id,
|
||||||
:dynamic_favicon,
|
:dynamic_favicon,
|
||||||
:enable_quoting,
|
:enable_quoting,
|
||||||
:enable_defer,
|
:enable_defer,
|
||||||
|
|
|
@ -25,6 +25,7 @@ class UserUpdater
|
||||||
:external_links_in_new_tab,
|
:external_links_in_new_tab,
|
||||||
:enable_quoting,
|
:enable_quoting,
|
||||||
:enable_defer,
|
:enable_defer,
|
||||||
|
:dark_scheme_id,
|
||||||
:dynamic_favicon,
|
:dynamic_favicon,
|
||||||
:automatically_unpin_topics,
|
:automatically_unpin_topics,
|
||||||
:digest_after_minutes,
|
:digest_after_minutes,
|
||||||
|
|
|
@ -909,6 +909,8 @@ en:
|
||||||
first_notification: "Your first notification! Select it to begin."
|
first_notification: "Your first notification! Select it to begin."
|
||||||
dynamic_favicon: "Show counts on browser icon"
|
dynamic_favicon: "Show counts on browser icon"
|
||||||
theme_default_on_all_devices: "Make this the default theme on all my devices"
|
theme_default_on_all_devices: "Make this the default theme on all my devices"
|
||||||
|
dark_mode: "Dark Mode"
|
||||||
|
dark_mode_enable: "Enable automatic dark mode color scheme"
|
||||||
text_size_default_on_all_devices: "Make this the default text size on all my devices"
|
text_size_default_on_all_devices: "Make this the default text size on all my devices"
|
||||||
allow_private_messages: "Allow other users to send me personal messages"
|
allow_private_messages: "Allow other users to send me personal messages"
|
||||||
external_links_in_new_tab: "Open all external links in a new tab"
|
external_links_in_new_tab: "Open all external links in a new tab"
|
||||||
|
|
|
@ -248,6 +248,7 @@ basic:
|
||||||
default_dark_mode_color_scheme_id:
|
default_dark_mode_color_scheme_id:
|
||||||
default: -1
|
default: -1
|
||||||
hidden: true
|
hidden: true
|
||||||
|
client: true
|
||||||
relative_date_duration:
|
relative_date_duration:
|
||||||
client: true
|
client: true
|
||||||
default: 30
|
default: 30
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddDarkSchemeIdToUserOptions < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :user_options, :dark_scheme_id, :integer
|
||||||
|
end
|
||||||
|
end
|
|
@ -93,13 +93,17 @@ class Stylesheet::Manager
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.color_scheme_stylesheet_details(color_scheme_id = nil)
|
def self.color_scheme_stylesheet_details(color_scheme_id = nil, media)
|
||||||
color_scheme = begin
|
color_scheme = begin
|
||||||
ColorScheme.find(color_scheme_id)
|
ColorScheme.find(color_scheme_id)
|
||||||
rescue
|
rescue
|
||||||
|
# don't load fallback when requesting dark color scheme
|
||||||
|
return false if media != "all"
|
||||||
Theme.find_by_id(SiteSetting.default_theme_id)&.color_scheme || ColorScheme.base
|
Theme.find_by_id(SiteSetting.default_theme_id)&.color_scheme || ColorScheme.base
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return false if !color_scheme
|
||||||
|
|
||||||
target = COLOR_SCHEME_STYLESHEET.to_sym
|
target = COLOR_SCHEME_STYLESHEET.to_sym
|
||||||
current_hostname = Discourse.current_hostname
|
current_hostname = Discourse.current_hostname
|
||||||
color_scheme_name = Slug.for(color_scheme.name)
|
color_scheme_name = Slug.for(color_scheme.name)
|
||||||
|
@ -119,7 +123,9 @@ class Stylesheet::Manager
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.color_scheme_stylesheet_link_tag(color_scheme_id = nil, media = 'all')
|
def self.color_scheme_stylesheet_link_tag(color_scheme_id = nil, media = 'all')
|
||||||
stylesheet = color_scheme_stylesheet_details(color_scheme_id)
|
stylesheet = color_scheme_stylesheet_details(color_scheme_id, media)
|
||||||
|
return '' if !stylesheet
|
||||||
|
|
||||||
href = stylesheet[:new_href]
|
href = stylesheet[:new_href]
|
||||||
%[<link href="#{href}" media="#{media}" rel="stylesheet"/>].html_safe
|
%[<link href="#{href}" media="#{media}" rel="stylesheet"/>].html_safe
|
||||||
end
|
end
|
||||||
|
|
|
@ -177,9 +177,14 @@ describe Stylesheet::Manager do
|
||||||
expect(link).not_to eq("")
|
expect(link).not_to eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "does not crash on missing color scheme" do
|
it "loads base scheme when defined scheme id is missing" do
|
||||||
link = Stylesheet::Manager.color_scheme_stylesheet_link_tag(125)
|
link = Stylesheet::Manager.color_scheme_stylesheet_link_tag(125)
|
||||||
expect(link).not_to eq("")
|
expect(link).to include("color_definitions_base")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "loads nothing when defined dark scheme id is missing" do
|
||||||
|
link = Stylesheet::Manager.color_scheme_stylesheet_link_tag(125, "(prefers-color-scheme: dark)")
|
||||||
|
expect(link).to eq("")
|
||||||
end
|
end
|
||||||
|
|
||||||
it "uses the correct color scheme from the default site theme" do
|
it "uses the correct color scheme from the default site theme" do
|
||||||
|
|
|
@ -349,6 +349,8 @@ describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'discourse_color_scheme_stylesheets' do
|
describe 'discourse_color_scheme_stylesheets' do
|
||||||
|
fab!(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
it 'returns a stylesheet link tag by default' do
|
it 'returns a stylesheet link tag by default' do
|
||||||
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
expect(cs_stylesheets).to include("stylesheets/color_definitions")
|
expect(cs_stylesheets).to include("stylesheets/color_definitions")
|
||||||
|
@ -361,5 +363,40 @@ describe ApplicationHelper do
|
||||||
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
|
expect(cs_stylesheets).to include("(prefers-color-scheme: dark)")
|
||||||
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
|
expect(cs_stylesheets.scan("stylesheets/color_definitions").size).to eq(2)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'fails gracefully when the dark color scheme ID is set but missing' do
|
||||||
|
SiteSetting.default_dark_mode_color_scheme_id = -5
|
||||||
|
cs_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
|
||||||
|
expect(cs_stylesheets).to include("stylesheets/color_definitions")
|
||||||
|
expect(cs_stylesheets).not_to include("(prefers-color-scheme: dark)")
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a user option" do
|
||||||
|
before do
|
||||||
|
user.user_option.dark_scheme_id = -1
|
||||||
|
user.user_option.save!
|
||||||
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
|
|
||||||
|
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck(:id).first
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns no dark scheme stylesheet when user has disabled that option" do
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
|
||||||
|
expect(color_stylesheets).to include("stylesheets/color_definitions")
|
||||||
|
expect(color_stylesheets).not_to include("(prefers-color-scheme: dark)")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns user-selected dark color scheme stylesheet" do
|
||||||
|
new_cs = Fabricate(:color_scheme, name: 'Custom Color Scheme')
|
||||||
|
user.user_option.update!(dark_scheme_id: new_cs.id)
|
||||||
|
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
expect(color_stylesheets).to include("(prefers-color-scheme: dark)")
|
||||||
|
expect(color_stylesheets).to include("custom-color-scheme")
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -482,3 +482,16 @@ QUnit.test("can select an option from a dropdown", async assert => {
|
||||||
await field.selectRowByValue("Cat");
|
await field.selectRowByValue("Cat");
|
||||||
assert.equal(field.header().value(), "Cat", "it sets the value of the field");
|
assert.equal(field.header().value(), "Cat", "it sets the value of the field");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
acceptance("User Preferences disabling dark mode", {
|
||||||
|
loggedIn: true,
|
||||||
|
settings: { default_dark_mode_color_scheme_id: 1 }
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("shows option to disable dark mode", async assert => {
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.ok(
|
||||||
|
$(".control-group.dark-mode").length,
|
||||||
|
"it has the option to disable dark mode"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue