defmodule LivebookWeb.HubLive.FlyComponent do
use LivebookWeb, :live_component
alias Livebook.Hubs
alias Livebook.Hubs.{Fly, FlyClient}
alias Livebook.Users.User
@impl true
def update(assigns, socket) do
{:ok,
socket
|> assign(assigns)
|> load_data()}
end
@impl true
def render(assigns) do
~H"""
<.form
id={@id}
class="flex flex-col space-y-4"
let={f}
for={:fly}
phx-submit="save_hub"
phx-change="update_data"
phx-target={@myself}
phx-debounce="blur"
>
Access Token
<%= password_input(f, :access_token,
phx_change: "fetch_data",
phx_debounce: "blur",
phx_target: @myself,
disabled: @operation == :edit,
value: @data["access_token"],
class: "input w-full",
autofocus: true,
spellcheck: "false",
required: true,
autocomplete: "off"
) %>
<%= if length(@apps) > 0 do %>
Application
<%= select(f, :application_id, @select_options,
class: "input",
required: true,
phx_target: @myself,
phx_change: "select_app",
disabled: @operation == :edit
) %>
Name
<%= text_input(f, :hub_name, value: @data["hub_name"], class: "input", required: true) %>
Color
<%= text_input(f, :hub_color,
value: @data["hub_color"],
class: "input",
spellcheck: "false",
required: true,
maxlength: 7
) %>
<.remix_icon icon="refresh-line" class="text-xl" />
<%= submit("Save", class: "button-base button-blue", phx_disable_with: "Saving...") %>
<% end %>
"""
end
defp load_data(%{assigns: %{operation: :new}} = socket) do
assign(socket, data: %{}, select_options: [], apps: [])
end
defp load_data(%{assigns: %{operation: :edit, hub: fly}} = socket) do
data = %{
"access_token" => fly.access_token,
"application_id" => fly.application_id,
"hub_name" => fly.hub_name,
"hub_color" => fly.hub_color
}
{:ok, apps} = FlyClient.fetch_apps(fly.access_token)
opts = select_options(apps, fly.application_id)
assign(socket, data: data, selected_app: fly, select_options: opts, apps: apps)
end
@impl true
def handle_event("fetch_data", %{"fly" => %{"access_token" => token}}, socket) do
case FlyClient.fetch_apps(token) do
{:ok, apps} ->
data = %{"access_token" => token, "hub_color" => User.random_hex_color()}
opts = select_options(apps)
{:noreply, assign(socket, data: data, select_options: opts, apps: apps)}
{:error, _} ->
send(self(), {:flash_error, "Invalid Access Token"})
{:noreply, assign(socket, data: %{}, select_options: [], apps: [])}
end
end
def handle_event("randomize_color", _, socket) do
data = Map.replace!(socket.assigns.data, "hub_color", User.random_hex_color())
{:noreply, assign(socket, data: data)}
end
def handle_event("save_hub", %{"fly" => params}, socket) do
params =
if socket.assigns.data do
Map.merge(socket.assigns.data, params)
else
params
end
case socket.assigns.operation do
:new -> create_fly(socket, params)
:edit -> save_fly(socket, params)
end
end
def handle_event("select_app", %{"fly" => %{"application_id" => app_id}}, socket) do
selected_app = Enum.find(socket.assigns.apps, &(&1.application_id == app_id))
opts = select_options(socket.assigns.apps, app_id)
{:noreply, assign(socket, selected_app: selected_app, select_options: opts)}
end
def handle_event("update_data", %{"fly" => data}, socket) do
data =
if socket.assigns.data do
Map.merge(socket.assigns.data, data)
else
data
end
opts = select_options(socket.assigns.apps, data["application_id"])
{:noreply, assign(socket, data: data, select_options: opts)}
end
defp select_options(hubs, app_id \\ nil) do
disabled_option = [key: "Select one application", value: "", selected: true, disabled: true]
options =
for fly <- hubs do
[
key: "#{fly.organization_name} - #{fly.application_id}",
value: fly.application_id,
selected: fly.application_id == app_id
]
end
Enum.reverse(options ++ [disabled_option])
end
defp create_fly(socket, params) do
if Hubs.hub_exists?(socket.assigns.selected_app.id) do
send(self(), {:flash_error, "Application already exists"})
{:noreply, assign(socket, data: params)}
else
save_fly(socket, params)
end
end
defp save_fly(socket, params) do
Fly.save_fly(socket.assigns.selected_app, params)
opts = select_options(socket.assigns.apps, params["application_id"])
{:noreply, assign(socket, data: params, select_options: opts)}
end
end