defmodule BlockBox do @moduledoc """ A tool used to generate slack UI blocks using elixir defined functions. ## Installation ```elixir def deps do [ {:blockbox, "~> 1.1.2"} ] end ``` ## Usage use BlockBox to access all the generator functions defined in other modules. ```elixir use BlockBox ``` """ alias BlockBox.CompositionObjects, as: CO alias BlockBox.LayoutBlocks, as: LB alias BlockBox.BlockElements, as: BE alias BlockBox.Views, as: Views @doc """ A quality-of-life function that takes in the `values` key of the view response payload and generates a map from `action_id` to the bottom level values. By default, the function maps `action_id`s to values (recommended approach) but by specifying `:block_id` as the second argument, it will map `block_id`s to values instead. ## Example ``` iex> view_values_payload = %{ ...> "attachments" => %{ ...> "att_input" => %{ ...> "type" => "multi_static_select", ...> "selected_options" => [%{"value" => "1"}, %{"value" => "2"}] ...> } ...> }, ...> "description" => %{ ...> "desc_input" => %{"type" => "plain_text_input", "value" => "description text"} ...> }, ...> "labels" => %{ ...> "label_input" => %{"type" => "plain_text_input", "value" => "label text"} ...> }, ...> "priority" => %{ ...> "pri_input" => %{ ...> "selected_option" => %{ ...> "text" => %{"emoji" => true, "text" => "P4", "type" => "plain_text"}, ...> "value" => "9" ...> }, ...> "type" => "static_select" ...> } ...> }, ...> "summary" => %{ ...> "summ_input" => %{"type" => "plain_text_input", "value" => "summary text"} ...> }, ...> "watchers" => %{ ...> "watch_input" => %{"type" => "multi_users_select", "selected_users" => ["11221", "12D123"]} ...> } ...>} iex> BlockBox.get_submission_values(view_values_payload) %{ "att_input" => ["1", "2"], "desc_input" => "description text", "label_input" => "label text", "pri_input" => "9", "summ_input" => "summary text", "watch_input" => ["11221", "12D123"] } iex> BlockBox.get_submission_values(view_values_payload, :block_id) %{ "attachments" => ["1", "2"], "description" => "description text", "labels" => "label text", "priority" => "9", "summary" => "summary text", "watchers" => ["11221", "12D123"] } ``` """ @spec get_submission_values(map(), :action_id | :block_id) :: map() def get_submission_values(values_payload, type \\ :action_id) def get_submission_values(values_payload, :action_id) do Enum.reduce(values_payload, %{}, fn {_k, v}, acc -> Map.merge(acc, map_values(v)) end) end def get_submission_values(values_payload, :block_id) do map_values(values_payload) end defp map_values(payload) do Enum.reduce(payload, %{}, fn {k, v}, acc -> result = get_val(v) case result do [head | []] -> Map.put(acc, k, head) [_head | _tail] -> Map.put(acc, k, result) [] -> acc _ -> acc end end) end defp get_val(list_val) when is_list(list_val) do Enum.reduce(list_val, [], fn v, acc -> result_val = get_val(v) case result_val do nil -> acc _ -> acc ++ get_val(v) end end) end defp get_val(map_val) when is_map(map_val) do val = Map.get(map_val, "value", false) val = case val do false -> Map.get(map_val, "selected_date", false) _ -> val end val = case val do false -> Map.get(map_val, "selected_users", false) _ -> val end case val do false -> Enum.reduce(map_val, [], fn {_k, v}, acc -> vals = get_val(v) case vals == [] or vals == nil do true -> acc false -> acc ++ vals end end) _ -> [val] end end defp get_val(_val) do nil end defmacro __using__(_opts) do quote do # composition objects defdelegate text_object(text, type \\ :plain_text, opts \\ []), to: CO defdelegate confirm_object(title, text, confirm \\ "Confirm", deny \\ "Deny"), to: CO defdelegate option_object(text, value, opts \\ []), to: CO defdelegate option_group_object(label, options), to: CO defdelegate filter_object(options), to: CO # layout blocks defdelegate section(text, opts \\ []), to: LB defdelegate divider(opts \\ []), to: LB defdelegate image_block(image_url, alt_text, opts \\ []), to: LB defdelegate actions_block(elements, opts \\ []), to: LB defdelegate context_block(elements, opts \\ []), to: LB defdelegate input(label, element, opts \\ []), to: LB defdelegate file_block(external_id, source \\ "remote", opts \\ []), to: LB # block elements defdelegate button(text, action_id, opts \\ []), to: BE defdelegate datepicker(action_id, opts \\ []), to: BE defdelegate image(image_url, alt_text), to: BE defdelegate overflow_menu(action_id, options, opts \\ []), to: BE defdelegate plain_text_input(action_id, opts \\ []), to: BE defdelegate radio_buttons(action_id, options, opts \\ []), to: BE defdelegate checkboxes(action_id, options, opts \\ []), to: BE defdelegate select_menu(placeholder, type, action_id, opts \\ []), to: BE defdelegate multi_select_menu(placeholder, type, action_id, opts \\ []), to: BE # view payload defdelegate build_view(type, title, blocks, opts \\ []), to: Views # auxilliary functions defdelegate get_submission_values(payload, type \\ :action_id), to: BlockBox end end end