From 57ebf0f1be8b92680f470cb890efb77d5e891c7a Mon Sep 17 00:00:00 2001 From: Alexandre de Souza Date: Mon, 8 Aug 2022 12:03:35 -0300 Subject: [PATCH] Add Hub LiveView page --- lib/livebook_web/live/hub_live.ex | 325 ++++++++++++++++++++++++++++++ lib/livebook_web/router.ex | 4 + 2 files changed, 329 insertions(+) create mode 100644 lib/livebook_web/live/hub_live.ex diff --git a/lib/livebook_web/live/hub_live.ex b/lib/livebook_web/live/hub_live.ex new file mode 100644 index 000000000..7281aedf7 --- /dev/null +++ b/lib/livebook_web/live/hub_live.ex @@ -0,0 +1,325 @@ +defmodule LivebookWeb.HubLive do + use LivebookWeb, :live_view + + import LivebookWeb.UserHelpers + + alias Livebook.Hub + alias Livebook.Hub.Fly + alias Livebook.Hub.Settings + alias Livebook.Users.User + alias LivebookWeb.{PageHelpers, SidebarHelpers} + + @impl true + def mount(_params, _session, socket) do + {:ok, + assign(socket, + selected_hub_service: nil, + machines: [], + machine_options: [], + data: %{}, + page_title: "Livebook - Hub" + )} + end + + @impl true + def render(assigns) do + ~H""" +
+ + +
+
+
+ +

+ Here you can create your Hubs. + Keep in mind that this configuration gets persisted and + will be restored on application launch. +

+

+ Follow the next steps to create you Hub configuration. +

+
+ +
+

+ 1. Select your Hub service +

+ +
+ <.card_item + id="fly" + selected={@selected_hub_service} + title="Fly" + headline="Connect to your application" + > + <:logo> + <.fly_logo /> + + + + <.card_item + disabled + selected={@selected_hub_service} + id="enterprise" + title="Enterprise" + headline="Coming soon..." + > + <:logo> + Fly logo + + +
+
+ + <%= if @selected_hub_service do %> +
+

+ 2. Connect to your Hub with the following form +

+ + <%= if @selected_hub_service == "fly" do %> + <.fly_form socket={@socket} data={@data} machines={@machine_options} /> + <% end %> +
+ <% end %> +
+
+ + <.current_user_modal current_user={@current_user} /> +
+ """ + end + + defp card_item(assigns) do + assigns = put_class(assigns) + + ~H""" +
+ +
+

+ <%= @title %> +

+ +

+ <%= @headline %> +

+
+
+ """ + end + + defp fly_logo(assigns) do + ~H""" + + Fly + + + + + + + + + + + + + + + + + + + + """ + end + + defp fly_form(assigns) do + ~H""" + <.form + class="flex flex-col space-y-4" + let={f} + for={:fly} + phx-submit="save" + phx-change="update_data" + phx-debounce="blur" + > +
+

+ Access Token +

+ <%= password_input(f, :token, + phx_change: "fetch_machines", + phx_debounce: "blur", + value: @data["token"], + class: "input w-full", + autofocus: true, + spellcheck: "false", + autocomplete: "off" + ) %> +
+ + <%= if length(@machines) > 0 do %> +
+

+ Application +

+ <%= select(f, :application, @machines, class: "input") %> +
+ +
+
+

+ Name +

+ <%= text_input(f, :name, value: @data["name"], class: "input") %> +
+ +
+

+ Color +

+ +
+ <.hex_color form={f} name="hex_color" value={@data["hex_color"]} /> +
+
+
+ + <%= submit("Save", class: "button-base button-blue") %> + <% end %> + + """ + end + + defp hex_color(assigns) do + ~H""" +
+
+
+
+ <%= text_input(@form, @name, + value: @value, + class: "input", + spellcheck: "false", + maxlength: 7 + ) %> + +
+ """ + end + + defp put_class(%{id: id, selected: service} = assigns) when service === id do + assign_new(assigns, :class, fn -> "flex card-item flex-col selected" end) + end + + defp put_class(%{disabled: true} = assigns) do + assign_new(assigns, :class, fn -> "flex card-item flex-col disabled" end) + end + + defp put_class(assigns) do + assign_new(assigns, :class, fn -> "flex card-item flex-col" end) + end + + @impl true + def handle_event("select_hub_service", %{"value" => service}, socket) do + {:noreply, assign(socket, selected_hub_service: service)} + end + + def handle_event("fetch_machines", %{"fly" => %{"token" => token}}, socket) do + case Hub.fetch_machines(%Fly{token: token}) do + {:ok, machines} -> + data = %{"token" => token, "hex_color" => User.random_hex_color()} + opts = select_machine_options(machines) + + {:noreply, assign(socket, data: data, machines: machines, machine_options: opts)} + + {:error, _} -> + {:noreply, + socket + |> assign(mahcines: [], machine_options: [], data: %{}) + |> put_flash(:error, "Invalid Access Token")} + end + end + + def handle_event("save", %{"fly" => params}, socket) do + machines = socket.assigns.machines + + case Enum.find(machines, &(&1.id == params["application"])) do + nil -> + {:noreply, + socket + |> assign(data: params) + |> put_flash(:error, "Internal Server Error")} + + selected_machine -> + opts = select_machine_options(machines, params["application"]) + + Settings.save_machine(%{ + selected_machine + | name: params["name"], + color: params["hex_color"], + token: params["token"] + }) + + {:noreply, assign(socket, data: params, machine_options: opts)} + end + end + + def handle_event("update_data", %{"fly" => data}, socket) do + opts = select_machine_options(socket.assigns.machines, data["application"]) + + {:noreply, assign(socket, data: data, machine_options: opts)} + end + + def handle_event("randomize_color", _, socket) do + data = Map.put(socket.assigns.data, "hex_color", User.random_hex_color()) + {:noreply, assign(socket, data: data)} + end + + defp select_machine_options(machines, machine_id \\ nil) do + for machine <- machines do + if machine.id == machine_id do + [key: machine.name, value: machine.id, selected: true] + else + [key: machine.name, value: machine.id] + end + end + end +end diff --git a/lib/livebook_web/router.ex b/lib/livebook_web/router.ex index eac9b9b47..165a8af4a 100644 --- a/lib/livebook_web/router.ex +++ b/lib/livebook_web/router.ex @@ -55,6 +55,10 @@ defmodule LivebookWeb.Router do live "/explore", ExploreLive, :page live "/explore/notebooks/:slug", ExploreLive, :notebook + if Application.get_env(:livebook, :feature_flags)[:hub] do + live "/hub", HubLive, :page + end + live "/sessions/:id", SessionLive, :page live "/sessions/:id/shortcuts", SessionLive, :shortcuts live "/sessions/:id/settings/runtime", SessionLive, :runtime_settings