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
val =
case val do
false -> Map.get(map_val, "selected_channel", 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