Add prefers-color-scheme support (#601)
* Add prefers-color-scheme support This should fix <https://github.com/omarroth/invidious/issues/559>. The cookie storage format has been changed from boolean ("true"/"false") to tri-state ("dark"/"light"/""), so that users without a cookie set will get dark mode if they have enabled the dark theme in their operating system. The code for handling the cookie state, along with the user's operating system theme, has been factored out into a new function `update_mode`, which is called both at window load and at the "storage" event listener, because the "storage" event listener is only trigerred when a change is made to the localStorage from another tab/window (for more info - see <https://stackoverflow.com/a/4679754>).
This commit is contained in:
parent
19eceb4ecc
commit
f54fbd057e
|
@ -1,8 +1,8 @@
|
||||||
var toggle_theme = document.getElementById('toggle_theme')
|
var toggle_theme = document.getElementById('toggle_theme');
|
||||||
toggle_theme.href = 'javascript:void(0);';
|
toggle_theme.href = 'javascript:void(0);';
|
||||||
|
|
||||||
toggle_theme.addEventListener('click', function () {
|
toggle_theme.addEventListener('click', function () {
|
||||||
var dark_mode = document.getElementById('dark_theme').media == 'none';
|
var dark_mode = document.getElementById('dark_theme').media === 'none';
|
||||||
|
|
||||||
var url = '/toggle_theme?redirect=false';
|
var url = '/toggle_theme?redirect=false';
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
|
@ -11,19 +11,24 @@ toggle_theme.addEventListener('click', function () {
|
||||||
xhr.open('GET', url, true);
|
xhr.open('GET', url, true);
|
||||||
|
|
||||||
set_mode(dark_mode);
|
set_mode(dark_mode);
|
||||||
localStorage.setItem('dark_mode', dark_mode);
|
window.localStorage.setItem('dark_mode', dark_mode ? 'dark' : 'light');
|
||||||
|
|
||||||
xhr.send();
|
xhr.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('storage', function (e) {
|
window.addEventListener('storage', function (e) {
|
||||||
if (e.key == 'dark_mode') {
|
if (e.key === 'dark_mode') {
|
||||||
var dark_mode = e.newValue === 'true';
|
update_mode(e.newValue);
|
||||||
set_mode(dark_mode);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function set_mode(bool) {
|
window.addEventListener('load', function () {
|
||||||
|
window.localStorage.setItem('dark_mode', document.getElementById('dark_mode_pref').textContent);
|
||||||
|
// Update localStorage if dark mode preference changed on preferences page
|
||||||
|
update_mode(window.localStorage.dark_mode);
|
||||||
|
});
|
||||||
|
|
||||||
|
function set_mode (bool) {
|
||||||
document.getElementById('dark_theme').media = !bool ? 'none' : '';
|
document.getElementById('dark_theme').media = !bool ? 'none' : '';
|
||||||
document.getElementById('light_theme').media = bool ? 'none' : '';
|
document.getElementById('light_theme').media = bool ? 'none' : '';
|
||||||
|
|
||||||
|
@ -33,3 +38,21 @@ function set_mode(bool) {
|
||||||
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-moon');
|
toggle_theme.children[0].setAttribute('class', 'icon ion-ios-moon');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_mode (mode) {
|
||||||
|
if (mode === 'true' /* for backwards compatibility */ || mode === 'dark') {
|
||||||
|
// If preference for dark mode indicated
|
||||||
|
set_mode(true);
|
||||||
|
}
|
||||||
|
else if (mode === 'false' /* for backwards compaibility */ || mode === 'light') {
|
||||||
|
// If preference for light mode indicated
|
||||||
|
set_mode(false);
|
||||||
|
}
|
||||||
|
else if (document.getElementById('dark_mode_pref').textContent === '' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
||||||
|
// If no preference indicated here and no preference indicated on the preferences page (backend), but the browser tells us that the operating system has a dark theme
|
||||||
|
set_mode(true);
|
||||||
|
}
|
||||||
|
// else do nothing, falling back to the mode defined by the `dark_mode` preference on the preferences page (backend)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "عرض مقاطع الفيديو ذات الصلة؟",
|
"Show related videos: ": "عرض مقاطع الفيديو ذات الصلة؟",
|
||||||
"Show annotations by default: ": "عرض الملاحظات فى الفيديو تلقائيا ؟",
|
"Show annotations by default: ": "عرض الملاحظات فى الفيديو تلقائيا ؟",
|
||||||
"Visual preferences": "التفضيلات المرئية",
|
"Visual preferences": "التفضيلات المرئية",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "الوضع الليلى: ",
|
"Dark mode: ": "الوضع الليلى: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "الوضع الخفيف: ",
|
"Thin mode: ": "الوضع الخفيف: ",
|
||||||
"Subscription preferences": "تفضيلات الإشتراك",
|
"Subscription preferences": "تفضيلات الإشتراك",
|
||||||
"Show annotations by default for subscribed channels: ": "عرض الملاحظات فى الفيديوهات تلقائيا فى القنوات المشترك بها فقط ؟",
|
"Show annotations by default for subscribed channels: ": "عرض الملاحظات فى الفيديوهات تلقائيا فى القنوات المشترك بها فقط ؟",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Ähnliche Videos anzeigen? ",
|
"Show related videos: ": "Ähnliche Videos anzeigen? ",
|
||||||
"Show annotations by default: ": "Standardmäßig Anmerkungen anzeigen? ",
|
"Show annotations by default: ": "Standardmäßig Anmerkungen anzeigen? ",
|
||||||
"Visual preferences": "Anzeigeeinstellungen",
|
"Visual preferences": "Anzeigeeinstellungen",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Nachtmodus: ",
|
"Dark mode: ": "Nachtmodus: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Schlanker Modus: ",
|
"Thin mode: ": "Schlanker Modus: ",
|
||||||
"Subscription preferences": "Abonnementeinstellungen",
|
"Subscription preferences": "Abonnementeinstellungen",
|
||||||
"Show annotations by default for subscribed channels: ": "Anmerkungen für abonnierte Kanäle standardmäßig anzeigen? ",
|
"Show annotations by default for subscribed channels: ": "Anmerkungen für abonnierte Kanäle standardmäßig anzeigen? ",
|
||||||
|
|
|
@ -74,7 +74,11 @@
|
||||||
"Show related videos: ": "Προβολή σχετικών βίντεο; ",
|
"Show related videos: ": "Προβολή σχετικών βίντεο; ",
|
||||||
"Show annotations by default: ": "Αυτόματη προβολή σημειώσεων; :",
|
"Show annotations by default: ": "Αυτόματη προβολή σημειώσεων; :",
|
||||||
"Visual preferences": "Προτιμήσεις εμφάνισης",
|
"Visual preferences": "Προτιμήσεις εμφάνισης",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Σκοτεινή λειτουργία: ",
|
"Dark mode: ": "Σκοτεινή λειτουργία: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Ελαφριά λειτουργία: ",
|
"Thin mode: ": "Ελαφριά λειτουργία: ",
|
||||||
"Subscription preferences": "Προτιμήσεις συνδρομών",
|
"Subscription preferences": "Προτιμήσεις συνδρομών",
|
||||||
"Show annotations by default for subscribed channels: ": "Προβολή σημειώσεων μόνο για κανάλια στα οποία είστε συνδρομητής; ",
|
"Show annotations by default for subscribed channels: ": "Προβολή σημειώσεων μόνο για κανάλια στα οποία είστε συνδρομητής; ",
|
||||||
|
|
|
@ -74,7 +74,11 @@
|
||||||
"Show related videos: ": "Show related videos: ",
|
"Show related videos: ": "Show related videos: ",
|
||||||
"Show annotations by default: ": "Show annotations by default: ",
|
"Show annotations by default: ": "Show annotations by default: ",
|
||||||
"Visual preferences": "Visual preferences",
|
"Visual preferences": "Visual preferences",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Dark mode: ",
|
"Dark mode: ": "Dark mode: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Thin mode: ",
|
"Thin mode: ": "Thin mode: ",
|
||||||
"Subscription preferences": "Subscription preferences",
|
"Subscription preferences": "Subscription preferences",
|
||||||
"Show annotations by default for subscribed channels: ": "Show annotations by default for subscribed channels? ",
|
"Show annotations by default for subscribed channels: ": "Show annotations by default for subscribed channels? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Ĉu montri rilatajn videojn? ",
|
"Show related videos: ": "Ĉu montri rilatajn videojn? ",
|
||||||
"Show annotations by default: ": "Ĉu montri prinotojn defaŭlte? ",
|
"Show annotations by default: ": "Ĉu montri prinotojn defaŭlte? ",
|
||||||
"Visual preferences": "Vidaj preferoj",
|
"Visual preferences": "Vidaj preferoj",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Malhela reĝimo: ",
|
"Dark mode: ": "Malhela reĝimo: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Maldika reĝimo: ",
|
"Thin mode: ": "Maldika reĝimo: ",
|
||||||
"Subscription preferences": "Abonaj agordoj",
|
"Subscription preferences": "Abonaj agordoj",
|
||||||
"Show annotations by default for subscribed channels: ": "Ĉu montri prinotojn defaŭlte por abonitaj kanaloj? ",
|
"Show annotations by default for subscribed channels: ": "Ĉu montri prinotojn defaŭlte por abonitaj kanaloj? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "¿Mostrar vídeos relacionados? ",
|
"Show related videos: ": "¿Mostrar vídeos relacionados? ",
|
||||||
"Show annotations by default: ": "¿Mostrar anotaciones por defecto? ",
|
"Show annotations by default: ": "¿Mostrar anotaciones por defecto? ",
|
||||||
"Visual preferences": "Preferencias visuales",
|
"Visual preferences": "Preferencias visuales",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Modo oscuro: ",
|
"Dark mode: ": "Modo oscuro: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Modo compacto: ",
|
"Thin mode: ": "Modo compacto: ",
|
||||||
"Subscription preferences": "Preferencias de la suscripción",
|
"Subscription preferences": "Preferencias de la suscripción",
|
||||||
"Show annotations by default for subscribed channels: ": "¿Mostrar anotaciones por defecto para los canales suscritos? ",
|
"Show annotations by default for subscribed channels: ": "¿Mostrar anotaciones por defecto para los canales suscritos? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "",
|
"Show related videos: ": "",
|
||||||
"Show annotations by default: ": "",
|
"Show annotations by default: ": "",
|
||||||
"Visual preferences": "",
|
"Visual preferences": "",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "",
|
"Dark mode: ": "",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "",
|
"Thin mode: ": "",
|
||||||
"Subscription preferences": "",
|
"Subscription preferences": "",
|
||||||
"Show annotations by default for subscribed channels: ": "",
|
"Show annotations by default for subscribed channels: ": "",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Voir les vidéos liées : ",
|
"Show related videos: ": "Voir les vidéos liées : ",
|
||||||
"Show annotations by default: ": "Voir les annotations par défaut : ",
|
"Show annotations by default: ": "Voir les annotations par défaut : ",
|
||||||
"Visual preferences": "Préférences du site",
|
"Visual preferences": "Préférences du site",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Mode Sombre : ",
|
"Dark mode: ": "Mode Sombre : ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Mode Simplifié : ",
|
"Thin mode: ": "Mode Simplifié : ",
|
||||||
"Subscription preferences": "Préférences de la page d'abonnements",
|
"Subscription preferences": "Préférences de la page d'abonnements",
|
||||||
"Show annotations by default for subscribed channels: ": "Voir les annotations par défaut sur les chaînes suivies : ",
|
"Show annotations by default for subscribed channels: ": "Voir les annotations par défaut sur les chaînes suivies : ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Sýna tengd myndbönd? ",
|
"Show related videos: ": "Sýna tengd myndbönd? ",
|
||||||
"Show annotations by default: ": "Á að sýna glósur sjálfgefið? ",
|
"Show annotations by default: ": "Á að sýna glósur sjálfgefið? ",
|
||||||
"Visual preferences": "Sjónrænar stillingar",
|
"Visual preferences": "Sjónrænar stillingar",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Myrkur ham: ",
|
"Dark mode: ": "Myrkur ham: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Þunnt ham: ",
|
"Thin mode: ": "Þunnt ham: ",
|
||||||
"Subscription preferences": "Áskriftarstillingar",
|
"Subscription preferences": "Áskriftarstillingar",
|
||||||
"Show annotations by default for subscribed channels: ": "Á að sýna glósur sjálfgefið fyrir áskriftarrásir? ",
|
"Show annotations by default for subscribed channels: ": "Á að sýna glósur sjálfgefið fyrir áskriftarrásir? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Mostra video correlati? ",
|
"Show related videos: ": "Mostra video correlati? ",
|
||||||
"Show annotations by default: ": "Mostra le annotazioni per impostazione predefinita? ",
|
"Show annotations by default: ": "Mostra le annotazioni per impostazione predefinita? ",
|
||||||
"Visual preferences": "Preferenze grafiche",
|
"Visual preferences": "Preferenze grafiche",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Tema scuro: ",
|
"Dark mode: ": "Tema scuro: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Modalità per connessioni lente: ",
|
"Thin mode: ": "Modalità per connessioni lente: ",
|
||||||
"Subscription preferences": "Preferenze iscrizioni",
|
"Subscription preferences": "Preferenze iscrizioni",
|
||||||
"Show annotations by default for subscribed channels: ": "",
|
"Show annotations by default for subscribed channels: ": "",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Vis relaterte videoer? ",
|
"Show related videos: ": "Vis relaterte videoer? ",
|
||||||
"Show annotations by default: ": "Vis merknader som forvalg? ",
|
"Show annotations by default: ": "Vis merknader som forvalg? ",
|
||||||
"Visual preferences": "Visuelle innstillinger",
|
"Visual preferences": "Visuelle innstillinger",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Mørk drakt: ",
|
"Dark mode: ": "Mørk drakt: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Tynt modus: ",
|
"Thin mode: ": "Tynt modus: ",
|
||||||
"Subscription preferences": "Abonnementsinnstillinger",
|
"Subscription preferences": "Abonnementsinnstillinger",
|
||||||
"Show annotations by default for subscribed channels: ": "Vis merknader som forvalg for kanaler det abonneres på? ",
|
"Show annotations by default for subscribed channels: ": "Vis merknader som forvalg for kanaler det abonneres på? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Gerelateerde video's tonen? ",
|
"Show related videos: ": "Gerelateerde video's tonen? ",
|
||||||
"Show annotations by default: ": "Standaard annotaties tonen? ",
|
"Show annotations by default: ": "Standaard annotaties tonen? ",
|
||||||
"Visual preferences": "Visuele instellingen",
|
"Visual preferences": "Visuele instellingen",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Donkere modus: ",
|
"Dark mode: ": "Donkere modus: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Smalle modus: ",
|
"Thin mode: ": "Smalle modus: ",
|
||||||
"Subscription preferences": "Abonnementsinstellingen",
|
"Subscription preferences": "Abonnementsinstellingen",
|
||||||
"Show annotations by default for subscribed channels: ": "Standaard annotaties tonen voor geabonneerde kanalen? ",
|
"Show annotations by default for subscribed channels: ": "Standaard annotaties tonen voor geabonneerde kanalen? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Pokaż powiązane filmy? ",
|
"Show related videos: ": "Pokaż powiązane filmy? ",
|
||||||
"Show annotations by default: ": "",
|
"Show annotations by default: ": "",
|
||||||
"Visual preferences": "Preferencje Wizualne",
|
"Visual preferences": "Preferencje Wizualne",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Ciemny motyw: ",
|
"Dark mode: ": "Ciemny motyw: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Tryb minimalny: ",
|
"Thin mode: ": "Tryb minimalny: ",
|
||||||
"Subscription preferences": "Preferencje subskrybcji",
|
"Subscription preferences": "Preferencje subskrybcji",
|
||||||
"Show annotations by default for subscribed channels: ": "",
|
"Show annotations by default for subscribed channels: ": "",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Показывать похожие видео? ",
|
"Show related videos: ": "Показывать похожие видео? ",
|
||||||
"Show annotations by default: ": "Всегда показывать аннотации? ",
|
"Show annotations by default: ": "Всегда показывать аннотации? ",
|
||||||
"Visual preferences": "Настройки сайта",
|
"Visual preferences": "Настройки сайта",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Тёмное оформление: ",
|
"Dark mode: ": "Тёмное оформление: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Облегчённое оформление: ",
|
"Thin mode: ": "Облегчённое оформление: ",
|
||||||
"Subscription preferences": "Настройки подписок",
|
"Subscription preferences": "Настройки подписок",
|
||||||
"Show annotations by default for subscribed channels: ": "Всегда показывать аннотации в видео каналов, на которые вы подписаны? ",
|
"Show annotations by default for subscribed channels: ": "Всегда показывать аннотации в видео каналов, на которые вы подписаны? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "Показувати схожі відео? ",
|
"Show related videos: ": "Показувати схожі відео? ",
|
||||||
"Show annotations by default: ": "Завжди показувати анотації? ",
|
"Show annotations by default: ": "Завжди показувати анотації? ",
|
||||||
"Visual preferences": "Налаштування сайту",
|
"Visual preferences": "Налаштування сайту",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "Темне оформлення: ",
|
"Dark mode: ": "Темне оформлення: ",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "Полегшене оформлення: ",
|
"Thin mode: ": "Полегшене оформлення: ",
|
||||||
"Subscription preferences": "Налаштування підписок",
|
"Subscription preferences": "Налаштування підписок",
|
||||||
"Show annotations by default for subscribed channels: ": "Завжди показувати анотації у відео каналів, на які ви підписані? ",
|
"Show annotations by default for subscribed channels: ": "Завжди показувати анотації у відео каналів, на які ви підписані? ",
|
||||||
|
|
|
@ -68,7 +68,11 @@
|
||||||
"Show related videos: ": "显示相关视频?",
|
"Show related videos: ": "显示相关视频?",
|
||||||
"Show annotations by default: ": "默认显示视频注释?",
|
"Show annotations by default: ": "默认显示视频注释?",
|
||||||
"Visual preferences": "视觉选项",
|
"Visual preferences": "视觉选项",
|
||||||
|
"Player style: ": "",
|
||||||
"Dark mode: ": "暗色模式:",
|
"Dark mode: ": "暗色模式:",
|
||||||
|
"Theme: ": "",
|
||||||
|
"dark": "",
|
||||||
|
"light": "",
|
||||||
"Thin mode: ": "窄页模式:",
|
"Thin mode: ": "窄页模式:",
|
||||||
"Subscription preferences": "订阅设置",
|
"Subscription preferences": "订阅设置",
|
||||||
"Show annotations by default for subscribed channels: ": "在订阅频道的视频默认显示注释?",
|
"Show annotations by default for subscribed channels: ": "在订阅频道的视频默认显示注释?",
|
||||||
|
|
|
@ -267,8 +267,7 @@ before_all do |env|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dark_mode = env.params.query["dark_mode"]? || preferences.dark_mode.to_s
|
dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
|
||||||
dark_mode = dark_mode == "true"
|
|
||||||
|
|
||||||
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
|
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
|
||||||
thin_mode = thin_mode == "true"
|
thin_mode = thin_mode == "true"
|
||||||
|
@ -1528,8 +1527,7 @@ post "/preferences" do |env|
|
||||||
locale ||= CONFIG.default_user_preferences.locale
|
locale ||= CONFIG.default_user_preferences.locale
|
||||||
|
|
||||||
dark_mode = env.params.body["dark_mode"]?.try &.as(String)
|
dark_mode = env.params.body["dark_mode"]?.try &.as(String)
|
||||||
dark_mode ||= "off"
|
dark_mode ||= CONFIG.default_user_preferences.dark_mode
|
||||||
dark_mode = dark_mode == "on"
|
|
||||||
|
|
||||||
thin_mode = env.params.body["thin_mode"]?.try &.as(String)
|
thin_mode = env.params.body["thin_mode"]?.try &.as(String)
|
||||||
thin_mode ||= "off"
|
thin_mode ||= "off"
|
||||||
|
@ -1553,6 +1551,7 @@ post "/preferences" do |env|
|
||||||
notifications_only ||= "off"
|
notifications_only ||= "off"
|
||||||
notifications_only = notifications_only == "on"
|
notifications_only = notifications_only == "on"
|
||||||
|
|
||||||
|
# Convert to JSON and back again to take advantage of converters used for compatability
|
||||||
preferences = Preferences.from_json({
|
preferences = Preferences.from_json({
|
||||||
annotations: annotations,
|
annotations: annotations,
|
||||||
annotations_subscribed: annotations_subscribed,
|
annotations_subscribed: annotations_subscribed,
|
||||||
|
@ -1648,12 +1647,27 @@ get "/toggle_theme" do |env|
|
||||||
if user = env.get? "user"
|
if user = env.get? "user"
|
||||||
user = user.as(User)
|
user = user.as(User)
|
||||||
preferences = user.preferences
|
preferences = user.preferences
|
||||||
preferences.dark_mode = !preferences.dark_mode
|
|
||||||
|
|
||||||
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences.to_json, user.email)
|
case preferences.dark_mode
|
||||||
|
when "dark"
|
||||||
|
preferences.dark_mode = "light"
|
||||||
|
else
|
||||||
|
preferences.dark_mode = "dark"
|
||||||
|
end
|
||||||
|
|
||||||
|
preferences = preferences.to_json
|
||||||
|
|
||||||
|
PG_DB.exec("UPDATE users SET preferences = $1 WHERE email = $2", preferences, user.email)
|
||||||
else
|
else
|
||||||
preferences = env.get("preferences").as(Preferences)
|
preferences = env.get("preferences").as(Preferences)
|
||||||
preferences.dark_mode = !preferences.dark_mode
|
|
||||||
|
case preferences.dark_mode
|
||||||
|
when "dark"
|
||||||
|
preferences.dark_mode = "light"
|
||||||
|
else
|
||||||
|
preferences.dark_mode = "dark"
|
||||||
|
end
|
||||||
|
|
||||||
preferences = preferences.to_json
|
preferences = preferences.to_json
|
||||||
|
|
||||||
if Kemal.config.ssl || config.https_only
|
if Kemal.config.ssl || config.https_only
|
||||||
|
@ -2026,7 +2040,7 @@ post "/data_control" do |env|
|
||||||
env.response.puts %(<meta http-equiv="refresh" content="0; url=#{referer}">)
|
env.response.puts %(<meta http-equiv="refresh" content="0; url=#{referer}">)
|
||||||
env.response.puts %(<link rel="stylesheet" href="/css/ionicons.min.css?v=#{ASSET_COMMIT}">)
|
env.response.puts %(<link rel="stylesheet" href="/css/ionicons.min.css?v=#{ASSET_COMMIT}">)
|
||||||
env.response.puts %(<link rel="stylesheet" href="/css/default.css?v=#{ASSET_COMMIT}">)
|
env.response.puts %(<link rel="stylesheet" href="/css/default.css?v=#{ASSET_COMMIT}">)
|
||||||
if env.get("preferences").as(Preferences).dark_mode
|
if env.get("preferences").as(Preferences).dark_mode == "dark"
|
||||||
env.response.puts %(<link rel="stylesheet" href="/css/darktheme.css?v=#{ASSET_COMMIT}">)
|
env.response.puts %(<link rel="stylesheet" href="/css/darktheme.css?v=#{ASSET_COMMIT}">)
|
||||||
else
|
else
|
||||||
env.response.puts %(<link rel="stylesheet" href="/css/lighttheme.css?v=#{ASSET_COMMIT}">)
|
env.response.puts %(<link rel="stylesheet" href="/css/lighttheme.css?v=#{ASSET_COMMIT}">)
|
||||||
|
|
|
@ -24,6 +24,27 @@ end
|
||||||
|
|
||||||
struct ConfigPreferences
|
struct ConfigPreferences
|
||||||
module StringToArray
|
module StringToArray
|
||||||
|
def self.to_json(value : Array(String), json : JSON::Builder)
|
||||||
|
json.array do
|
||||||
|
value.each do |element|
|
||||||
|
json.string element
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_json(value : JSON::PullParser) : Array(String)
|
||||||
|
begin
|
||||||
|
result = [] of String
|
||||||
|
value.read_array do
|
||||||
|
result << HTML.escape(value.read_string[0, 100])
|
||||||
|
end
|
||||||
|
rescue ex
|
||||||
|
result = [HTML.escape(value.read_string[0, 100]), ""]
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
def self.to_yaml(value : Array(String), yaml : YAML::Nodes::Builder)
|
def self.to_yaml(value : Array(String), yaml : YAML::Nodes::Builder)
|
||||||
yaml.sequence do
|
yaml.sequence do
|
||||||
value.each do |element|
|
value.each do |element|
|
||||||
|
@ -44,11 +65,11 @@ struct ConfigPreferences
|
||||||
node.raise "Expected scalar, not #{item.class}"
|
node.raise "Expected scalar, not #{item.class}"
|
||||||
end
|
end
|
||||||
|
|
||||||
result << item.value
|
result << HTML.escape(item.value[0, 100])
|
||||||
end
|
end
|
||||||
rescue ex
|
rescue ex
|
||||||
if node.is_a?(YAML::Nodes::Scalar)
|
if node.is_a?(YAML::Nodes::Scalar)
|
||||||
result = [node.value, ""]
|
result = [HTML.escape(node.value[0, 100]), ""]
|
||||||
else
|
else
|
||||||
result = ["", ""]
|
result = ["", ""]
|
||||||
end
|
end
|
||||||
|
@ -58,6 +79,53 @@ struct ConfigPreferences
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
module BoolToString
|
||||||
|
def self.to_json(value : String, json : JSON::Builder)
|
||||||
|
json.string value
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_json(value : JSON::PullParser) : String
|
||||||
|
begin
|
||||||
|
result = value.read_string
|
||||||
|
|
||||||
|
if result.empty?
|
||||||
|
CONFIG.default_user_preferences.dark_mode
|
||||||
|
else
|
||||||
|
result
|
||||||
|
end
|
||||||
|
rescue ex
|
||||||
|
result = value.read_bool
|
||||||
|
|
||||||
|
if result
|
||||||
|
"dark"
|
||||||
|
else
|
||||||
|
"light"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.to_yaml(value : String, yaml : YAML::Nodes::Builder)
|
||||||
|
yaml.scalar value
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : String
|
||||||
|
unless node.is_a?(YAML::Nodes::Scalar)
|
||||||
|
node.raise "Expected sequence, not #{node.class}"
|
||||||
|
end
|
||||||
|
|
||||||
|
case node.value
|
||||||
|
when "true"
|
||||||
|
"dark"
|
||||||
|
when "false"
|
||||||
|
"light"
|
||||||
|
when ""
|
||||||
|
CONFIG.default_user_preferences.dark_mode
|
||||||
|
else
|
||||||
|
node.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
yaml_mapping({
|
yaml_mapping({
|
||||||
annotations: {type: Bool, default: false},
|
annotations: {type: Bool, default: false},
|
||||||
annotations_subscribed: {type: Bool, default: false},
|
annotations_subscribed: {type: Bool, default: false},
|
||||||
|
@ -66,7 +134,7 @@ struct ConfigPreferences
|
||||||
comments: {type: Array(String), default: ["youtube", ""], converter: StringToArray},
|
comments: {type: Array(String), default: ["youtube", ""], converter: StringToArray},
|
||||||
continue: {type: Bool, default: false},
|
continue: {type: Bool, default: false},
|
||||||
continue_autoplay: {type: Bool, default: true},
|
continue_autoplay: {type: Bool, default: true},
|
||||||
dark_mode: {type: Bool, default: false},
|
dark_mode: {type: String, default: "", converter: BoolToString},
|
||||||
latest_only: {type: Bool, default: false},
|
latest_only: {type: Bool, default: false},
|
||||||
listen: {type: Bool, default: false},
|
listen: {type: Bool, default: false},
|
||||||
local: {type: Bool, default: false},
|
local: {type: Bool, default: false},
|
||||||
|
|
|
@ -4,7 +4,7 @@ def Object.from_json(string_or_io, default) : self
|
||||||
new parser, default
|
new parser, default
|
||||||
end
|
end
|
||||||
|
|
||||||
# Adds configurable 'default' to
|
# Adds configurable 'default'
|
||||||
macro patched_json_mapping(_properties_, strict = false)
|
macro patched_json_mapping(_properties_, strict = false)
|
||||||
{% for key, value in _properties_ %}
|
{% for key, value in _properties_ %}
|
||||||
{% _properties_[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %}
|
{% _properties_[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %}
|
||||||
|
|
|
@ -356,3 +356,16 @@ def parse_range(range)
|
||||||
|
|
||||||
return 0_i64, nil
|
return 0_i64, nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def convert_theme(theme)
|
||||||
|
case theme
|
||||||
|
when "true"
|
||||||
|
"dark"
|
||||||
|
when "false"
|
||||||
|
"light"
|
||||||
|
when "", nil
|
||||||
|
nil
|
||||||
|
else
|
||||||
|
theme
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -31,62 +31,6 @@ struct User
|
||||||
end
|
end
|
||||||
|
|
||||||
struct Preferences
|
struct Preferences
|
||||||
module StringToArray
|
|
||||||
def self.to_json(value : Array(String), json : JSON::Builder)
|
|
||||||
json.array do
|
|
||||||
value.each do |element|
|
|
||||||
json.string element
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_json(value : JSON::PullParser) : Array(String)
|
|
||||||
begin
|
|
||||||
result = [] of String
|
|
||||||
value.read_array do
|
|
||||||
result << HTML.escape(value.read_string[0, 100])
|
|
||||||
end
|
|
||||||
rescue ex
|
|
||||||
result = [HTML.escape(value.read_string[0, 100]), ""]
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.to_yaml(value : Array(String), yaml : YAML::Nodes::Builder)
|
|
||||||
yaml.sequence do
|
|
||||||
value.each do |element|
|
|
||||||
yaml.scalar element
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.from_yaml(ctx : YAML::ParseContext, node : YAML::Nodes::Node) : Array(String)
|
|
||||||
begin
|
|
||||||
unless node.is_a?(YAML::Nodes::Sequence)
|
|
||||||
node.raise "Expected sequence, not #{node.class}"
|
|
||||||
end
|
|
||||||
|
|
||||||
result = [] of String
|
|
||||||
node.nodes.each do |item|
|
|
||||||
unless item.is_a?(YAML::Nodes::Scalar)
|
|
||||||
node.raise "Expected scalar, not #{item.class}"
|
|
||||||
end
|
|
||||||
|
|
||||||
result << HTML.escape(item.value[0, 100])
|
|
||||||
end
|
|
||||||
rescue ex
|
|
||||||
if node.is_a?(YAML::Nodes::Scalar)
|
|
||||||
result = [HTML.escape(node.value[0, 100]), ""]
|
|
||||||
else
|
|
||||||
result = ["", ""]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
result
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
module ProcessString
|
module ProcessString
|
||||||
def self.to_json(value : String, json : JSON::Builder)
|
def self.to_json(value : String, json : JSON::Builder)
|
||||||
json.string value
|
json.string value
|
||||||
|
@ -127,11 +71,11 @@ struct Preferences
|
||||||
annotations: {type: Bool, default: CONFIG.default_user_preferences.annotations},
|
annotations: {type: Bool, default: CONFIG.default_user_preferences.annotations},
|
||||||
annotations_subscribed: {type: Bool, default: CONFIG.default_user_preferences.annotations_subscribed},
|
annotations_subscribed: {type: Bool, default: CONFIG.default_user_preferences.annotations_subscribed},
|
||||||
autoplay: {type: Bool, default: CONFIG.default_user_preferences.autoplay},
|
autoplay: {type: Bool, default: CONFIG.default_user_preferences.autoplay},
|
||||||
captions: {type: Array(String), default: CONFIG.default_user_preferences.captions, converter: StringToArray},
|
captions: {type: Array(String), default: CONFIG.default_user_preferences.captions, converter: ConfigPreferences::StringToArray},
|
||||||
comments: {type: Array(String), default: CONFIG.default_user_preferences.comments, converter: StringToArray},
|
comments: {type: Array(String), default: CONFIG.default_user_preferences.comments, converter: ConfigPreferences::StringToArray},
|
||||||
continue: {type: Bool, default: CONFIG.default_user_preferences.continue},
|
continue: {type: Bool, default: CONFIG.default_user_preferences.continue},
|
||||||
continue_autoplay: {type: Bool, default: CONFIG.default_user_preferences.continue_autoplay},
|
continue_autoplay: {type: Bool, default: CONFIG.default_user_preferences.continue_autoplay},
|
||||||
dark_mode: {type: Bool, default: CONFIG.default_user_preferences.dark_mode},
|
dark_mode: {type: String, default: CONFIG.default_user_preferences.dark_mode, converter: ConfigPreferences::BoolToString},
|
||||||
latest_only: {type: Bool, default: CONFIG.default_user_preferences.latest_only},
|
latest_only: {type: Bool, default: CONFIG.default_user_preferences.latest_only},
|
||||||
listen: {type: Bool, default: CONFIG.default_user_preferences.listen},
|
listen: {type: Bool, default: CONFIG.default_user_preferences.listen},
|
||||||
local: {type: Bool, default: CONFIG.default_user_preferences.local},
|
local: {type: Bool, default: CONFIG.default_user_preferences.local},
|
||||||
|
|
|
@ -122,8 +122,12 @@ function update_value(element) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
<label for="dark_mode"><%= translate(locale, "Dark mode: ") %></label>
|
<label for="dark_mode"><%= translate(locale, "Theme: ") %></label>
|
||||||
<input name="dark_mode" id="dark_mode" type="checkbox" <% if preferences.dark_mode %>checked<% end %>>
|
<select name="dark_mode" id="dark_mode">
|
||||||
|
<% {"", "light", "dark"}.each do |option| %>
|
||||||
|
<option value="<%= option %>" <% if preferences.dark_mode == option %> selected <% end %>><%= translate(locale, option) %></option>
|
||||||
|
<% end %>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pure-control-group">
|
<div class="pure-control-group">
|
||||||
|
|
|
@ -18,13 +18,14 @@
|
||||||
<link rel="stylesheet" href="/css/grids-responsive-min.css?v=<%= ASSET_COMMIT %>">
|
<link rel="stylesheet" href="/css/grids-responsive-min.css?v=<%= ASSET_COMMIT %>">
|
||||||
<link rel="stylesheet" href="/css/ionicons.min.css?v=<%= ASSET_COMMIT %>">
|
<link rel="stylesheet" href="/css/ionicons.min.css?v=<%= ASSET_COMMIT %>">
|
||||||
<link rel="stylesheet" href="/css/default.css?v=<%= ASSET_COMMIT %>">
|
<link rel="stylesheet" href="/css/default.css?v=<%= ASSET_COMMIT %>">
|
||||||
<link rel="stylesheet" href="/css/darktheme.css?v=<%= ASSET_COMMIT %>" id="dark_theme" <% if !env.get("preferences").as(Preferences).dark_mode %>media="none"<% end %>>
|
<link rel="stylesheet" href="/css/darktheme.css?v=<%= ASSET_COMMIT %>" id="dark_theme" <% if env.get("preferences").as(Preferences).dark_mode != "dark" %>media="none"<% end %>>
|
||||||
<link rel="stylesheet" href="/css/lighttheme.css?v=<%= ASSET_COMMIT %>" id="light_theme" <% if env.get("preferences").as(Preferences).dark_mode %>media="none"<% end %>>
|
<link rel="stylesheet" href="/css/lighttheme.css?v=<%= ASSET_COMMIT %>" id="light_theme" <% if env.get("preferences").as(Preferences).dark_mode == "dark" %>media="none"<% end %>>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
|
<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
<span style="display:none" id="dark_mode_pref"><%= env.get("preferences").as(Preferences).dark_mode %></span>
|
||||||
<div class="pure-g">
|
<div class="pure-g">
|
||||||
<div class="pure-u-1 pure-u-md-2-24"></div>
|
<div class="pure-u-1 pure-u-md-2-24"></div>
|
||||||
<div class="pure-u-1 pure-u-md-20-24">
|
<div class="pure-u-1 pure-u-md-20-24">
|
||||||
|
@ -43,7 +44,7 @@
|
||||||
<% if env.get? "user" %>
|
<% if env.get? "user" %>
|
||||||
<div class="pure-u-1-4">
|
<div class="pure-u-1-4">
|
||||||
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<% if env.get("preferences").as(Preferences).dark_mode %>
|
<% if env.get("preferences").as(Preferences).dark_mode == "dark" %>
|
||||||
<i class="icon ion-ios-sunny"></i>
|
<i class="icon ion-ios-sunny"></i>
|
||||||
<% else %>
|
<% else %>
|
||||||
<i class="icon ion-ios-moon"></i>
|
<i class="icon ion-ios-moon"></i>
|
||||||
|
@ -76,7 +77,7 @@
|
||||||
<% else %>
|
<% else %>
|
||||||
<div class="pure-u-1-3">
|
<div class="pure-u-1-3">
|
||||||
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
<a id="toggle_theme" href="/toggle_theme?referer=<%= env.get?("current_page") %>" class="pure-menu-heading">
|
||||||
<% if env.get("preferences").as(Preferences).dark_mode %>
|
<% if env.get("preferences").as(Preferences).dark_mode == "dark" %>
|
||||||
<i class="icon ion-ios-sunny"></i>
|
<i class="icon ion-ios-sunny"></i>
|
||||||
<% else %>
|
<% else %>
|
||||||
<i class="icon ion-ios-moon"></i>
|
<i class="icon ion-ios-moon"></i>
|
||||||
|
|
Loading…
Reference in a new issue