forked from e621ng/e621ng
[Themes] Rework the theme config page (#752)
This commit is contained in:
parent
aa5a7fb6fb
commit
d69c34ee9a
@ -50,6 +50,7 @@ export { default as Shortcuts } from '../src/javascripts/shortcuts.js';
|
||||
export { default as Utility } from '../src/javascripts/utility.js';
|
||||
export { default as TagRelationships } from '../src/javascripts/tag_relationships.js';
|
||||
export { default as Takedown } from '../src/javascripts/takedowns.js';
|
||||
export { default as Theme } from "../src/javascripts/themes.js";
|
||||
export { default as Thumbnails } from '../src/javascripts/thumbnails.js';
|
||||
export { default as Uploader } from '../src/javascripts/uploader.js';
|
||||
export { default as VoteManager } from '../src/javascripts/vote_manager.js';
|
||||
|
@ -256,7 +256,7 @@ class E6Swipe extends ZingTouch.Swipe {
|
||||
}
|
||||
|
||||
Post.initialize_gestures = function () {
|
||||
if (!LStorage.Posts.Gestures) return;
|
||||
if (!LStorage.Theme.Gestures) return;
|
||||
if (!(("ontouchstart" in window) || (navigator.maxTouchPoints > 0)))
|
||||
return;
|
||||
// Need activeElement to make sure that this doesn't go off during input.
|
||||
|
42
app/javascript/src/javascripts/themes.js
Normal file
42
app/javascript/src/javascripts/themes.js
Normal file
@ -0,0 +1,42 @@
|
||||
import Page from "./utility/page";
|
||||
import LStorage from "./utility/storage";
|
||||
|
||||
const Theme = {};
|
||||
|
||||
Theme.Values = ["Main", "Extra", "Palette", "Navbar", "Gestures"];
|
||||
|
||||
for (const one of Theme.Values) {
|
||||
Object.defineProperty(Theme, one, {
|
||||
get () { return LStorage.Theme[one]; },
|
||||
set (value) {
|
||||
// No value checking, we die like men
|
||||
LStorage.Theme[one] = value;
|
||||
$("body").attr("data-th-" + one.toLowerCase(), value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Theme.initialize_selector = function () {
|
||||
if (!LStorage.isAvailable()) {
|
||||
// This is here purely because it was in the old code.
|
||||
// All browsers made after 2008 should support this.
|
||||
$("#no_save_warning").show();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const one of Theme.Values) {
|
||||
$("#theme_" + one.toLowerCase())
|
||||
.val(LStorage.Theme[one] + "")
|
||||
.on("change", (event) => {
|
||||
const data = event.target.value;
|
||||
Theme[one] = data;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
$(() => {
|
||||
if (Page.matches("static", "theme"))
|
||||
Theme.initialize_selector();
|
||||
});
|
||||
|
||||
export default Theme;
|
41
app/javascript/src/javascripts/utility/page.js
Normal file
41
app/javascript/src/javascripts/utility/page.js
Normal file
@ -0,0 +1,41 @@
|
||||
export default class Page {
|
||||
|
||||
static _controller;
|
||||
|
||||
static _action;
|
||||
|
||||
static _init () {
|
||||
const data = document.body.dataset;
|
||||
this._controller = data.controller;
|
||||
this._action = data.action;
|
||||
}
|
||||
|
||||
/** @returns {string} Controller for the current page */
|
||||
static get Controller () {
|
||||
if (!this._controller) this._init();
|
||||
return this._controller;
|
||||
}
|
||||
|
||||
/** @returns {string} Action for the current page */
|
||||
static get Action () {
|
||||
if (!this._action) this._init();
|
||||
return this._action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current page matches the provided parameters.
|
||||
* If both params are provided, checks against both of them.
|
||||
* If only the controller is provided, action is ignored.
|
||||
* @param {string} controller Controller to match against
|
||||
* @param {string} action Action to match against
|
||||
* @returns {boolean} True if the params match, false otherwise
|
||||
*/
|
||||
static matches (controller, action = "") {
|
||||
if (!this._controller) this._init();
|
||||
|
||||
if (action)
|
||||
return this._controller == controller && this._action == action;
|
||||
return this._controller == controller;
|
||||
}
|
||||
|
||||
}
|
@ -23,6 +23,14 @@ LStorage.putObject = function (name, value) {
|
||||
this.put(name, JSON.stringify(value));
|
||||
};
|
||||
|
||||
LStorage.isAvailable = function () {
|
||||
try {
|
||||
localStorage.setItem("test", "a");
|
||||
localStorage.removeItem("test");
|
||||
} catch { return false; }
|
||||
return true;
|
||||
};
|
||||
|
||||
// Content that does not belong anywhere else
|
||||
LStorage.Site = {
|
||||
/** @returns {number} Currently displayed Mascot ID, or 0 if none is selected */
|
||||
@ -34,14 +42,33 @@ LStorage.Site = {
|
||||
StorageUtils.bootstrapMany(LStorage.Site);
|
||||
|
||||
|
||||
// Site themes and other visual options
|
||||
// Note that these are HARD-CODED in theme_include.html.erb
|
||||
// Any changes here must be reflected there as well
|
||||
LStorage.Theme = {
|
||||
/** @returns {string} Main theme */
|
||||
Main: ["theme", "hexagon"],
|
||||
|
||||
/** @returns {string} Extra theme / seasonal decotrations */
|
||||
Extra: ["theme-extra", "hexagon"],
|
||||
|
||||
/** @returns {string} Colorblind-friendly palette (default / deut / trit) */
|
||||
Palette: ["theme-palette", "default"],
|
||||
|
||||
/** @returns {string} Position of the navbar on the post page (top / bottom / both / none) */
|
||||
Navbar: ["theme-nav", "top"],
|
||||
|
||||
/** @returns {boolean} True if the mobile gestures should be enabled */
|
||||
Gestures: ["emg", false],
|
||||
};
|
||||
StorageUtils.bootstrapMany(LStorage.Theme);
|
||||
|
||||
|
||||
// Values relevant to the posts pages
|
||||
LStorage.Posts = {
|
||||
/** @returns {string} Viewing mode on the search page */
|
||||
Mode: ["mode", "view"],
|
||||
|
||||
/** @returns {boolean} True if the mobile gestures should be enabled */
|
||||
Gestures: ["emg", false],
|
||||
|
||||
/** @returns {boolean} True if parent/child posts preview should be visible */
|
||||
ShowPostChildren: ["show-relationship-previews", false],
|
||||
|
||||
|
@ -73,6 +73,7 @@
|
||||
@import "specific/tags.scss";
|
||||
@import "specific/takedowns.scss";
|
||||
@import "specific/terms_of_service.scss";
|
||||
@import "specific/themes.scss";
|
||||
@import "specific/tickets.scss";
|
||||
@import "specific/uploads.scss";
|
||||
@import "specific/uploads_file.scss";
|
||||
|
19
app/javascript/src/styles/specific/themes.scss
Normal file
19
app/javascript/src/styles/specific/themes.scss
Normal file
@ -0,0 +1,19 @@
|
||||
.theme-form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.5em 1em;
|
||||
|
||||
@include window-larger-than(30rem) {
|
||||
grid-template-columns: min-content auto;
|
||||
max-width: 30rem;
|
||||
gap: 1em;
|
||||
|
||||
h3, br, p.theme-info { grid-column: 1 / -1; }
|
||||
label { text-align: right; }
|
||||
.hint { grid-column: 2; }
|
||||
}
|
||||
|
||||
h3, br, p.theme-info { text-align: center; }
|
||||
h3 { margin-top: 0.5em; }
|
||||
.hint { max-width: unset; }
|
||||
}
|
@ -10,7 +10,6 @@
|
||||
b.setAttribute('data-th-extra', extra);
|
||||
b.setAttribute('data-th-palette', palette);
|
||||
b.setAttribute('data-th-nav', nav);
|
||||
} catch(e) {
|
||||
}
|
||||
} catch(e) {}
|
||||
})();
|
||||
<% end -%>
|
||||
|
@ -1,24 +1,30 @@
|
||||
<p>Theme settings are saved using cookies and javascript. This means that they will not persist across private browsing
|
||||
sessions, or work inside incognito mode on mobile devices.</p>
|
||||
|
||||
<h3 class="text-error" id="no_save_warning" style="display:none;">Your device does not allow the site to save theme
|
||||
settings.</h3>
|
||||
|
||||
<h3 class="text-error" id="no_save_warning" style="display:none;">
|
||||
Your device does not allow the site to save theme settings.
|
||||
</h3>
|
||||
<noscript>
|
||||
<h3 class="text-error" id="no_save_warning">Your device does not allow the site to save theme settings.</h3>
|
||||
</noscript>
|
||||
|
||||
<div class="simple_form">
|
||||
<div class="input">
|
||||
<label for="theme_name">Theme</label>
|
||||
<select id="theme_name">
|
||||
<div class="simple_form theme-form">
|
||||
|
||||
<h3>Theme Preferences</h3>
|
||||
|
||||
<p class="theme-info">
|
||||
Theme settings are saved locally in your browser.
|
||||
This means that they will not persist across multiple devices, or in incognito mode.
|
||||
</p>
|
||||
|
||||
<label for="theme_main">Theme</label>
|
||||
<select id="theme_main">
|
||||
<option value="hexagon">Hexagon</option>
|
||||
<option value="bloodlust">Bloodlust</option>
|
||||
<option value="serpent">Serpent</option>
|
||||
<option value="pony">Pony</option>
|
||||
<option value="serpent">Serpent</option>
|
||||
<option value="hotdog">Hotdog</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input">
|
||||
|
||||
<label for="theme_extra">Embellishments</label>
|
||||
<select id="theme_extra">
|
||||
<option value="none">(None)</option>
|
||||
@ -30,82 +36,33 @@
|
||||
<option value="stars">Stars</option>
|
||||
<option value="winter">Eternal Winter</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input">
|
||||
|
||||
<h3>Accessibility</h3>
|
||||
<label for="theme_palette">Palette</label>
|
||||
<select id="theme_palette">
|
||||
<option value="default">Default</option>
|
||||
<option value="deut">Protanopia & Deuteranopia</option>
|
||||
<option value="trit">Tritanopia</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input">
|
||||
<label for="theme_navbar">Navigation bar location</label>
|
||||
|
||||
<h3>Navigation</h3>
|
||||
<label for="theme_navbar">Navbar location</label>
|
||||
<select id="theme_navbar">
|
||||
<option value="top">Top (default)</option>
|
||||
<option value="bottom">Bottom</option>
|
||||
<option value="both">Both</option>
|
||||
<option value="none">Off (not recommended)</option>
|
||||
</select>
|
||||
</div>
|
||||
<hr>
|
||||
<div class="input">
|
||||
<label for="gestures_enable">Enable Mobile Gestures</label>
|
||||
<select id="gestures_enable">
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
|
||||
<label for="theme_gestures">Mobile Gestures</label>
|
||||
<select id="theme_gestures">
|
||||
<option value="false">Disabled</option>
|
||||
<option value="true">Enabled</option>
|
||||
</select>
|
||||
<div class="hint">Swipe left for next page/image. Swipe right for previous page/image.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<%= javascript_tag nonce: true do -%>
|
||||
$(function() {
|
||||
var $body = $(document.body);
|
||||
try {
|
||||
var ls = window.localStorage;
|
||||
ls.setItem('test', 'a');
|
||||
ls.removeItem('test');
|
||||
var theme = ls.getItem('theme') || 'hexagon';
|
||||
var extra = ls.getItem('theme-extra') || 'hexagon';
|
||||
var palette = ls.getItem('theme-palette') || 'default';
|
||||
var nav = ls.getItem('theme-nav') || 'top';
|
||||
var emg = ls.getItem('emg') || 'false';
|
||||
$('#theme_name').val(theme);
|
||||
$('#theme_extra').val(extra);
|
||||
$('#theme_palette').val(palette);
|
||||
$('#theme_navbar').val(nav);
|
||||
$('#gestures_enable').val(emg);
|
||||
} catch(e) {
|
||||
$("#no_save_warning").show();
|
||||
}
|
||||
$("#theme_name").on('change', function(e) {
|
||||
var theme = e.target.value;
|
||||
$body.attr('data-th-main', theme);
|
||||
window.localStorage.setItem('theme', theme);
|
||||
});
|
||||
$("#theme_extra").on('change', function(e) {
|
||||
var extra = e.target.value;
|
||||
$body.attr('data-th-extra', extra);
|
||||
window.localStorage.setItem('theme-extra', extra);
|
||||
});
|
||||
$("#theme_palette").on('change', function(e) {
|
||||
var palette = e.target.value;
|
||||
$body.attr('data-th-palette', palette);
|
||||
window.localStorage.setItem('theme-palette', palette);
|
||||
});
|
||||
$("#theme_navbar").on('change', function(e) {
|
||||
var pos = e.target.value;
|
||||
$body.attr('data-th-nav', pos);
|
||||
window.localStorage.setItem('theme-nav', pos);
|
||||
});
|
||||
$("#gestures_enable").on('change', function(e) {
|
||||
var emg = e.target.value;
|
||||
window.localStorage.setItem('emg', emg);
|
||||
});
|
||||
});
|
||||
<% end -%>
|
||||
|
||||
<% content_for(:page_title) do %>
|
||||
Themes
|
||||
<% end %>
|
||||
|
Loading…
Reference in New Issue
Block a user