FEATURE: User selectable color schemes (#10544)
This commit is contained in:
parent
cff57c1883
commit
b7cfc9e861
|
@ -72,6 +72,10 @@ export default Controller.extend({
|
||||||
this.model.save();
|
this.model.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
applyUserSelectable() {
|
||||||
|
this.model.updateUserSelectable(this.get("model.user_selectable"));
|
||||||
|
},
|
||||||
|
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
const model = this.model;
|
const model = this.model;
|
||||||
return bootbox.confirm(
|
return bootbox.confirm(
|
||||||
|
|
|
@ -107,6 +107,17 @@ const ColorScheme = EmberObject.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateUserSelectable(value) {
|
||||||
|
if (!this.id) return;
|
||||||
|
|
||||||
|
return ajax(`/admin/color_schemes/${this.id}.json`, {
|
||||||
|
data: JSON.stringify({ color_scheme: { user_selectable: value } }),
|
||||||
|
type: "PUT",
|
||||||
|
dataType: "json",
|
||||||
|
contentType: "application/json"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
if (this.id) {
|
if (this.id) {
|
||||||
return ajax(`/admin/color_schemes/${this.id}`, { type: "DELETE" });
|
return ajax(`/admin/color_schemes/${this.id}`, { type: "DELETE" });
|
||||||
|
@ -129,6 +140,7 @@ ColorScheme.reopenClass({
|
||||||
theme_id: colorScheme.theme_id,
|
theme_id: colorScheme.theme_id,
|
||||||
theme_name: colorScheme.theme_name,
|
theme_name: colorScheme.theme_name,
|
||||||
base_scheme_id: colorScheme.base_scheme_id,
|
base_scheme_id: colorScheme.base_scheme_id,
|
||||||
|
user_selectable: colorScheme.user_selectable,
|
||||||
colors: colorScheme.colors.map(c => {
|
colors: colorScheme.colors.map(c => {
|
||||||
return ColorSchemeColor.create({
|
return ColorSchemeColor.create({
|
||||||
name: c.name,
|
name: c.name,
|
||||||
|
|
|
@ -22,9 +22,12 @@
|
||||||
icon="far-clipboard"
|
icon="far-clipboard"
|
||||||
label="admin.customize.copy_to_clipboard"
|
label="admin.customize.copy_to_clipboard"
|
||||||
}}
|
}}
|
||||||
|
<span class="saving {{unless model.savingStatus "hidden"}}">{{model.savingStatus}}</span>
|
||||||
{{#if model.theme_id}}
|
{{#if model.theme_id}}
|
||||||
{{i18n "admin.customize.theme_owner"}}
|
<span class="not-editable">
|
||||||
{{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}}
|
{{i18n "admin.customize.theme_owner"}}
|
||||||
|
{{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}}
|
||||||
|
</span>
|
||||||
{{else}}
|
{{else}}
|
||||||
{{d-button
|
{{d-button
|
||||||
action=(action "destroy")
|
action=(action "destroy")
|
||||||
|
@ -33,27 +36,29 @@
|
||||||
label="admin.customize.delete"
|
label="admin.customize.delete"
|
||||||
}}
|
}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<span class="saving {{unless model.savingStatus "hidden"}}">{{model.savingStatus}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="admin-controls">
|
<div class="admin-controls">
|
||||||
<div class="search controls">
|
{{inline-edit-checkbox action=(action "applyUserSelectable") labelKey="admin.customize.theme.color_scheme_user_selectable" checked=model.user_selectable}}
|
||||||
<label>
|
|
||||||
{{input type="checkbox" checked=onlyOverridden}}
|
|
||||||
{{i18n "admin.settings.show_overriden"}}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if colors.length}}
|
{{#if colors.length}}
|
||||||
<table class="table colors">
|
<table class="table colors">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th>
|
||||||
|
</th>
|
||||||
<th class="hex">{{i18n "admin.customize.color"}}</th>
|
<th class="hex">{{i18n "admin.customize.color"}}</th>
|
||||||
<th></th>
|
<th class="overriden">
|
||||||
|
{{#unless model.theme_id}}
|
||||||
|
<label>
|
||||||
|
{{input type="checkbox" checked=onlyOverridden}}
|
||||||
|
{{i18n "admin.settings.show_overriden"}}
|
||||||
|
</label>
|
||||||
|
{{/unless}}
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
|
@ -4,6 +4,11 @@ import Controller from "@ember/controller";
|
||||||
import { setDefaultHomepage } from "discourse/lib/utilities";
|
import { setDefaultHomepage } from "discourse/lib/utilities";
|
||||||
import discourseComputed from "discourse-common/utils/decorators";
|
import discourseComputed from "discourse-common/utils/decorators";
|
||||||
import { listThemes, setLocalTheme } from "discourse/lib/theme-selector";
|
import { listThemes, setLocalTheme } from "discourse/lib/theme-selector";
|
||||||
|
import {
|
||||||
|
listColorSchemes,
|
||||||
|
loadColorSchemeStylesheet,
|
||||||
|
updateColorSchemeCookie
|
||||||
|
} from "discourse/lib/color-scheme-picker";
|
||||||
import { popupAjaxError } from "discourse/lib/ajax-error";
|
import { popupAjaxError } from "discourse/lib/ajax-error";
|
||||||
import { reload } from "discourse/helpers/page-reloader";
|
import { reload } from "discourse/helpers/page-reloader";
|
||||||
import {
|
import {
|
||||||
|
@ -12,6 +17,7 @@ import {
|
||||||
iOSWithVisualViewport
|
iOSWithVisualViewport
|
||||||
} from "discourse/lib/utilities";
|
} from "discourse/lib/utilities";
|
||||||
import { computed } from "@ember/object";
|
import { computed } from "@ember/object";
|
||||||
|
import { reads } from "@ember/object/computed";
|
||||||
|
|
||||||
const USER_HOMES = {
|
const USER_HOMES = {
|
||||||
1: "latest",
|
1: "latest",
|
||||||
|
@ -26,14 +32,25 @@ const TITLE_COUNT_MODES = ["notifications", "contextual"];
|
||||||
|
|
||||||
export default Controller.extend({
|
export default Controller.extend({
|
||||||
currentThemeId: -1,
|
currentThemeId: -1,
|
||||||
|
previewingColorScheme: false,
|
||||||
|
selectedColorSchemeId: null,
|
||||||
|
selectedDarkColorSchemeId: null,
|
||||||
preferencesController: inject("preferences"),
|
preferencesController: inject("preferences"),
|
||||||
|
|
||||||
@discourseComputed("makeThemeDefault")
|
init() {
|
||||||
saveAttrNames(makeDefault) {
|
this._super(...arguments);
|
||||||
|
|
||||||
|
this.setProperties({
|
||||||
|
selectedColorSchemeId: this.session.userColorSchemeId,
|
||||||
|
selectedDarkColorSchemeId: this.session.userDarkSchemeId
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("makeThemeDefault", "makeColorSchemeDefault")
|
||||||
|
saveAttrNames(makeThemeDefault, makeColorSchemeDefault) {
|
||||||
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",
|
||||||
|
@ -47,10 +64,14 @@ export default Controller.extend({
|
||||||
"skip_new_user_tips"
|
"skip_new_user_tips"
|
||||||
];
|
];
|
||||||
|
|
||||||
if (makeDefault) {
|
if (makeThemeDefault) {
|
||||||
attrs.push("theme_ids");
|
attrs.push("theme_ids");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (makeColorSchemeDefault) {
|
||||||
|
attrs.push("color_scheme_id");
|
||||||
|
attrs.push("dark_scheme_id");
|
||||||
|
}
|
||||||
return attrs;
|
return attrs;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -71,6 +92,11 @@ export default Controller.extend({
|
||||||
return JSON.parse(this.siteSettings.available_locales);
|
return JSON.parse(this.siteSettings.available_locales);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
defaultDarkSchemeId() {
|
||||||
|
return this.siteSettings.default_dark_mode_color_scheme_id;
|
||||||
|
},
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
textSizes() {
|
textSizes() {
|
||||||
return TEXT_SIZES.map(value => {
|
return TEXT_SIZES.map(value => {
|
||||||
|
@ -116,6 +142,16 @@ export default Controller.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
userSelectableColorSchemes() {
|
||||||
|
return listColorSchemes(this.site);
|
||||||
|
},
|
||||||
|
|
||||||
|
showColorSchemeSelector: reads("userSelectableColorSchemes.length"),
|
||||||
|
selectedColorSchemeNoneLabel: I18n.t(
|
||||||
|
"user.color_schemes.default_description"
|
||||||
|
),
|
||||||
|
|
||||||
@discourseComputed("model.user_option.theme_ids", "themeId")
|
@discourseComputed("model.user_option.theme_ids", "themeId")
|
||||||
showThemeSetDefault(userOptionThemes, selectedTheme) {
|
showThemeSetDefault(userOptionThemes, selectedTheme) {
|
||||||
return !userOptionThemes || userOptionThemes[0] !== selectedTheme;
|
return !userOptionThemes || userOptionThemes[0] !== selectedTheme;
|
||||||
|
@ -153,7 +189,23 @@ export default Controller.extend({
|
||||||
|
|
||||||
@discourseComputed
|
@discourseComputed
|
||||||
showDarkModeToggle() {
|
showDarkModeToggle() {
|
||||||
return this.siteSettings.default_dark_mode_color_scheme_id > 0;
|
return this.defaultDarkSchemeId > 0 && !this.showDarkColorSchemeSelector;
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed
|
||||||
|
userSelectableDarkColorSchemes() {
|
||||||
|
return listColorSchemes(this.site, {
|
||||||
|
darkOnly: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
@discourseComputed("userSelectableDarkColorSchemes")
|
||||||
|
showDarkColorSchemeSelector(darkSchemes) {
|
||||||
|
// when a default dark scheme is set
|
||||||
|
// dropdown has two items (disable / use site default)
|
||||||
|
// but we show a checkbox in that case
|
||||||
|
const minToShow = this.defaultDarkSchemeId > 0 ? 2 : 1;
|
||||||
|
return darkSchemes && darkSchemes.length > minToShow;
|
||||||
},
|
},
|
||||||
|
|
||||||
enableDarkMode: computed({
|
enableDarkMode: computed({
|
||||||
|
@ -178,10 +230,32 @@ export default Controller.extend({
|
||||||
this.set("model.user_option.text_size", this.textSize);
|
this.set("model.user_option.text_size", this.textSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set(
|
if (this.makeColorSchemeDefault) {
|
||||||
"model.user_option.dark_scheme_id",
|
this.set(
|
||||||
this.enableDarkMode ? null : -1
|
"model.user_option.color_scheme_id",
|
||||||
);
|
this.selectedColorSchemeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.showDarkModeToggle) {
|
||||||
|
this.set(
|
||||||
|
"model.user_option.dark_scheme_id",
|
||||||
|
this.enableDarkMode ? null : -1
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// if chosen dark scheme matches site dark scheme, no need to store
|
||||||
|
if (
|
||||||
|
this.defaultDarkSchemeId > 0 &&
|
||||||
|
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
|
||||||
|
) {
|
||||||
|
this.set("model.user_option.dark_scheme_id", null);
|
||||||
|
} else {
|
||||||
|
this.set(
|
||||||
|
"model.user_option.dark_scheme_id",
|
||||||
|
this.selectedDarkColorSchemeId
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return this.model
|
return this.model
|
||||||
.save(this.saveAttrNames)
|
.save(this.saveAttrNames)
|
||||||
|
@ -202,6 +276,24 @@ export default Controller.extend({
|
||||||
this.model.updateTextSizeCookie(this.textSize);
|
this.model.updateTextSizeCookie(this.textSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.makeColorSchemeDefault) {
|
||||||
|
updateColorSchemeCookie(null);
|
||||||
|
updateColorSchemeCookie(null, { dark: true });
|
||||||
|
} else {
|
||||||
|
updateColorSchemeCookie(this.selectedColorSchemeId);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.defaultDarkSchemeId > 0 &&
|
||||||
|
this.selectedDarkColorSchemeId === this.defaultDarkSchemeId
|
||||||
|
) {
|
||||||
|
updateColorSchemeCookie(null, { dark: true });
|
||||||
|
} else {
|
||||||
|
updateColorSchemeCookie(this.selectedDarkColorSchemeId, {
|
||||||
|
dark: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.homeChanged();
|
this.homeChanged();
|
||||||
|
|
||||||
if (this.isiPad) {
|
if (this.isiPad) {
|
||||||
|
@ -236,6 +328,60 @@ export default Controller.extend({
|
||||||
// Force refresh when leaving this screen
|
// Force refresh when leaving this screen
|
||||||
this.session.requiresRefresh = true;
|
this.session.requiresRefresh = true;
|
||||||
this.set("textSize", newSize);
|
this.set("textSize", newSize);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadColorScheme(colorSchemeId) {
|
||||||
|
this.setProperties({
|
||||||
|
selectedColorSchemeId: colorSchemeId,
|
||||||
|
previewingColorScheme: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (colorSchemeId < 0) {
|
||||||
|
const defaultTheme = this.userSelectableThemes.findBy(
|
||||||
|
"id",
|
||||||
|
this.themeId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (defaultTheme && defaultTheme.color_scheme_id) {
|
||||||
|
colorSchemeId = defaultTheme.color_scheme_id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadColorSchemeStylesheet(colorSchemeId, this.themeId);
|
||||||
|
if (this.selectedDarkColorSchemeId === -1) {
|
||||||
|
// set this same scheme for dark mode preview when dark scheme is disabled
|
||||||
|
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
loadDarkColorScheme(colorSchemeId) {
|
||||||
|
this.setProperties({
|
||||||
|
selectedDarkColorSchemeId: colorSchemeId,
|
||||||
|
previewingColorScheme: true
|
||||||
|
});
|
||||||
|
|
||||||
|
if (colorSchemeId === -1) {
|
||||||
|
// load preview of regular scheme when dark scheme is disabled
|
||||||
|
loadColorSchemeStylesheet(
|
||||||
|
this.selectedColorSchemeId,
|
||||||
|
this.themeId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
loadColorSchemeStylesheet(colorSchemeId, this.themeId, true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
undoColorSchemePreview() {
|
||||||
|
this.setProperties({
|
||||||
|
selectedColorSchemeId: this.session.userColorSchemeId,
|
||||||
|
selectedDarkColorSchemeId: this.session.userDarkSchemeId,
|
||||||
|
previewingColorScheme: false
|
||||||
|
});
|
||||||
|
const darkStylesheet = document.querySelector("link#cs-preview-dark"),
|
||||||
|
lightStylesheet = document.querySelector("link#cs-preview-light");
|
||||||
|
if (darkStylesheet) darkStylesheet.remove();
|
||||||
|
|
||||||
|
if (lightStylesheet) lightStylesheet.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,88 @@
|
||||||
|
import I18n from "I18n";
|
||||||
|
import { ajax } from "discourse/lib/ajax";
|
||||||
|
import cookie, { removeCookie } from "discourse/lib/cookie";
|
||||||
|
|
||||||
|
export function listColorSchemes(site, options = {}) {
|
||||||
|
let schemes = site.get("user_color_schemes");
|
||||||
|
|
||||||
|
if (!schemes || !Array.isArray(schemes)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let results = [];
|
||||||
|
|
||||||
|
if (!options.darkOnly) {
|
||||||
|
schemes = schemes.sort((a, b) => Number(a.is_dark) - Number(b.is_dark));
|
||||||
|
}
|
||||||
|
schemes.forEach(s => {
|
||||||
|
if ((options.darkOnly && s.is_dark) || !options.darkOnly) {
|
||||||
|
results.push({
|
||||||
|
name: s.name,
|
||||||
|
id: s.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (options.darkOnly) {
|
||||||
|
const defaultDarkColorScheme = site.get("default_dark_color_scheme");
|
||||||
|
if (defaultDarkColorScheme) {
|
||||||
|
const existing = schemes.findBy("id", defaultDarkColorScheme.id);
|
||||||
|
if (!existing) {
|
||||||
|
results.unshift({
|
||||||
|
id: defaultDarkColorScheme.id,
|
||||||
|
name: `${defaultDarkColorScheme.name} ${I18n.t(
|
||||||
|
"user.color_schemes.default_dark_scheme"
|
||||||
|
)}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results.unshift({
|
||||||
|
id: -1,
|
||||||
|
name: I18n.t("user.color_schemes.disable_dark_scheme")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.length === 0 ? null : results;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadColorSchemeStylesheet(
|
||||||
|
colorSchemeId,
|
||||||
|
theme_id,
|
||||||
|
dark = false
|
||||||
|
) {
|
||||||
|
const themeId = theme_id ? `/${theme_id}` : "";
|
||||||
|
ajax(`/color-scheme-stylesheet/${colorSchemeId}${themeId}.json`).then(
|
||||||
|
result => {
|
||||||
|
if (result && result.new_href) {
|
||||||
|
const elementId = dark ? "cs-preview-dark" : "cs-preview-light";
|
||||||
|
const existingElement = document.querySelector(`link#${elementId}`);
|
||||||
|
if (existingElement) {
|
||||||
|
existingElement.href = result.new_href;
|
||||||
|
} else {
|
||||||
|
let link = document.createElement("link");
|
||||||
|
link.href = result.new_href;
|
||||||
|
link.media = dark
|
||||||
|
? "(prefers-color-scheme: dark)"
|
||||||
|
: "(prefers-color-scheme: light)";
|
||||||
|
link.rel = "stylesheet";
|
||||||
|
link.id = elementId;
|
||||||
|
|
||||||
|
document.body.appendChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateColorSchemeCookie(id, options = {}) {
|
||||||
|
const cookieName = options.dark ? "dark_scheme_id" : "color_scheme_id";
|
||||||
|
if (id) {
|
||||||
|
cookie(cookieName, id, {
|
||||||
|
path: "/",
|
||||||
|
expires: 9999
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
removeCookie(cookieName, { path: "/", expires: 1 });
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,7 +94,11 @@ export function listThemes(site) {
|
||||||
}
|
}
|
||||||
|
|
||||||
themes.forEach(t => {
|
themes.forEach(t => {
|
||||||
results.push({ name: t.name, id: t.theme_id });
|
results.push({
|
||||||
|
name: t.name,
|
||||||
|
id: t.theme_id,
|
||||||
|
color_scheme_id: t.color_scheme_id
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return results.length === 0 ? null : results;
|
return results.length === 0 ? null : results;
|
||||||
|
|
|
@ -302,6 +302,7 @@ const User = RestModel.extend({
|
||||||
"email_messages_level",
|
"email_messages_level",
|
||||||
"email_level",
|
"email_level",
|
||||||
"email_previous_replies",
|
"email_previous_replies",
|
||||||
|
"color_scheme_id",
|
||||||
"dark_scheme_id",
|
"dark_scheme_id",
|
||||||
"dynamic_favicon",
|
"dynamic_favicon",
|
||||||
"enable_quoting",
|
"enable_quoting",
|
||||||
|
|
|
@ -90,6 +90,9 @@ export default {
|
||||||
|
|
||||||
session.highlightJsPath = setupData.highlightJsPath;
|
session.highlightJsPath = setupData.highlightJsPath;
|
||||||
session.svgSpritePath = setupData.svgSpritePath;
|
session.svgSpritePath = setupData.svgSpritePath;
|
||||||
|
session.userColorSchemeId =
|
||||||
|
parseInt(setupData.userColorSchemeId, 10) || null;
|
||||||
|
session.userDarkSchemeId = parseInt(setupData.userDarkSchemeId, 10) || -1;
|
||||||
|
|
||||||
if (isDevelopment()) {
|
if (isDevelopment()) {
|
||||||
setIconList(setupData.svgIconList);
|
setIconList(setupData.svgIconList);
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default RestrictedUserRoute.extend({
|
||||||
makeThemeDefault:
|
makeThemeDefault:
|
||||||
!user.get("user_option.theme_ids") ||
|
!user.get("user_option.theme_ids") ||
|
||||||
currentThemeId() === user.get("user_option.theme_ids")[0],
|
currentThemeId() === user.get("user_option.theme_ids")[0],
|
||||||
|
makeColorSchemeDefault: !user.get("user_option.color_scheme_id"),
|
||||||
makeTextSizeDefault:
|
makeTextSizeDefault:
|
||||||
user.get("currentTextSize") === user.get("user_option.text_size")
|
user.get("currentTextSize") === user.get("user_option.text_size")
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,53 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if showColorSchemeSelector}}
|
||||||
|
<div class="control-group color-scheme">
|
||||||
|
<label class="control-label">{{i18n "user.color_scheme"}}</label>
|
||||||
|
<div class="control-subgroup light-color-scheme">
|
||||||
|
{{#if showDarkColorSchemeSelector}}
|
||||||
|
<div class="instructions">{{i18n "user.color_schemes.regular" }}</div>
|
||||||
|
{{/if}}
|
||||||
|
<div class="controls">
|
||||||
|
{{combo-box
|
||||||
|
content=userSelectableColorSchemes
|
||||||
|
value=selectedColorSchemeId
|
||||||
|
onChange=(action "loadColorScheme")
|
||||||
|
options=(hash
|
||||||
|
translatedNone=selectedColorSchemeNoneLabel
|
||||||
|
)
|
||||||
|
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#if showDarkColorSchemeSelector}}
|
||||||
|
<div class="control-subgroup dark-color-scheme">
|
||||||
|
<div class="instructions">{{i18n "user.color_schemes.dark" }}</div>
|
||||||
|
<div class="controls">
|
||||||
|
{{combo-box
|
||||||
|
content=userSelectableDarkColorSchemes
|
||||||
|
value=selectedDarkColorSchemeId
|
||||||
|
onChange=(action "loadDarkColorScheme")}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="instructions">
|
||||||
|
{{i18n "user.color_schemes.dark_instructions" }}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if previewingColorScheme}}
|
||||||
|
{{#if previewingColorScheme}}
|
||||||
|
{{d-button action=(action "undoColorSchemePreview") label="user.color_schemes.undo" icon="undo" class="btn-default btn-small undo-preview"}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<div class="controls color-scheme-checkbox">
|
||||||
|
{{preference-checkbox labelKey="user.color_scheme_default_on_all_devices" checked=makeColorSchemeDefault}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
{{#if showDarkModeToggle}}
|
{{#if showDarkModeToggle}}
|
||||||
<div class="control-group dark-mode">
|
<div class="control-group dark-mode">
|
||||||
<label class="control-label">{{i18n "user.dark_mode"}}</label>
|
<label class="control-label">{{i18n "user.dark_mode"}}</label>
|
||||||
|
|
|
@ -179,6 +179,10 @@
|
||||||
margin: 0 0.5em 0.5em 0;
|
margin: 0 0.5em 0.5em 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.color-scheme .admin-controls {
|
||||||
|
padding: 0.5em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.add-component-button {
|
.add-component-button {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
|
@ -486,10 +490,16 @@
|
||||||
}
|
}
|
||||||
.color-scheme {
|
.color-scheme {
|
||||||
.controls {
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
button,
|
button,
|
||||||
a {
|
a {
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button.btn-danger {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.colors {
|
.colors {
|
||||||
|
@ -507,8 +517,8 @@
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
.hex {
|
th.overriden {
|
||||||
text-align: center;
|
text-align: right;
|
||||||
}
|
}
|
||||||
.color-input {
|
.color-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -656,6 +656,46 @@
|
||||||
.save-theme-alert {
|
.save-theme-alert {
|
||||||
font-size: $font-down-1;
|
font-size: $font-down-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.control-subgroup {
|
||||||
|
float: left;
|
||||||
|
+ .controls {
|
||||||
|
clear: both;
|
||||||
|
padding-top: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.light-color-scheme {
|
||||||
|
margin-right: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.instructions {
|
||||||
|
clear: both;
|
||||||
|
display: inline-block;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@mixin inactiveMode() {
|
||||||
|
color: var(--primary-medium);
|
||||||
|
.select-kit.combo-box .select-kit-header {
|
||||||
|
border-color: var(--primary-medium);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.light-color-scheme {
|
||||||
|
@include inactiveMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (prefers-color-scheme: light) {
|
||||||
|
.dark-color-scheme {
|
||||||
|
@include inactiveMode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.undo-preview {
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.paginated-topics-list {
|
.paginated-topics-list {
|
||||||
|
|
|
@ -247,11 +247,6 @@
|
||||||
.user-chooser {
|
.user-chooser {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.instructions {
|
|
||||||
display: inline-block;
|
|
||||||
margin-top: 4px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-crawler {
|
.user-crawler {
|
||||||
|
|
|
@ -38,6 +38,6 @@ class Admin::ColorSchemesController < Admin::AdminController
|
||||||
end
|
end
|
||||||
|
|
||||||
def color_scheme_params
|
def color_scheme_params
|
||||||
params.permit(color_scheme: [:base_scheme_id, :name, colors: [:name, :hex]])[:color_scheme]
|
params.permit(color_scheme: [:base_scheme_id, :name, :user_selectable, colors: [:name, :hex]])[:color_scheme]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class StylesheetsController < ApplicationController
|
class StylesheetsController < ApplicationController
|
||||||
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_source_map]
|
skip_before_action :preload_json, :redirect_to_login_if_required, :check_xhr, :verify_authenticity_token, only: [:show, :show_source_map, :color_scheme]
|
||||||
|
|
||||||
def show_source_map
|
def show_source_map
|
||||||
show_resource(source_map: true)
|
show_resource(source_map: true)
|
||||||
|
@ -13,6 +13,13 @@ class StylesheetsController < ApplicationController
|
||||||
show_resource
|
show_resource
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def color_scheme
|
||||||
|
params.require("id")
|
||||||
|
params.permit("theme_id")
|
||||||
|
|
||||||
|
stylesheet = Stylesheet::Manager.color_scheme_stylesheet_details(params[:id], 'all', params[:theme_id])
|
||||||
|
render json: stylesheet
|
||||||
|
end
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def show_resource(source_map: false)
|
def show_resource(source_map: false)
|
||||||
|
@ -28,7 +35,7 @@ class StylesheetsController < ApplicationController
|
||||||
if !Rails.env.production?
|
if !Rails.env.production?
|
||||||
# TODO add theme
|
# TODO add theme
|
||||||
# calling this method ensures we have a cache for said target
|
# calling this method ensures we have a cache for said target
|
||||||
# we hold of re-compilation till someone asks for asset
|
# we hold off re-compilation till someone asks for asset
|
||||||
if target.include?("color_definitions")
|
if target.include?("color_definitions")
|
||||||
split_target, color_scheme_id = target.split(/_(-?[0-9]+)/)
|
split_target, color_scheme_id = target.split(/_(-?[0-9]+)/)
|
||||||
Stylesheet::Manager.color_scheme_stylesheet_link_tag(color_scheme_id)
|
Stylesheet::Manager.color_scheme_stylesheet_link_tag(color_scheme_id)
|
||||||
|
|
|
@ -402,6 +402,11 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def scheme_id
|
def scheme_id
|
||||||
|
custom_user_scheme_id = cookies[:color_scheme_id] || current_user&.user_option&.color_scheme_id
|
||||||
|
if custom_user_scheme_id && ColorScheme.find_by_id(custom_user_scheme_id)
|
||||||
|
return custom_user_scheme_id
|
||||||
|
end
|
||||||
|
|
||||||
return if theme_ids.blank?
|
return if theme_ids.blank?
|
||||||
Theme
|
Theme
|
||||||
.where(id: theme_ids.first)
|
.where(id: theme_ids.first)
|
||||||
|
@ -409,6 +414,10 @@ module ApplicationHelper
|
||||||
.first
|
.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def dark_scheme_id
|
||||||
|
cookies[:dark_scheme_id] || current_user&.user_option&.dark_scheme_id || SiteSetting.default_dark_mode_color_scheme_id
|
||||||
|
end
|
||||||
|
|
||||||
def current_homepage
|
def current_homepage
|
||||||
current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
|
current_user&.user_option&.homepage || SiteSetting.anonymous_homepage
|
||||||
end
|
end
|
||||||
|
@ -454,9 +463,6 @@ module ApplicationHelper
|
||||||
result = +""
|
result = +""
|
||||||
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(scheme_id, 'all', theme_ids)
|
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(scheme_id, 'all', theme_ids)
|
||||||
|
|
||||||
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)', theme_ids)
|
result << Stylesheet::Manager.color_scheme_stylesheet_link_tag(dark_scheme_id, '(prefers-color-scheme: dark)', theme_ids)
|
||||||
end
|
end
|
||||||
|
@ -489,7 +495,9 @@ module ApplicationHelper
|
||||||
highlight_js_path: HighlightJs.path,
|
highlight_js_path: HighlightJs.path,
|
||||||
svg_sprite_path: SvgSprite.path(theme_ids),
|
svg_sprite_path: SvgSprite.path(theme_ids),
|
||||||
enable_js_error_reporting: GlobalSetting.enable_js_error_reporting,
|
enable_js_error_reporting: GlobalSetting.enable_js_error_reporting,
|
||||||
color_scheme_is_dark: dark_color_scheme?
|
color_scheme_is_dark: dark_color_scheme?,
|
||||||
|
user_color_scheme_id: scheme_id,
|
||||||
|
user_dark_scheme_id: dark_scheme_id
|
||||||
}
|
}
|
||||||
|
|
||||||
if Rails.env.development?
|
if Rails.env.development?
|
||||||
|
|
|
@ -131,8 +131,8 @@ class ColorScheme < ActiveRecord::Base
|
||||||
|
|
||||||
before_save :bump_version
|
before_save :bump_version
|
||||||
after_save :publish_discourse_stylesheet
|
after_save :publish_discourse_stylesheet
|
||||||
after_save :dump_hex_cache
|
after_save :dump_caches
|
||||||
after_destroy :dump_hex_cache
|
after_destroy :dump_caches
|
||||||
belongs_to :theme
|
belongs_to :theme
|
||||||
|
|
||||||
validates_associated :color_scheme_colors
|
validates_associated :color_scheme_colors
|
||||||
|
@ -286,8 +286,9 @@ class ColorScheme < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def dump_hex_cache
|
def dump_caches
|
||||||
self.class.hex_cache.clear
|
self.class.hex_cache.clear
|
||||||
|
ApplicationSerializer.expire_cache_fragment!("user_color_schemes")
|
||||||
end
|
end
|
||||||
|
|
||||||
def bump_version
|
def bump_version
|
||||||
|
@ -297,6 +298,8 @@ class ColorScheme < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def is_dark?
|
def is_dark?
|
||||||
|
return if colors.empty?
|
||||||
|
|
||||||
primary_b = brightness(colors_by_name["primary"].hex)
|
primary_b = brightness(colors_by_name["primary"].hex)
|
||||||
secondary_b = brightness(colors_by_name["secondary"].hex)
|
secondary_b = brightness(colors_by_name["secondary"].hex)
|
||||||
|
|
||||||
|
@ -314,12 +317,13 @@ end
|
||||||
#
|
#
|
||||||
# Table name: color_schemes
|
# Table name: color_schemes
|
||||||
#
|
#
|
||||||
# id :integer not null, primary key
|
# id :integer not null, primary key
|
||||||
# name :string not null
|
# name :string not null
|
||||||
# version :integer default(1), not null
|
# version :integer default(1), not null
|
||||||
# created_at :datetime not null
|
# created_at :datetime not null
|
||||||
# updated_at :datetime not null
|
# updated_at :datetime not null
|
||||||
# via_wizard :boolean default(FALSE), not null
|
# via_wizard :boolean default(FALSE), not null
|
||||||
# base_scheme_id :string
|
# base_scheme_id :string
|
||||||
# theme_id :integer
|
# theme_id :integer
|
||||||
|
# user_selectable :boolean default(FALSE), not null
|
||||||
#
|
#
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ColorSchemeSelectableSerializer < ApplicationSerializer
|
||||||
|
attributes :id, :name, :is_dark
|
||||||
|
|
||||||
|
def is_dark
|
||||||
|
object.is_dark?
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ColorSchemeSerializer < ApplicationSerializer
|
class ColorSchemeSerializer < ApplicationSerializer
|
||||||
attributes :id, :name, :is_base, :base_scheme_id, :theme_id, :theme_name
|
attributes :id, :name, :is_base, :base_scheme_id, :theme_id, :theme_name, :user_selectable
|
||||||
has_many :colors, serializer: ColorSchemeColorSerializer, embed: :objects
|
has_many :colors, serializer: ColorSchemeColorSerializer, embed: :objects
|
||||||
|
|
||||||
def theme_name
|
def theme_name
|
||||||
|
|
|
@ -24,6 +24,8 @@ class SiteSerializer < ApplicationSerializer
|
||||||
:wizard_required,
|
:wizard_required,
|
||||||
:topic_featured_link_allowed_category_ids,
|
:topic_featured_link_allowed_category_ids,
|
||||||
:user_themes,
|
:user_themes,
|
||||||
|
:user_color_schemes,
|
||||||
|
:default_dark_color_scheme,
|
||||||
:censored_regexp,
|
:censored_regexp,
|
||||||
:shared_drafts_category_id,
|
:shared_drafts_category_id,
|
||||||
:custom_emoji_translation
|
:custom_emoji_translation
|
||||||
|
@ -40,12 +42,23 @@ class SiteSerializer < ApplicationSerializer
|
||||||
Theme.where('id = :default OR user_selectable',
|
Theme.where('id = :default OR user_selectable',
|
||||||
default: SiteSetting.default_theme_id)
|
default: SiteSetting.default_theme_id)
|
||||||
.order(:name)
|
.order(:name)
|
||||||
.pluck(:id, :name)
|
.pluck(:id, :name, :color_scheme_id)
|
||||||
.map { |id, n| { theme_id: id, name: n, default: id == SiteSetting.default_theme_id } }
|
.map { |id, n, cs| { theme_id: id, name: n, default: id == SiteSetting.default_theme_id, color_scheme_id: cs } }
|
||||||
.as_json
|
.as_json
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def user_color_schemes
|
||||||
|
cache_fragment("user_color_schemes") do
|
||||||
|
schemes = ColorScheme.where('user_selectable').order(:name)
|
||||||
|
ActiveModel::ArraySerializer.new(schemes, each_serializer: ColorSchemeSelectableSerializer).as_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_dark_color_scheme
|
||||||
|
ColorScheme.find_by_id(SiteSetting.default_dark_mode_color_scheme_id).as_json
|
||||||
|
end
|
||||||
|
|
||||||
def groups
|
def groups
|
||||||
cache_anon_fragment("group_names") do
|
cache_anon_fragment("group_names") do
|
||||||
object.groups.order(:name).pluck(:id, :name).map { |id, name| { id: id, name: name } }.as_json
|
object.groups.order(:name).pluck(:id, :name).map { |id, name| { id: id, name: name } }.as_json
|
||||||
|
|
|
@ -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,
|
||||||
|
:color_scheme_id,
|
||||||
:dark_scheme_id,
|
:dark_scheme_id,
|
||||||
:dynamic_favicon,
|
:dynamic_favicon,
|
||||||
:enable_quoting,
|
:enable_quoting,
|
||||||
|
|
|
@ -13,8 +13,8 @@ class ColorSchemeRevisor
|
||||||
|
|
||||||
def revise
|
def revise
|
||||||
ColorScheme.transaction do
|
ColorScheme.transaction do
|
||||||
|
@color_scheme.name = @params[:name] if @params.has_key?(:name)
|
||||||
@color_scheme.name = @params[:name] if @params.has_key?(:name)
|
@color_scheme.user_selectable = @params[:user_selectable] if @params.has_key?(:user_selectable)
|
||||||
@color_scheme.base_scheme_id = @params[:base_scheme_id] if @params.has_key?(:base_scheme_id)
|
@color_scheme.base_scheme_id = @params[:base_scheme_id] if @params.has_key?(:base_scheme_id)
|
||||||
has_colors = @params[:colors]
|
has_colors = @params[:colors]
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ class ColorSchemeRevisor
|
||||||
|
|
||||||
if has_colors ||
|
if has_colors ||
|
||||||
@color_scheme.saved_change_to_name? ||
|
@color_scheme.saved_change_to_name? ||
|
||||||
|
@color_scheme.will_save_change_to_user_selectable? ||
|
||||||
@color_scheme.saved_change_to_base_scheme_id?
|
@color_scheme.saved_change_to_base_scheme_id?
|
||||||
|
|
||||||
@color_scheme.save
|
@color_scheme.save
|
||||||
|
|
|
@ -26,6 +26,7 @@ class UserUpdater
|
||||||
:external_links_in_new_tab,
|
:external_links_in_new_tab,
|
||||||
:enable_quoting,
|
:enable_quoting,
|
||||||
:enable_defer,
|
:enable_defer,
|
||||||
|
:color_scheme_id,
|
||||||
:dark_scheme_id,
|
:dark_scheme_id,
|
||||||
:dynamic_favicon,
|
:dynamic_favicon,
|
||||||
:automatically_unpin_topics,
|
:automatically_unpin_topics,
|
||||||
|
|
|
@ -935,6 +935,16 @@ en:
|
||||||
not_first_time: "Not your first time?"
|
not_first_time: "Not your first time?"
|
||||||
skip_link: "Skip these tips"
|
skip_link: "Skip these tips"
|
||||||
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"
|
||||||
|
color_scheme_default_on_all_devices: "Set default color scheme(s) on all my devices"
|
||||||
|
color_scheme: "Color Scheme"
|
||||||
|
color_schemes:
|
||||||
|
default_description: "Default"
|
||||||
|
disable_dark_scheme: "Same as regular"
|
||||||
|
dark_instructions: "You can preview the dark mode color scheme by toggling your device's dark mode."
|
||||||
|
undo: "Reset"
|
||||||
|
regular: "Regular"
|
||||||
|
dark: "Dark mode"
|
||||||
|
default_dark_scheme: "(site default)"
|
||||||
dark_mode: "Dark Mode"
|
dark_mode: "Dark Mode"
|
||||||
dark_mode_enable: "Enable automatic dark mode color scheme"
|
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"
|
||||||
|
@ -3886,6 +3896,7 @@ en:
|
||||||
hide_unused_fields: "Hide unused fields"
|
hide_unused_fields: "Hide unused fields"
|
||||||
is_default: "Theme is enabled by default"
|
is_default: "Theme is enabled by default"
|
||||||
user_selectable: "Theme can be selected by users"
|
user_selectable: "Theme can be selected by users"
|
||||||
|
color_scheme_user_selectable: "Color scheme can be selected by users"
|
||||||
color_scheme: "Color Palette"
|
color_scheme: "Color Palette"
|
||||||
default_light_scheme: "Light (default)"
|
default_light_scheme: "Light (default)"
|
||||||
color_scheme_select: "Select colors to be used by theme"
|
color_scheme_select: "Select colors to be used by theme"
|
||||||
|
|
|
@ -518,6 +518,7 @@ Discourse::Application.routes.draw do
|
||||||
|
|
||||||
get "stylesheets/:name.css.map" => "stylesheets#show_source_map", constraints: { name: /[-a-z0-9_]+/ }
|
get "stylesheets/:name.css.map" => "stylesheets#show_source_map", constraints: { name: /[-a-z0-9_]+/ }
|
||||||
get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ }
|
get "stylesheets/:name.css" => "stylesheets#show", constraints: { name: /[-a-z0-9_]+/ }
|
||||||
|
get "color-scheme-stylesheet/:id(/:theme_id)" => "stylesheets#color_scheme", constraints: { format: :json }
|
||||||
get "theme-javascripts/:digest.js" => "theme_javascripts#show", constraints: { digest: /\h{40}/ }
|
get "theme-javascripts/:digest.js" => "theme_javascripts#show", constraints: { digest: /\h{40}/ }
|
||||||
|
|
||||||
post "uploads/lookup-metadata" => "uploads#metadata"
|
post "uploads/lookup-metadata" => "uploads#metadata"
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddUserSelectableColumnToColorSchemes < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :color_schemes, :user_selectable, :bool, null: false, default: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddColorSchemeIdToUserOptions < ActiveRecord::Migration[6.0]
|
||||||
|
def change
|
||||||
|
add_column :user_options, :color_scheme_id, :integer
|
||||||
|
end
|
||||||
|
end
|
|
@ -374,11 +374,44 @@ describe ApplicationHelper do
|
||||||
expect(cs_stylesheets).not_to include("(prefers-color-scheme: dark)")
|
expect(cs_stylesheets).not_to include("(prefers-color-scheme: dark)")
|
||||||
end
|
end
|
||||||
|
|
||||||
context "with a user option" do
|
context "custom light scheme" do
|
||||||
|
before do
|
||||||
|
@new_cs = Fabricate(:color_scheme, name: 'Flamboyant')
|
||||||
|
user.user_option.color_scheme_id = @new_cs.id
|
||||||
|
user.user_option.save!
|
||||||
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns color scheme from user option value" do
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
expect(color_stylesheets).to include("color_definitions_flamboyant")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns color scheme from cookie value" do
|
||||||
|
cs = ColorScheme.where(name: "Dark").first
|
||||||
|
helper.request.cookies["color_scheme_id"] = cs.id
|
||||||
|
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
|
||||||
|
expect(color_stylesheets).to include("color_definitions_dark")
|
||||||
|
expect(color_stylesheets).not_to include("color_definitions_flamboyant")
|
||||||
|
end
|
||||||
|
|
||||||
|
it "falls back to base scheme with invalid cookie value" do
|
||||||
|
helper.request.cookies["color_scheme_id"] = -50
|
||||||
|
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
expect(color_stylesheets).not_to include("color_definitions_flamboyant")
|
||||||
|
expect(color_stylesheets).to include("color_definitions_base")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "dark scheme with user option and/or cookies" do
|
||||||
before do
|
before do
|
||||||
user.user_option.dark_scheme_id = -1
|
user.user_option.dark_scheme_id = -1
|
||||||
user.user_option.save!
|
user.user_option.save!
|
||||||
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
helper.request.env[Auth::DefaultCurrentUserProvider::CURRENT_USER_KEY] = user
|
||||||
|
@new_cs = Fabricate(:color_scheme, name: 'Custom Color Scheme')
|
||||||
|
|
||||||
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck(:id).first
|
SiteSetting.default_dark_mode_color_scheme_id = ColorScheme.where(name: "Dark").pluck(:id).first
|
||||||
end
|
end
|
||||||
|
@ -391,14 +424,28 @@ describe ApplicationHelper do
|
||||||
end
|
end
|
||||||
|
|
||||||
it "returns user-selected dark color scheme stylesheet" do
|
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)
|
||||||
user.user_option.update!(dark_scheme_id: new_cs.id)
|
|
||||||
|
|
||||||
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
expect(color_stylesheets).to include("(prefers-color-scheme: dark)")
|
expect(color_stylesheets).to include("(prefers-color-scheme: dark)")
|
||||||
expect(color_stylesheets).to include("custom-color-scheme")
|
expect(color_stylesheets).to include("custom-color-scheme")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "respects cookie value over user option for dark color scheme" do
|
||||||
|
helper.request.cookies["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
|
||||||
|
|
||||||
|
it "returns no dark scheme with invalid cookie value" do
|
||||||
|
helper.request.cookies["dark_scheme_id"] = -10
|
||||||
|
|
||||||
|
color_stylesheets = helper.discourse_color_scheme_stylesheets
|
||||||
|
expect(color_stylesheets).not_to include("(prefers-color-scheme: dark)")
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -96,5 +96,10 @@ describe ColorScheme do
|
||||||
ColorSchemeRevisor.revise(scheme, colors: [{ name: 'primary', hex: 'F8F8F8' }, { name: 'secondary', hex: '232323' }])
|
ColorSchemeRevisor.revise(scheme, colors: [{ name: 'primary', hex: 'F8F8F8' }, { name: 'secondary', hex: '232323' }])
|
||||||
expect(scheme.is_dark?).to eq(true)
|
expect(scheme.is_dark?).to eq(true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "does not break in scheme without colors" do
|
||||||
|
scheme = ColorScheme.create(name: "No Bueno")
|
||||||
|
expect(scheme.is_dark?).to eq(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,8 +11,8 @@ describe Site do
|
||||||
expected = Theme.where('id = :default OR user_selectable',
|
expected = Theme.where('id = :default OR user_selectable',
|
||||||
default: SiteSetting.default_theme_id)
|
default: SiteSetting.default_theme_id)
|
||||||
.order(:name)
|
.order(:name)
|
||||||
.pluck(:id, :name)
|
.pluck(:id, :name, :color_scheme_id)
|
||||||
.map { |id, n| { "theme_id" => id, "name" => n, "default" => id == SiteSetting.default_theme_id } }
|
.map { |id, n, cs| { "theme_id" => id, "name" => n, "default" => id == SiteSetting.default_theme_id, "color_scheme_id" => cs } }
|
||||||
|
|
||||||
expect(parsed["user_themes"]).to eq(expected)
|
expect(parsed["user_themes"]).to eq(expected)
|
||||||
end
|
end
|
||||||
|
|
|
@ -57,4 +57,26 @@ describe StylesheetsController do
|
||||||
|
|
||||||
expect(response.status).to eq(200)
|
expect(response.status).to eq(200)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "#color_scheme" do
|
||||||
|
it 'works as expected' do
|
||||||
|
scheme = ColorScheme.last
|
||||||
|
get "/color-scheme-stylesheet/#{scheme.id}.json"
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json["color_scheme_id"]).to eq(scheme.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'works with a theme parameter' do
|
||||||
|
scheme = ColorScheme.last
|
||||||
|
theme = Theme.last
|
||||||
|
get "/color-scheme-stylesheet/#{scheme.id}/#{theme.id}.json"
|
||||||
|
|
||||||
|
expect(response.status).to eq(200)
|
||||||
|
json = JSON.parse(response.body)
|
||||||
|
expect(json["color_scheme_id"]).to eq(scheme.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -28,4 +28,33 @@ describe SiteSerializer do
|
||||||
expect(categories[0][:notification_level]).to eq(0)
|
expect(categories[0][:notification_level]).to eq(0)
|
||||||
expect(categories[-1][:notification_level]).to eq(1)
|
expect(categories[-1][:notification_level]).to eq(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "includes user-selectable color schemes" do
|
||||||
|
scheme = ColorScheme.create_from_base(name: "Neutral", base_scheme_id: "Neutral")
|
||||||
|
scheme.user_selectable = true
|
||||||
|
scheme.save!
|
||||||
|
|
||||||
|
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
|
||||||
|
expect(serialized[:user_color_schemes].count).to eq (1)
|
||||||
|
|
||||||
|
dark_scheme = ColorScheme.create_from_base(name: "ADarkScheme", base_scheme_id: "Dark")
|
||||||
|
dark_scheme.user_selectable = true
|
||||||
|
dark_scheme.save!
|
||||||
|
|
||||||
|
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
|
||||||
|
expect(serialized[:user_color_schemes].count).to eq(2)
|
||||||
|
expect(serialized[:user_color_schemes][0][:is_dark]).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "includes default dark mode scheme" do
|
||||||
|
scheme = ColorScheme.last
|
||||||
|
SiteSetting.default_dark_mode_color_scheme_id = scheme.id
|
||||||
|
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
|
||||||
|
default_dark_scheme =
|
||||||
|
expect(serialized[:default_dark_color_scheme]["name"]).to eq(scheme.name)
|
||||||
|
|
||||||
|
SiteSetting.default_dark_mode_color_scheme_id = -1
|
||||||
|
serialized = described_class.new(Site.new(guardian), scope: guardian, root: false).as_json
|
||||||
|
expect(serialized[:default_dark_color_scheme]).to eq(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -48,6 +48,14 @@ describe ColorSchemeRevisor do
|
||||||
}.to_not change { color_scheme.reload.version }
|
}.to_not change { color_scheme.reload.version }
|
||||||
expect(color_scheme.colors.first.hex).to eq(color.hex)
|
expect(color_scheme.colors.first.hex).to eq(color.hex)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "can change the user_selectable column" do
|
||||||
|
expect(color_scheme.user_selectable).to eq(false)
|
||||||
|
|
||||||
|
ColorSchemeRevisor.revise(color_scheme, { user_selectable: true })
|
||||||
|
expect(color_scheme.reload.user_selectable).to eq(true)
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,6 @@ import I18n from "I18n";
|
||||||
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
|
import { acceptance, updateCurrentUser } from "helpers/qunit-helpers";
|
||||||
import selectKit from "helpers/select-kit-helper";
|
import selectKit from "helpers/select-kit-helper";
|
||||||
import User from "discourse/models/user";
|
import User from "discourse/models/user";
|
||||||
import cookie, { removeCookie } from "discourse/lib/cookie";
|
|
||||||
|
|
||||||
function preferencesPretender(server, helper) {
|
function preferencesPretender(server, helper) {
|
||||||
server.post("/u/second_factors.json", () => {
|
server.post("/u/second_factors.json", () => {
|
||||||
|
@ -122,51 +121,6 @@ QUnit.test("update some fields", async assert => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
QUnit.test("font size change", async assert => {
|
|
||||||
removeCookie("text_size");
|
|
||||||
|
|
||||||
const savePreferences = async () => {
|
|
||||||
assert.ok(!exists(".saved"), "it hasn't been saved yet");
|
|
||||||
await click(".save-changes");
|
|
||||||
assert.ok(exists(".saved"), "it displays the saved message");
|
|
||||||
find(".saved").remove();
|
|
||||||
};
|
|
||||||
|
|
||||||
await visit("/u/eviltrout/preferences/interface");
|
|
||||||
|
|
||||||
// Live changes without reload
|
|
||||||
await selectKit(".text-size .combobox").expand();
|
|
||||||
await selectKit(".text-size .combobox").selectRowByValue("larger");
|
|
||||||
assert.ok(document.documentElement.classList.contains("text-size-larger"));
|
|
||||||
|
|
||||||
await selectKit(".text-size .combobox").expand();
|
|
||||||
await selectKit(".text-size .combobox").selectRowByValue("largest");
|
|
||||||
assert.ok(document.documentElement.classList.contains("text-size-largest"));
|
|
||||||
|
|
||||||
assert.equal(cookie("text_size"), null, "cookie is not set");
|
|
||||||
|
|
||||||
// Click save (by default this sets for all browsers, no cookie)
|
|
||||||
await savePreferences();
|
|
||||||
|
|
||||||
assert.equal(cookie("text_size"), null, "cookie is not set");
|
|
||||||
|
|
||||||
await selectKit(".text-size .combobox").expand();
|
|
||||||
await selectKit(".text-size .combobox").selectRowByValue("larger");
|
|
||||||
await click(".text-size input[type=checkbox]");
|
|
||||||
|
|
||||||
await savePreferences();
|
|
||||||
|
|
||||||
assert.equal(cookie("text_size"), "larger|1", "cookie is set");
|
|
||||||
await click(".text-size input[type=checkbox]");
|
|
||||||
await selectKit(".text-size .combobox").expand();
|
|
||||||
await selectKit(".text-size .combobox").selectRowByValue("largest");
|
|
||||||
|
|
||||||
await savePreferences();
|
|
||||||
assert.equal(cookie("text_size"), null, "cookie is removed");
|
|
||||||
|
|
||||||
removeCookie("text_size");
|
|
||||||
});
|
|
||||||
|
|
||||||
QUnit.test("username", async assert => {
|
QUnit.test("username", async assert => {
|
||||||
await visit("/u/eviltrout/preferences/username");
|
await visit("/u/eviltrout/preferences/username");
|
||||||
assert.ok(exists("#change_username"), "it has the input element");
|
assert.ok(exists("#change_username"), "it has the input element");
|
||||||
|
@ -483,16 +437,3 @@ 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"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,206 @@
|
||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
import selectKit from "helpers/select-kit-helper";
|
||||||
|
import Site from "discourse/models/site";
|
||||||
|
import Session from "discourse/models/session";
|
||||||
|
import cookie, { removeCookie } from "discourse/lib/cookie";
|
||||||
|
|
||||||
|
acceptance("User Preferences - Interface", {
|
||||||
|
loggedIn: true
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("font size change", async assert => {
|
||||||
|
removeCookie("text_size");
|
||||||
|
|
||||||
|
const savePreferences = async () => {
|
||||||
|
assert.ok(!exists(".saved"), "it hasn't been saved yet");
|
||||||
|
await click(".save-changes");
|
||||||
|
assert.ok(exists(".saved"), "it displays the saved message");
|
||||||
|
find(".saved").remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
|
||||||
|
// Live changes without reload
|
||||||
|
await selectKit(".text-size .combobox").expand();
|
||||||
|
await selectKit(".text-size .combobox").selectRowByValue("larger");
|
||||||
|
assert.ok(document.documentElement.classList.contains("text-size-larger"));
|
||||||
|
|
||||||
|
await selectKit(".text-size .combobox").expand();
|
||||||
|
await selectKit(".text-size .combobox").selectRowByValue("largest");
|
||||||
|
assert.ok(document.documentElement.classList.contains("text-size-largest"));
|
||||||
|
|
||||||
|
assert.equal(cookie("text_size"), null, "cookie is not set");
|
||||||
|
|
||||||
|
// Click save (by default this sets for all browsers, no cookie)
|
||||||
|
await savePreferences();
|
||||||
|
|
||||||
|
assert.equal(cookie("text_size"), null, "cookie is not set");
|
||||||
|
|
||||||
|
await selectKit(".text-size .combobox").expand();
|
||||||
|
await selectKit(".text-size .combobox").selectRowByValue("larger");
|
||||||
|
await click(".text-size input[type=checkbox]");
|
||||||
|
|
||||||
|
await savePreferences();
|
||||||
|
|
||||||
|
assert.equal(cookie("text_size"), "larger|1", "cookie is set");
|
||||||
|
await click(".text-size input[type=checkbox]");
|
||||||
|
await selectKit(".text-size .combobox").expand();
|
||||||
|
await selectKit(".text-size .combobox").selectRowByValue("largest");
|
||||||
|
|
||||||
|
await savePreferences();
|
||||||
|
assert.equal(cookie("text_size"), null, "cookie is removed");
|
||||||
|
|
||||||
|
removeCookie("text_size");
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test(
|
||||||
|
"does not show option to disable dark mode by default",
|
||||||
|
async assert => {
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.equal($(".control-group.dark-mode").length, 0);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
QUnit.test("shows light/dark color scheme pickers", async assert => {
|
||||||
|
let site = Site.current();
|
||||||
|
site.set("user_color_schemes", [
|
||||||
|
{ id: 2, name: "Cool Breeze" },
|
||||||
|
{ id: 3, name: "Dark Night", is_dark: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.ok($(".light-color-scheme").length, "has regular dropdown");
|
||||||
|
assert.ok($(".dark-color-scheme").length, "has dark color scheme dropdown");
|
||||||
|
});
|
||||||
|
|
||||||
|
function interfacePretender(server, helper) {
|
||||||
|
server.get("/color-scheme-stylesheet/2.json", () => {
|
||||||
|
return helper.response({
|
||||||
|
success: "OK"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptance("User Preferences Color Schemes (with default dark scheme)", {
|
||||||
|
loggedIn: true,
|
||||||
|
settings: { default_dark_mode_color_scheme_id: 1 },
|
||||||
|
pretend: interfacePretender
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("show 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"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("no color scheme picker by default", async assert => {
|
||||||
|
let site = Site.current();
|
||||||
|
site.set("user_color_schemes", []);
|
||||||
|
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.equal($(".control-group.color-scheme").length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("light color scheme picker", async assert => {
|
||||||
|
let site = Site.current();
|
||||||
|
site.set("user_color_schemes", [{ id: 2, name: "Cool Breeze" }]);
|
||||||
|
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.ok($(".light-color-scheme").length, "has regular picker dropdown");
|
||||||
|
assert.equal(
|
||||||
|
$(".dark-color-scheme").length,
|
||||||
|
0,
|
||||||
|
"does not have a dark color scheme picker"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("light and dark color scheme pickers", async assert => {
|
||||||
|
let site = Site.current();
|
||||||
|
let session = Session.current();
|
||||||
|
session.userDarkSchemeId = 1; // same as default set in site settings
|
||||||
|
|
||||||
|
site.set("default_dark_color_scheme", { id: 1, name: "Dark" });
|
||||||
|
site.set("user_color_schemes", [
|
||||||
|
{ id: 2, name: "Cool Breeze" },
|
||||||
|
{ id: 3, name: "Dark Night", is_dark: true }
|
||||||
|
]);
|
||||||
|
|
||||||
|
const savePreferences = async () => {
|
||||||
|
assert.ok(!exists(".saved"), "it hasn't been saved yet");
|
||||||
|
await click(".save-changes");
|
||||||
|
assert.ok(exists(".saved"), "it displays the saved message");
|
||||||
|
find(".saved").remove();
|
||||||
|
};
|
||||||
|
|
||||||
|
await visit("/u/eviltrout/preferences/interface");
|
||||||
|
assert.ok($(".light-color-scheme").length, "has regular dropdown");
|
||||||
|
assert.ok($(".dark-color-scheme").length, "has dark color scheme dropdown");
|
||||||
|
assert.equal(
|
||||||
|
$(".dark-color-scheme .selected-name").data("value"),
|
||||||
|
session.userDarkSchemeId,
|
||||||
|
"sets site default as selected dark scheme"
|
||||||
|
);
|
||||||
|
assert.equal(
|
||||||
|
$(".control-group.dark-mode").length,
|
||||||
|
0,
|
||||||
|
"it does not show disable dark mode checkbox"
|
||||||
|
);
|
||||||
|
|
||||||
|
removeCookie("color_scheme_id");
|
||||||
|
removeCookie("dark_scheme_id");
|
||||||
|
|
||||||
|
await selectKit(".light-color-scheme .combobox").expand();
|
||||||
|
await selectKit(".light-color-scheme .combobox").selectRowByValue(2);
|
||||||
|
assert.equal(cookie("color_scheme_id"), null, "cookie is not set");
|
||||||
|
assert.ok(
|
||||||
|
exists(".color-scheme-checkbox input:checked"),
|
||||||
|
"defaults to storing values in user options"
|
||||||
|
);
|
||||||
|
|
||||||
|
await savePreferences();
|
||||||
|
assert.equal(cookie("color_scheme_id"), null, "cookie is unchanged");
|
||||||
|
|
||||||
|
// Switch to saving changes in cookies
|
||||||
|
await click(".color-scheme-checkbox input[type=checkbox]");
|
||||||
|
await savePreferences();
|
||||||
|
assert.equal(cookie("color_scheme_id"), 2, "cookie is set");
|
||||||
|
|
||||||
|
// dark scheme
|
||||||
|
await selectKit(".dark-color-scheme .combobox").expand();
|
||||||
|
assert.ok(
|
||||||
|
selectKit(".dark-color-scheme .combobox")
|
||||||
|
.rowByValue(1)
|
||||||
|
.exists(),
|
||||||
|
"default dark scheme is included"
|
||||||
|
);
|
||||||
|
|
||||||
|
await selectKit(".dark-color-scheme .combobox").selectRowByValue(-1);
|
||||||
|
assert.equal(
|
||||||
|
cookie("dark_scheme_id"),
|
||||||
|
null,
|
||||||
|
"cookie is not set before saving"
|
||||||
|
);
|
||||||
|
|
||||||
|
await savePreferences();
|
||||||
|
assert.equal(cookie("dark_scheme_id"), -1, "cookie is set");
|
||||||
|
|
||||||
|
await click("button.undo-preview");
|
||||||
|
assert.equal(
|
||||||
|
selectKit(".light-color-scheme .combobox")
|
||||||
|
.header()
|
||||||
|
.value(),
|
||||||
|
null,
|
||||||
|
"resets light scheme dropdown"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
selectKit(".dark-color-scheme .combobox")
|
||||||
|
.header()
|
||||||
|
.value(),
|
||||||
|
session.userDarkSchemeId,
|
||||||
|
"resets dark scheme dropdown"
|
||||||
|
);
|
||||||
|
});
|
Loading…
Reference in New Issue