From beea7b0caf3e30f5db2ba19043c1770bf4693d43 Mon Sep 17 00:00:00 2001 From: Harshad Sharma Date: Tue, 14 May 2024 06:22:55 +0530 Subject: [PATCH] Add colorhash --- config/config.exs | 3 +- config/runtime.exs | 6 ++++ freedive.env.sample | 1 + lib/freedive/colorhash.ex | 48 ++++++++++++++++++++++++++++++ lib/freedive_web/live/home_live.ex | 39 ++++++++++++++++++------ test/freedive/colorhash_test.exs | 23 ++++++++++++++ 6 files changed, 110 insertions(+), 10 deletions(-) create mode 100644 lib/freedive/colorhash.ex create mode 100644 test/freedive/colorhash_test.exs diff --git a/config/config.exs b/config/config.exs index de5dca8..c8d6154 100644 --- a/config/config.exs +++ b/config/config.exs @@ -13,7 +13,8 @@ config :freedive, config :freedive, features: [ - register_account: true + register_account: true, + colorhash: true ] # Configures the endpoint diff --git a/config/runtime.exs b/config/runtime.exs index 2c82427..9b46e51 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -18,6 +18,12 @@ if config_env() == :prod do config :freedive, :features, register_account: false end + # Colorhash feature is enabled by default. + # To disable, set the COLORHASH_ENABLE environment variable to "false". + if System.get_env("COLORHASH_ENABLE") == "false" do + config :freedive, :features, colorhash: false + end + database_path = System.get_env("DATABASE_PATH") || raise """ diff --git a/freedive.env.sample b/freedive.env.sample index cff49b5..bf2e487 100644 --- a/freedive.env.sample +++ b/freedive.env.sample @@ -11,6 +11,7 @@ TLS_CERT_PATH="<%= @data_dir %>/tls.crt" # Features REGISTER_ACCOUNT_ENABLE="false" +COLORHASH_ENABLE="true" # Phoenix PHX_SERVER=true diff --git a/lib/freedive/colorhash.ex b/lib/freedive/colorhash.ex new file mode 100644 index 0000000..da43237 --- /dev/null +++ b/lib/freedive/colorhash.ex @@ -0,0 +1,48 @@ +defmodule Freedive.Colorhash do + @moduledoc """ + Given a string returns consistent HSL color. + """ + alias Freedive.Features + + @default_hsl {0, 0, 0} + + def hsl(input, css: true) do + {hue, saturation, lightness} = hsl(input) + "hsl(#{hue}, #{saturation}%, #{lightness}%)" + end + + def hsl(input) do + if Features.enabled?(:colorhash) do + input = String.downcase(input) + hash = :erlang.phash2(input, 2_147_483_647) + + hue = calculate_hue(hash) + saturation = 80 + rem(hash, 21) + lightness = 30 + rem(hash, 31) + + {hue, saturation, lightness} + else + @default_hsl + end + end + + @doc """ + Calculates hue avoiding low contrast colors. + """ + def calculate_hue(hash) do + base_hue = rem(hash, 360) + + # Ranges to exclude (yellow to light green, cyan) + excluded_ranges = [ + # yellow + {50, 70}, + # cyan + {175, 185}, + ] + + # Adjust the hue to skip over excluded ranges + Enum.reduce(excluded_ranges, base_hue, fn {start, stop}, acc_hue -> + if acc_hue >= start and acc_hue <= stop, do: stop + (acc_hue - start + 1), else: acc_hue + end) + end +end diff --git a/lib/freedive_web/live/home_live.ex b/lib/freedive_web/live/home_live.ex index 612ddc6..feef2b7 100644 --- a/lib/freedive_web/live/home_live.ex +++ b/lib/freedive_web/live/home_live.ex @@ -1,5 +1,6 @@ defmodule FreediveWeb.HomeLive do use FreediveWeb, :live_view + import Freedive.Colorhash def render(assigns) do ~H""" @@ -14,11 +15,19 @@ defmodule FreediveWeb.HomeLive do System <.panel_tabs is-hidden-tablet> - - - - - + + + + + + + + + <.panel_block> @@ -37,25 +46,37 @@ defmodule FreediveWeb.HomeLive do <.link patch={~p"/users/settings"} class="panel-block pt-1"> - Account settings <.link patch={~p"/services"} class="panel-block pt-1"> -
System services
<.link patch={~p"/packages"} class="panel-block pt-1"> - System packages <.link patch={~p"/updates"} class="panel-block pt-1"> - System software updates diff --git a/test/freedive/colorhash_test.exs b/test/freedive/colorhash_test.exs new file mode 100644 index 0000000..532e5ad --- /dev/null +++ b/test/freedive/colorhash_test.exs @@ -0,0 +1,23 @@ +defmodule Freedive.ColorhashTest do + use ExUnit.Case + + test "hsl" do + assert Freedive.Colorhash.hsl("foo") == {312, 80, 48} + assert Freedive.Colorhash.hsl("bar") == {38, 88, 38} + assert Freedive.Colorhash.hsl("baz") == {288, 95, 42} + end + + test "hsl with css" do + assert Freedive.Colorhash.hsl("foo", css: true) == "hsl(312, 80%, 48%)" + assert Freedive.Colorhash.hsl("bar", css: true) == "hsl(38, 88%, 38%)" + assert Freedive.Colorhash.hsl("baz", css: true) == "hsl(288, 95%, 42%)" + end + + test "hsl with disabled feature" do + Freedive.Features.disable(:colorhash) + assert Freedive.Colorhash.hsl("foo") == {0, 0, 0} + assert Freedive.Colorhash.hsl("bar") == {0, 0, 0} + assert Freedive.Colorhash.hsl("baz") == {0, 0, 0} + Freedive.Features.enable(:colorhash) + end +end