summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorpsvenk <45520974+psvenk@users.noreply.github.com>2019-08-15 16:29:55 +0000
committerOmar Roth <omarroth@protonmail.com>2019-08-15 11:29:55 -0500
commitf54fbd057ebb8d1e9631782b6d182b403598648f (patch)
treefb2e56fed7d71bbcdeff59b711803057af330ca9 /src
parent19eceb4ecc8b8261c6268f82b8c5d550604e9f4f (diff)
downloadinvidious-f54fbd057ebb8d1e9631782b6d182b403598648f.tar.gz
invidious-f54fbd057ebb8d1e9631782b6d182b403598648f.tar.bz2
invidious-f54fbd057ebb8d1e9631782b6d182b403598648f.zip
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>).
Diffstat (limited to 'src')
-rw-r--r--src/invidious.cr30
-rw-r--r--src/invidious/helpers/helpers.cr74
-rw-r--r--src/invidious/helpers/patch_mapping.cr2
-rw-r--r--src/invidious/helpers/utils.cr13
-rw-r--r--src/invidious/users.cr62
-rw-r--r--src/invidious/views/preferences.ecr8
-rw-r--r--src/invidious/views/template.ecr9
7 files changed, 121 insertions, 77 deletions
diff --git a/src/invidious.cr b/src/invidious.cr
index 16695c5f..712a408f 100644
--- a/src/invidious.cr
+++ b/src/invidious.cr
@@ -267,8 +267,7 @@ before_all do |env|
end
end
- dark_mode = env.params.query["dark_mode"]? || preferences.dark_mode.to_s
- dark_mode = dark_mode == "true"
+ dark_mode = convert_theme(env.params.query["dark_mode"]?) || preferences.dark_mode.to_s
thin_mode = env.params.query["thin_mode"]? || preferences.thin_mode.to_s
thin_mode = thin_mode == "true"
@@ -1528,8 +1527,7 @@ post "/preferences" do |env|
locale ||= CONFIG.default_user_preferences.locale
dark_mode = env.params.body["dark_mode"]?.try &.as(String)
- dark_mode ||= "off"
- dark_mode = dark_mode == "on"
+ dark_mode ||= CONFIG.default_user_preferences.dark_mode
thin_mode = env.params.body["thin_mode"]?.try &.as(String)
thin_mode ||= "off"
@@ -1553,6 +1551,7 @@ post "/preferences" do |env|
notifications_only ||= "off"
notifications_only = notifications_only == "on"
+ # Convert to JSON and back again to take advantage of converters used for compatability
preferences = Preferences.from_json({
annotations: annotations,
annotations_subscribed: annotations_subscribed,
@@ -1648,12 +1647,27 @@ get "/toggle_theme" do |env|
if user = env.get? "user"
user = user.as(User)
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
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
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 %(<link rel="stylesheet" href="/css/ionicons.min.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}">)
else
env.response.puts %(<link rel="stylesheet" href="/css/lighttheme.css?v=#{ASSET_COMMIT}">)
diff --git a/src/invidious/helpers/helpers.cr b/src/invidious/helpers/helpers.cr
index ce0ded32..03c1654c 100644
--- a/src/invidious/helpers/helpers.cr
+++ b/src/invidious/helpers/helpers.cr
@@ -24,6 +24,27 @@ end
struct ConfigPreferences
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|
@@ -44,11 +65,11 @@ struct ConfigPreferences
node.raise "Expected scalar, not #{item.class}"
end
- result << item.value
+ result << HTML.escape(item.value[0, 100])
end
rescue ex
if node.is_a?(YAML::Nodes::Scalar)
- result = [node.value, ""]
+ result = [HTML.escape(node.value[0, 100]), ""]
else
result = ["", ""]
end
@@ -58,6 +79,53 @@ struct ConfigPreferences
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({
annotations: {type: Bool, default: false},
annotations_subscribed: {type: Bool, default: false},
@@ -66,7 +134,7 @@ struct ConfigPreferences
comments: {type: Array(String), default: ["youtube", ""], converter: StringToArray},
continue: {type: Bool, default: false},
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},
listen: {type: Bool, default: false},
local: {type: Bool, default: false},
diff --git a/src/invidious/helpers/patch_mapping.cr b/src/invidious/helpers/patch_mapping.cr
index 8360caa6..e138aa1c 100644
--- a/src/invidious/helpers/patch_mapping.cr
+++ b/src/invidious/helpers/patch_mapping.cr
@@ -4,7 +4,7 @@ def Object.from_json(string_or_io, default) : self
new parser, default
end
-# Adds configurable 'default' to
+# Adds configurable 'default'
macro patched_json_mapping(_properties_, strict = false)
{% for key, value in _properties_ %}
{% _properties_[key] = {type: value} unless value.is_a?(HashLiteral) || value.is_a?(NamedTupleLiteral) %}
diff --git a/src/invidious/helpers/utils.cr b/src/invidious/helpers/utils.cr
index 69aae839..b39f65c5 100644
--- a/src/invidious/helpers/utils.cr
+++ b/src/invidious/helpers/utils.cr
@@ -356,3 +356,16 @@ def parse_range(range)
return 0_i64, nil
end
+
+def convert_theme(theme)
+ case theme
+ when "true"
+ "dark"
+ when "false"
+ "light"
+ when "", nil
+ nil
+ else
+ theme
+ end
+end
diff --git a/src/invidious/users.cr b/src/invidious/users.cr
index 35d8a49e..8bd82bf1 100644
--- a/src/invidious/users.cr
+++ b/src/invidious/users.cr
@@ -31,62 +31,6 @@ struct User
end
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
def self.to_json(value : String, json : JSON::Builder)
json.string value
@@ -127,11 +71,11 @@ struct Preferences
annotations: {type: Bool, default: CONFIG.default_user_preferences.annotations},
annotations_subscribed: {type: Bool, default: CONFIG.default_user_preferences.annotations_subscribed},
autoplay: {type: Bool, default: CONFIG.default_user_preferences.autoplay},
- captions: {type: Array(String), default: CONFIG.default_user_preferences.captions, converter: StringToArray},
- comments: {type: Array(String), default: CONFIG.default_user_preferences.comments, 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: ConfigPreferences::StringToArray},
continue: {type: Bool, default: CONFIG.default_user_preferences.continue},
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},
listen: {type: Bool, default: CONFIG.default_user_preferences.listen},
local: {type: Bool, default: CONFIG.default_user_preferences.local},
diff --git a/src/invidious/views/preferences.ecr b/src/invidious/views/preferences.ecr
index b04bcd4d..6ea01fba 100644
--- a/src/invidious/views/preferences.ecr
+++ b/src/invidious/views/preferences.ecr
@@ -122,8 +122,12 @@ function update_value(element) {
</div>
<div class="pure-control-group">
- <label for="dark_mode"><%= translate(locale, "Dark mode: ") %></label>
- <input name="dark_mode" id="dark_mode" type="checkbox" <% if preferences.dark_mode %>checked<% end %>>
+ <label for="dark_mode"><%= translate(locale, "Theme: ") %></label>
+ <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 class="pure-control-group">
diff --git a/src/invidious/views/template.ecr b/src/invidious/views/template.ecr
index 6272d2be..8d8cec88 100644
--- a/src/invidious/views/template.ecr
+++ b/src/invidious/views/template.ecr
@@ -18,13 +18,14 @@
<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/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/lighttheme.css?v=<%= ASSET_COMMIT %>" id="light_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 == "dark" %>media="none"<% end %>>
</head>
<% locale = LOCALES[env.get("preferences").as(Preferences).locale]? %>
<body>
+ <span style="display:none" id="dark_mode_pref"><%= env.get("preferences").as(Preferences).dark_mode %></span>
<div class="pure-g">
<div class="pure-u-1 pure-u-md-2-24"></div>
<div class="pure-u-1 pure-u-md-20-24">
@@ -43,7 +44,7 @@
<% if env.get? "user" %>
<div class="pure-u-1-4">
<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>
<% else %>
<i class="icon ion-ios-moon"></i>
@@ -76,7 +77,7 @@
<% else %>
<div class="pure-u-1-3">
<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>
<% else %>
<i class="icon ion-ios-moon"></i>