defmodule Livebook.Users.User do @moduledoc false # Represents a Livebook user. # # Livebook users are not regular web app accounts, # but rather ephemeral data about the clients # using the app. Every person using Livebook # can provide data like name and cursor color # to improve visibility during collaboration. defstruct [:id, :name, :hex_color] alias Livebook.Utils @type t :: %__MODULE__{ id: id(), name: String.t() | nil, hex_color: hex_color() } @type id :: Utils.id() @type hex_color :: String.t() @doc """ Generates a new user. """ @spec new() :: t() def new() do %__MODULE__{ id: Utils.random_id(), name: nil, hex_color: random_hex_color() } end @doc """ Validates `attrs` and returns an updated user. In case of validation errors `{:error, errors, user}` tuple is returned, where `user` is partially updated by using only the valid attributes. """ @spec change(t(), %{binary() => any()}) :: {:ok, t()} | {:error, list({atom(), String.t()}), t()} def change(user, attrs \\ %{}) do {user, []} |> change_name(attrs) |> change_hex_color(attrs) |> case do {user, []} -> {:ok, user} {user, errors} -> {:error, errors, user} end end defp change_name({user, errors}, %{"name" => ""}) do {%{user | name: nil}, errors} end defp change_name({user, errors}, %{"name" => name}) do {%{user | name: name}, errors} end defp change_name({user, errors}, _attrs), do: {user, errors} defp change_hex_color({user, errors}, %{"hex_color" => hex_color}) do if Utils.valid_hex_color?(hex_color) do {%{user | hex_color: hex_color}, errors} else {user, [{:hex_color, "not a valid color"} | errors]} end end defp change_hex_color({user, errors}, _attrs), do: {user, errors} @doc """ Returns a random hex color for a user. ## Options * `:except` - a list of colors to omit """ def random_hex_color(opts \\ []) do colors = [ # red "#F87171", # yellow "#FBBF24", # green "#6EE7B7", # blue "#60A5FA", # purple "#A78BFA", # pink "#F472B6", # salmon "#FA8072", # mat green "#9ED9CC" ] except = opts[:except] || [] colors = colors -- except Enum.random(colors) end end