diff --git a/assets/js/hooks/emoji_picker.js b/assets/js/hooks/emoji_picker.js
new file mode 100644
index 000000000..762b77865
--- /dev/null
+++ b/assets/js/hooks/emoji_picker.js
@@ -0,0 +1,33 @@
+import { createPicker } from "picmo";
+
+/**
+ * A hook for the emoji picker input.
+ */
+const EmojiPicker = {
+ mounted() {
+ const rootElement = this.el.querySelector("[data-emoji-container]");
+ const preview = this.el.querySelector("[data-emoji-preview]");
+ const input = this.el.querySelector("[data-emoji-input]");
+ const button = this.el.querySelector("[data-emoji-button]");
+
+ const pickerOptions = {
+ rootElement,
+ showSearch: false,
+ showPreview: false,
+ };
+
+ const picker = createPicker(pickerOptions);
+
+ picker.addEventListener("emoji:select", ({ emoji }) => {
+ preview.innerHTML = emoji;
+ input.value = emoji;
+ rootElement.classList.toggle("hidden");
+ });
+
+ button.addEventListener("click", (_) => {
+ rootElement.classList.toggle("hidden");
+ });
+ },
+};
+
+export default EmojiPicker;
diff --git a/assets/js/hooks/index.js b/assets/js/hooks/index.js
index 4200acd85..b53922226 100644
--- a/assets/js/hooks/index.js
+++ b/assets/js/hooks/index.js
@@ -4,6 +4,7 @@ import CellEditor from "./cell_editor";
import ConfirmModal from "./confirm_modal";
import Dropzone from "./dropzone";
import EditorSettings from "./editor_settings";
+import EmojiPicker from "./emoji_picker";
import FocusOnUpdate from "./focus_on_update";
import Headline from "./headline";
import Highlight from "./highlight";
@@ -26,6 +27,7 @@ export default {
ConfirmModal,
Dropzone,
EditorSettings,
+ EmojiPicker,
FocusOnUpdate,
Headline,
Highlight,
diff --git a/assets/package-lock.json b/assets/package-lock.json
index 9e9666b35..eac9a9a91 100644
--- a/assets/package-lock.json
+++ b/assets/package-lock.json
@@ -21,6 +21,7 @@
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view",
+ "picmo": "^5.7.2",
"postcss-import": "^15.0.0",
"postcss-loader": "^7.0.1",
"rehype-katex": "^6.0.0",
@@ -4505,6 +4506,15 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
+ "node_modules/emojibase": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/emojibase/-/emojibase-6.1.0.tgz",
+ "integrity": "sha512-1GkKJPXP6tVkYJHOBSJHoGOr/6uaDxZ9xJ6H7m6PfdGXTmQgbALHLWaVRY4Gi/qf5x/gT/NUXLPuSHYLqtLtrQ==",
+ "funding": {
+ "type": "ko-fi",
+ "url": "https://ko-fi.com/milesjohnson"
+ }
+ },
"node_modules/emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
@@ -8544,6 +8554,17 @@
"resolved": "../deps/phoenix_live_view",
"link": true
},
+ "node_modules/picmo": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/picmo/-/picmo-5.7.2.tgz",
+ "integrity": "sha512-A7c5O8x1Xwq11KBYFY93+GIbHnw9PVz35HaWWHn/dgT08GA67M6cXKjjwzLnEAyXSdxXKrEk8/gPyTs+ibzWfQ==",
+ "dependencies": {
+ "emojibase": "^6.1.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/joeattardi"
+ }
+ },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -14080,6 +14101,11 @@
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
},
+ "emojibase": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/emojibase/-/emojibase-6.1.0.tgz",
+ "integrity": "sha512-1GkKJPXP6tVkYJHOBSJHoGOr/6uaDxZ9xJ6H7m6PfdGXTmQgbALHLWaVRY4Gi/qf5x/gT/NUXLPuSHYLqtLtrQ=="
+ },
"emojis-list": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz",
@@ -16897,6 +16923,14 @@
"phoenix_live_view": {
"version": "file:../deps/phoenix_live_view"
},
+ "picmo": {
+ "version": "5.7.2",
+ "resolved": "https://registry.npmjs.org/picmo/-/picmo-5.7.2.tgz",
+ "integrity": "sha512-A7c5O8x1Xwq11KBYFY93+GIbHnw9PVz35HaWWHn/dgT08GA67M6cXKjjwzLnEAyXSdxXKrEk8/gPyTs+ibzWfQ==",
+ "requires": {
+ "emojibase": "^6.1.0"
+ }
+ },
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
diff --git a/assets/package.json b/assets/package.json
index 451eb2826..493fd4edd 100644
--- a/assets/package.json
+++ b/assets/package.json
@@ -25,6 +25,7 @@
"phoenix": "file:../deps/phoenix",
"phoenix_html": "file:../deps/phoenix_html",
"phoenix_live_view": "file:../deps/phoenix_live_view",
+ "picmo": "^5.7.2",
"postcss-import": "^15.0.0",
"postcss-loader": "^7.0.1",
"rehype-katex": "^6.0.0",
diff --git a/lib/livebook/application.ex b/lib/livebook/application.ex
index 806f3a373..08fb13049 100644
--- a/lib/livebook/application.ex
+++ b/lib/livebook/application.ex
@@ -197,13 +197,11 @@ defmodule Livebook.Application do
if Livebook.Config.feature_flag_enabled?(:localhost_hub) do
defp insert_development_hub do
- unless Livebook.Hubs.hub_exists?("local-host") do
- Livebook.Hubs.save_hub(%Livebook.Hubs.Local{
- id: "local-host",
- hub_name: "Localhost",
- hub_color: Livebook.EctoTypes.HexColor.random()
- })
- end
+ Livebook.Hubs.save_hub(%Livebook.Hubs.Local{
+ id: "local-host",
+ hub_name: "Localhost",
+ hub_emoji: "🏠"
+ })
end
else
defp insert_development_hub, do: :ok
diff --git a/lib/livebook/hubs.ex b/lib/livebook/hubs.ex
index d3e94e8b3..ddfa4ee1f 100644
--- a/lib/livebook/hubs.ex
+++ b/lib/livebook/hubs.ex
@@ -2,7 +2,7 @@ defmodule Livebook.Hubs do
@moduledoc false
alias Livebook.Storage
- alias Livebook.Hubs.{Enterprise, Fly, Local, Metadata, Provider}
+ alias Livebook.Hubs.{Broadcasts, Enterprise, Fly, Local, Metadata, Provider}
@namespace :hubs
@@ -28,7 +28,7 @@ defmodule Livebook.Hubs do
@spec get_metadatas() :: list(Metadata.t())
def get_metadatas do
for hub <- get_hubs() do
- Provider.normalize(hub)
+ %{Provider.normalize(hub) | connected?: Provider.connected?(hub)}
end
end
@@ -71,7 +71,7 @@ defmodule Livebook.Hubs do
attributes = struct |> Map.from_struct() |> Map.to_list()
:ok = Storage.insert(@namespace, struct.id, attributes)
:ok = connect_hub(struct)
- :ok = broadcast_hubs_change()
+ :ok = Broadcasts.hubs_metadata_changed()
struct
end
@@ -84,7 +84,7 @@ defmodule Livebook.Hubs do
end
:ok = Storage.delete(@namespace, id)
- :ok = broadcast_hubs_change()
+ :ok = Broadcasts.hubs_metadata_changed()
end
:ok
@@ -98,30 +98,50 @@ defmodule Livebook.Hubs do
end
@doc """
- Subscribes to updates in hubs information.
+ Subscribes to one or more subtopics in `"hubs"`.
## Messages
- * `{:hubs_metadata_changed, hubs}`
+ Topic `hubs:crud`:
+
+ * `:hubs_metadata_changed`
+
+ Topic `hubs:connection`:
+
+ * `:hub_connected`
+ * `:hub_disconnected`
+ * `{:connection_error, reason}`
+ * `{:disconnection_error, reason}`
+
+ Topic `hubs:secrets`:
+
+ * `{:secret_created, %Secret{}}`
+ * `{:secret_updated, %Secret{}}`
"""
- @spec subscribe() :: :ok | {:error, term()}
- def subscribe do
- Phoenix.PubSub.subscribe(Livebook.PubSub, "hubs")
+ @spec subscribe(atom() | list(atom())) :: :ok | {:error, term()}
+ def subscribe(topics) when is_list(topics) do
+ for topic <- topics, do: subscribe(topic)
+
+ :ok
+ end
+
+ def subscribe(topic) do
+ Phoenix.PubSub.subscribe(Livebook.PubSub, "hubs:#{topic}")
end
@doc """
Unsubscribes from `subscribe/0`.
"""
- @spec unsubscribe() :: :ok
- def unsubscribe do
- Phoenix.PubSub.unsubscribe(Livebook.PubSub, "hubs")
+ @spec unsubscribe(atom() | list(atom())) :: :ok
+ def unsubscribe(topics) when is_list(topics) do
+ for topic <- topics, do: unsubscribe(topic)
+
+ :ok
end
- # Notifies interested processes about hubs data change.
- # Broadcasts `{:hubs_metadata_changed, hubs}` message under the `"hubs"` topic.
- defp broadcast_hubs_change do
- Phoenix.PubSub.broadcast(Livebook.PubSub, "hubs", {:hubs_metadata_changed, get_metadatas()})
+ def unsubscribe(topic) do
+ Phoenix.PubSub.unsubscribe(Livebook.PubSub, "hubs:#{topic}")
end
defp to_struct(%{id: "fly-" <> _} = fields) do
diff --git a/lib/livebook/hubs/broadcasts.ex b/lib/livebook/hubs/broadcasts.ex
new file mode 100644
index 000000000..74ee89cad
--- /dev/null
+++ b/lib/livebook/hubs/broadcasts.ex
@@ -0,0 +1,71 @@
+defmodule Livebook.Hubs.Broadcasts do
+ @moduledoc false
+
+ alias Livebook.Secrets.Secret
+
+ @type broadcast :: :ok | {:error, term()}
+
+ @crud_topic "hubs:crud"
+ @connection_topic "hubs:connection"
+ @secrets_topic "hubs:secrets"
+
+ @doc """
+ Broadcasts when hubs changed under `hubs:crud` topic
+ """
+ @spec hubs_metadata_changed() :: broadcast()
+ def hubs_metadata_changed do
+ broadcast(@crud_topic, :hubs_metadata_changed)
+ end
+
+ @doc """
+ Broadcasts when hub connected under `hubs:connection` topic
+ """
+ @spec hub_connected() :: broadcast()
+ def hub_connected do
+ broadcast(@connection_topic, :hub_connected)
+ end
+
+ @doc """
+ Broadcasts when hub disconnected under `hubs:connection` topic
+ """
+ @spec hub_disconnected() :: broadcast()
+ def hub_disconnected do
+ broadcast(@connection_topic, :hub_disconnected)
+ end
+
+ @doc """
+ Broadcasts when hub had an error when connecting under `hubs:connection` topic
+ """
+ @spec hub_connection_failed(String.t()) :: broadcast()
+ def hub_connection_failed(reason) when is_binary(reason) do
+ broadcast(@connection_topic, {:connection_error, reason})
+ end
+
+ @doc """
+ Broadcasts when hub had an error when disconnecting under `hubs:connection` topic
+ """
+ @spec hub_disconnection_failed(String.t()) :: broadcast()
+ def hub_disconnection_failed(reason) when is_binary(reason) do
+ broadcast(@connection_topic, {:disconnection_error, reason})
+ end
+
+ @doc """
+ Broadcasts when hub received a new secret under `hubs:secrets` topic
+ """
+ @spec secret_created(Secret.t()) :: broadcast()
+ def secret_created(%Secret{} = secret) do
+ broadcast(@secrets_topic, {:secret_created, secret})
+ end
+
+ @doc """
+ Broadcasts when hub received an updated secret under `hubs:secrets` topic
+ """
+ @spec secret_updated(Secret.t()) :: broadcast()
+ def secret_updated(%Secret{} = secret) do
+ broadcast(@secrets_topic, {:secret_updated, secret})
+ end
+
+ defp broadcast(topic, message) do
+ Phoenix.PubSub.broadcast(Livebook.PubSub, topic, message)
+ end
+end
diff --git a/lib/livebook/hubs/enterprise.ex b/lib/livebook/hubs/enterprise.ex
index 49358d276..c85e02e45 100644
--- a/lib/livebook/hubs/enterprise.ex
+++ b/lib/livebook/hubs/enterprise.ex
@@ -12,7 +12,7 @@ defmodule Livebook.Hubs.Enterprise do
token: String.t() | nil,
external_id: String.t() | nil,
hub_name: String.t() | nil,
- hub_color: String.t() | nil
+ hub_emoji: String.t() | nil
}
embedded_schema do
@@ -20,7 +20,7 @@ defmodule Livebook.Hubs.Enterprise do
field :token, :string
field :external_id, :string
field :hub_name, :string
- field :hub_color, Livebook.EctoTypes.HexColor
+ field :hub_emoji, :string
end
@fields ~w(
@@ -28,7 +28,7 @@ defmodule Livebook.Hubs.Enterprise do
token
external_id
hub_name
- hub_color
+ hub_emoji
)a
@doc """
@@ -113,7 +113,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
token: fields.token,
external_id: fields.external_id,
hub_name: fields.hub_name,
- hub_color: fields.hub_color
+ hub_emoji: fields.hub_emoji
}
end
@@ -122,7 +122,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
id: enterprise.id,
name: enterprise.hub_name,
provider: enterprise,
- color: enterprise.hub_color
+ emoji: enterprise.hub_emoji
}
end
@@ -130,4 +130,8 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Enterprise do
def connect(%Livebook.Hubs.Enterprise{} = enterprise),
do: {Livebook.Hubs.EnterpriseClient, enterprise}
+
+ def connected?(%Livebook.Hubs.Enterprise{id: id}) do
+ Livebook.Hubs.EnterpriseClient.connected?(id)
+ end
end
diff --git a/lib/livebook/hubs/enterprise_client.ex b/lib/livebook/hubs/enterprise_client.ex
index 04db576c3..d2361f2ad 100644
--- a/lib/livebook/hubs/enterprise_client.ex
+++ b/lib/livebook/hubs/enterprise_client.ex
@@ -2,21 +2,31 @@ defmodule Livebook.Hubs.EnterpriseClient do
@moduledoc false
use GenServer
+ alias Livebook.Hubs.Broadcasts
alias Livebook.Hubs.Enterprise
alias Livebook.Secrets.Secret
alias Livebook.WebSocket.Server
- @pubsub_topic "enterprise"
@registry Livebook.HubsRegistry
- defstruct [:server, :hub, secrets: []]
+ defstruct [:server, :hub, connected?: false, secrets: []]
@doc """
Connects the Enterprise client with WebSocket server.
"""
@spec start_link(Enterprise.t()) :: GenServer.on_start()
def start_link(%Enterprise{} = enterprise) do
- GenServer.start_link(__MODULE__, enterprise, name: registry_name(enterprise))
+ GenServer.start_link(__MODULE__, enterprise, name: registry_name(enterprise.id))
+ end
+
+ @doc """
+ Stops the WebSocket server.
+ """
+ @spec stop(pid()) :: :ok
+ def stop(pid) do
+ pid |> GenServer.call(:get_server) |> GenServer.stop()
+
+ :ok
end
@doc """
@@ -36,27 +46,15 @@ defmodule Livebook.Hubs.EnterpriseClient do
end
@doc """
- Subscribe to WebSocket Server events.
-
- ## Messages
-
- * `{:connect, :ok, :connected}`
- * `{:connect, :error, reason}`
- * `{:secret_created, %Secret{}}`
- * `{:secret_updated, %Secret{}}`
-
+ Returns if the given enterprise is connected.
"""
- @spec subscribe() :: :ok | {:error, {:already_registered, pid()}}
- def subscribe do
- Phoenix.PubSub.subscribe(Livebook.PubSub, @pubsub_topic)
- end
-
- @doc """
- Unsubscribes from `subscribe/0`.
- """
- @spec unsubscribe() :: :ok
- def unsubscribe do
- Phoenix.PubSub.unsubscribe(Livebook.PubSub, @pubsub_topic)
+ @spec connected?(String.t()) :: boolean()
+ def connected?(id) do
+ try do
+ GenServer.call(registry_name(id), :connected?)
+ catch
+ :exit, _ -> false
+ end
end
## GenServer callbacks
@@ -78,44 +76,48 @@ defmodule Livebook.Hubs.EnterpriseClient do
{:reply, state.secrets, state}
end
- @impl true
- def handle_info({:connect, _, _} = message, state) do
- broadcast_message(message)
- {:noreply, state}
+ def handle_call(:connected?, _caller, state) do
+ {:reply, state.connected?, state}
end
- def handle_info({:disconnect, :error, _} = message, state) do
- broadcast_message(message)
- {:noreply, state}
+ @impl true
+ def handle_info({:connect, :ok, _}, state) do
+ Broadcasts.hub_connected()
+ {:noreply, %{state | connected?: true}}
+ end
+
+ def handle_info({:connect, :error, reason}, state) do
+ Broadcasts.hub_connection_failed(reason)
+ {:noreply, %{state | connected?: false}}
+ end
+
+ def handle_info({:disconnect, :error, reason}, state) do
+ Broadcasts.hub_disconnection_failed(reason)
+ {:noreply, %{state | connected?: false}}
end
def handle_info({:event, :secret_created, %{name: name, value: value}}, state) do
secret = %Secret{name: name, value: value}
- broadcast_message({:secret_created, secret})
+ Broadcasts.secret_created(secret)
{:noreply, put_secret(state, secret)}
end
def handle_info({:event, :secret_updated, %{name: name, value: value}}, state) do
secret = %Secret{name: name, value: value}
- broadcast_message({:secret_updated, secret})
+ Broadcasts.secret_updated(secret)
{:noreply, put_secret(state, secret)}
end
def handle_info({:disconnect, :ok, :disconnected}, state) do
+ Broadcasts.hub_disconnected()
{:stop, :normal, state}
end
# Private
- # Notifies interested processes about WebSocket Server messages.
- # Broadcasts the given message under the `"enterprise"` topic.
- defp broadcast_message(message) do
- Phoenix.PubSub.broadcast(Livebook.PubSub, @pubsub_topic, message)
- end
-
- defp registry_name(%Enterprise{id: id}) do
+ defp registry_name(id) do
{:via, Registry, {@registry, id}}
end
diff --git a/lib/livebook/hubs/fly.ex b/lib/livebook/hubs/fly.ex
index 62669d236..cb56e4cbd 100644
--- a/lib/livebook/hubs/fly.ex
+++ b/lib/livebook/hubs/fly.ex
@@ -10,7 +10,7 @@ defmodule Livebook.Hubs.Fly do
id: String.t() | nil,
access_token: String.t() | nil,
hub_name: String.t() | nil,
- hub_color: String.t() | nil,
+ hub_emoji: String.t() | nil,
organization_id: String.t() | nil,
organization_type: String.t() | nil,
organization_name: String.t() | nil,
@@ -20,7 +20,7 @@ defmodule Livebook.Hubs.Fly do
embedded_schema do
field :access_token, :string
field :hub_name, :string
- field :hub_color, Livebook.EctoTypes.HexColor
+ field :hub_emoji, :string
field :organization_id, :string
field :organization_type, :string
field :organization_name, :string
@@ -30,7 +30,7 @@ defmodule Livebook.Hubs.Fly do
@fields ~w(
access_token
hub_name
- hub_color
+ hub_emoji
organization_id
organization_name
organization_type
@@ -116,7 +116,7 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Fly do
| id: fields.id,
access_token: fields.access_token,
hub_name: fields.hub_name,
- hub_color: fields.hub_color,
+ hub_emoji: fields.hub_emoji,
organization_id: fields.organization_id,
organization_type: fields.organization_type,
organization_name: fields.organization_name,
@@ -129,11 +129,13 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Fly do
id: fly.id,
name: fly.hub_name,
provider: fly,
- color: fly.hub_color
+ emoji: fly.hub_emoji
}
end
def type(_fly), do: "fly"
def connect(_fly), do: nil
+
+ def connected?(_fly), do: false
end
diff --git a/lib/livebook/hubs/local.ex b/lib/livebook/hubs/local.ex
index 009f61142..5f96a93cd 100644
--- a/lib/livebook/hubs/local.ex
+++ b/lib/livebook/hubs/local.ex
@@ -1,12 +1,12 @@
defmodule Livebook.Hubs.Local do
@moduledoc false
- defstruct [:id, :hub_name, :hub_color]
+ defstruct [:id, :hub_name, :hub_emoji]
end
defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Local do
def load(%Livebook.Hubs.Local{} = local, fields) do
- %{local | id: fields.id, hub_name: fields.hub_name, hub_color: fields.hub_color}
+ %{local | id: fields.id, hub_name: fields.hub_name, hub_emoji: fields.hub_emoji}
end
def normalize(%Livebook.Hubs.Local{} = local) do
@@ -14,11 +14,13 @@ defimpl Livebook.Hubs.Provider, for: Livebook.Hubs.Local do
id: local.id,
name: local.hub_name,
provider: local,
- color: local.hub_color
+ emoji: local.hub_emoji
}
end
def type(_local), do: "local"
def connect(_local), do: nil
+
+ def connected?(_local), do: false
end
diff --git a/lib/livebook/hubs/metadata.ex b/lib/livebook/hubs/metadata.ex
index 59272b0ea..60acbb2cb 100644
--- a/lib/livebook/hubs/metadata.ex
+++ b/lib/livebook/hubs/metadata.ex
@@ -1,12 +1,13 @@
defmodule Livebook.Hubs.Metadata do
@moduledoc false
- defstruct [:id, :name, :provider, :color]
+ defstruct [:id, :name, :provider, :emoji, connected?: false]
@type t :: %__MODULE__{
id: String.t(),
name: String.t(),
provider: struct(),
- color: String.t()
+ emoji: String.t(),
+ connected?: boolean()
}
end
diff --git a/lib/livebook/hubs/provider.ex b/lib/livebook/hubs/provider.ex
index 3c54b92f7..833fa1a76 100644
--- a/lib/livebook/hubs/provider.ex
+++ b/lib/livebook/hubs/provider.ex
@@ -24,4 +24,10 @@ defprotocol Livebook.Hubs.Provider do
"""
@spec connect(struct()) :: Supervisor.child_spec() | module() | {module(), any()} | nil
def connect(struct)
+
+ @doc """
+ Gets the connection status of the given struct.
+ """
+ @spec connected?(struct()) :: boolean()
+ def connected?(struct)
end
diff --git a/lib/livebook_web/live/form_helpers.ex b/lib/livebook_web/live/form_helpers.ex
index 901367520..a43d60b74 100644
--- a/lib/livebook_web/live/form_helpers.ex
+++ b/lib/livebook_web/live/form_helpers.ex
@@ -2,7 +2,6 @@ defmodule LivebookWeb.FormHelpers do
@moduledoc """
Conveniences for translating and building error messages.
"""
-
use Phoenix.Component
import Phoenix.HTML.Form
@@ -55,6 +54,38 @@ defmodule LivebookWeb.FormHelpers do
"""
end
+ @doc """
+ Emoji input.
+ """
+ def emoji_input(assigns) do
+ ~H"""
+
+
+
+
+ <%= input_value(@form, @field) %>
+
+
+
+
+
+
+
+
+
+ <%= hidden_input(@form, @field, class: "hidden emoji-picker-input", "data-emoji-input": true) %>
+
+
+ """
+ end
+
@doc """
Translates an error message.
"""
diff --git a/lib/livebook_web/live/hooks/sidebar_hook.ex b/lib/livebook_web/live/hooks/sidebar_hook.ex
index d8c92212b..3c7837955 100644
--- a/lib/livebook_web/live/hooks/sidebar_hook.ex
+++ b/lib/livebook_web/live/hooks/sidebar_hook.ex
@@ -6,7 +6,7 @@ defmodule LivebookWeb.SidebarHook do
def on_mount(:default, _params, _session, socket) do
if connected?(socket) do
- Livebook.Hubs.subscribe()
+ Livebook.Hubs.subscribe([:crud, :connection])
end
socket =
@@ -18,8 +18,16 @@ defmodule LivebookWeb.SidebarHook do
{:cont, socket}
end
- defp handle_info({:hubs_metadata_changed, hubs}, socket) do
- {:halt, assign(socket, saved_hubs: hubs)}
+ @connection_events ~w(hub_connected hub_disconnected hubs_metadata_changed)a
+
+ defp handle_info(event, socket) when event in @connection_events do
+ {:halt, assign(socket, saved_hubs: Livebook.Hubs.get_metadatas())}
+ end
+
+ @error_events ~w(connection_error disconnection_error)a
+
+ defp handle_info({event, _reason}, socket) when event in @error_events do
+ {:halt, assign(socket, saved_hubs: Livebook.Hubs.get_metadatas())}
end
defp handle_info(_event, socket), do: {:cont, socket}
diff --git a/lib/livebook_web/live/hooks/user_hook.ex b/lib/livebook_web/live/hooks/user_hook.ex
index 03cb0e928..541bbe91a 100644
--- a/lib/livebook_web/live/hooks/user_hook.ex
+++ b/lib/livebook_web/live/hooks/user_hook.ex
@@ -21,7 +21,7 @@ defmodule LivebookWeb.UserHook do
{:user_change, %{id: id} = user},
%{assigns: %{current_user: %{id: id}}} = socket
) do
- {:cont, assign(socket, :current_user, user)}
+ {:halt, assign(socket, :current_user, user)}
end
defp info(_message, socket), do: {:cont, socket}
diff --git a/lib/livebook_web/live/hub/edit/enterprise_component.ex b/lib/livebook_web/live/hub/edit/enterprise_component.ex
index 9d17c03c3..06030fff8 100644
--- a/lib/livebook_web/live/hub/edit/enterprise_component.ex
+++ b/lib/livebook_web/live/hub/edit/enterprise_component.ex
@@ -1,7 +1,6 @@
defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
use LivebookWeb, :live_component
- alias Livebook.EctoTypes.HexColor
alias Livebook.Hubs.Enterprise
@impl true
@@ -35,13 +34,9 @@ defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
phx-debounce="blur"
>
- <.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
-
Color
- <.hex_color_input
- form={f}
- field={:hub_color}
- randomize={JS.push("randomize_color", target: @myself)}
- />
+ <.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
+
Emoji
+ <.emoji_input id="enterprise-emoji-input" form={f} field={:hub_emoji} />
@@ -58,10 +53,6 @@ defmodule LivebookWeb.Hub.Edit.EnterpriseComponent do
end
@impl true
- def handle_event("randomize_color", _, socket) do
- handle_event("validate", %{"enterprise" => %{"hub_color" => HexColor.random()}}, socket)
- end
-
def handle_event("save", %{"enterprise" => params}, socket) do
case Enterprise.update_hub(socket.assigns.hub, params) do
{:ok, hub} ->
diff --git a/lib/livebook_web/live/hub/edit/fly_component.ex b/lib/livebook_web/live/hub/edit/fly_component.ex
index 8cec534ea..5e227bd74 100644
--- a/lib/livebook_web/live/hub/edit/fly_component.ex
+++ b/lib/livebook_web/live/hub/edit/fly_component.ex
@@ -1,7 +1,6 @@
defmodule LivebookWeb.Hub.Edit.FlyComponent do
use LivebookWeb, :live_component
- alias Livebook.EctoTypes.HexColor
alias Livebook.Hubs.{Fly, FlyClient}
@impl true
@@ -76,13 +75,9 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do
<%= text_input(f, :hub_name, class: "input") %>
- <.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
- Color
- <.hex_color_input
- form={f}
- field={:hub_color}
- randomize={JS.push("randomize_color", target: @myself)}
- />
+ <.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
+ Emoji
+ <.emoji_input id="fly-emoji-input" form={f} field={:hub_emoji} />
@@ -134,10 +129,6 @@ defmodule LivebookWeb.Hub.Edit.FlyComponent do
end
@impl true
- def handle_event("randomize_color", _, socket) do
- handle_event("validate", %{"fly" => %{"hub_color" => HexColor.random()}}, socket)
- end
-
def handle_event("save", %{"fly" => params}, socket) do
case Fly.update_hub(socket.assigns.hub, params) do
{:ok, hub} ->
diff --git a/lib/livebook_web/live/hub/new/enterprise_component.ex b/lib/livebook_web/live/hub/new/enterprise_component.ex
index ac9860482..d04bbc211 100644
--- a/lib/livebook_web/live/hub/new/enterprise_component.ex
+++ b/lib/livebook_web/live/hub/new/enterprise_component.ex
@@ -3,13 +3,12 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
import Ecto.Changeset, only: [get_field: 2]
- alias Livebook.EctoTypes.HexColor
alias Livebook.Hubs.{Enterprise, EnterpriseClient}
@impl true
def update(assigns, socket) do
if connected?(socket) do
- EnterpriseClient.subscribe()
+ Livebook.Hubs.subscribe(:connection)
end
{:ok,
@@ -82,13 +81,9 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
<%= text_input(f, :hub_name, class: "input", readonly: true) %>
- <.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
- Color
- <.hex_color_input
- form={f}
- field={:hub_color}
- randomize={JS.push("randomize_color", target: @myself)}
- />
+ <.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
+ Emoji
+ <.emoji_input id="enterprise-emoji-input" form={f} field={:hub_emoji} />
@@ -109,20 +104,26 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
token = get_field(socket.assigns.changeset, :token)
base = %Enterprise{
+ id: "enterprise-placeholder",
token: token,
+ external_id: "placeholder",
url: url,
hub_name: "Enterprise",
- hub_color: HexColor.random()
+ hub_emoji: "🏭"
}
{:ok, pid} = EnterpriseClient.start_link(base)
receive do
- {:connect, :error, reason} ->
- GenServer.stop(pid)
- handle_error(reason, socket)
+ {:connection_error, reason} ->
+ EnterpriseClient.stop(pid)
- {:connect, :ok, :connected} ->
+ {:noreply,
+ socket
+ |> put_flash(:error, "Failed to connect with Enterprise: " <> reason)
+ |> push_patch(to: Routes.hub_path(socket, :new))}
+
+ :hub_connected ->
session_request =
LivebookProto.SessionRequest.new!(app_version: Livebook.Config.app_version())
@@ -134,16 +135,16 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
{:noreply, assign(socket, pid: pid, changeset: changeset, base: base)}
{:error, reason} ->
- GenServer.stop(pid)
- handle_error(reason, socket)
+ EnterpriseClient.stop(pid)
+
+ {:noreply,
+ socket
+ |> put_flash(:error, "Failed to connect with Enterprise: " <> reason)
+ |> push_patch(to: Routes.hub_path(socket, :new))}
end
end
end
- def handle_event("randomize_color", _, socket) do
- handle_event("validate", %{"enterprise" => %{"hub_color" => HexColor.random()}}, socket)
- end
-
def handle_event("save", %{"enterprise" => params}, socket) do
if socket.assigns.changeset.valid? do
case Enterprise.create_hub(socket.assigns.base, params) do
@@ -168,23 +169,4 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponent do
def handle_event("validate", %{"enterprise" => attrs}, socket) do
{:noreply, assign(socket, changeset: Enterprise.change_hub(socket.assigns.base, attrs))}
end
-
- def handle_error(%{reason: :econnrefused}, socket) do
- show_connect_error("Failed to connect with given URL", socket)
- end
-
- def handle_error(%{reason: _}, socket) do
- show_connect_error("Failed to connect with Enterprise", socket)
- end
-
- def handle_error(reason, socket) do
- show_connect_error(reason, socket)
- end
-
- defp show_connect_error(message, socket) do
- {:noreply,
- socket
- |> put_flash(:error, message)
- |> push_patch(to: Routes.hub_path(socket, :new))}
- end
end
diff --git a/lib/livebook_web/live/hub/new/fly_component.ex b/lib/livebook_web/live/hub/new/fly_component.ex
index b8f990ebf..2597ed15b 100644
--- a/lib/livebook_web/live/hub/new/fly_component.ex
+++ b/lib/livebook_web/live/hub/new/fly_component.ex
@@ -3,7 +3,6 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
import Ecto.Changeset, only: [get_field: 2, add_error: 3]
- alias Livebook.EctoTypes.HexColor
alias Livebook.Hubs.{Fly, FlyClient}
@impl true
@@ -60,13 +59,9 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
<%= text_input(f, :hub_name, class: "input") %>
- <.input_wrapper form={f} field={:hub_color} class="flex flex-col space-y-1">
- Color
- <.hex_color_input
- form={f}
- field={:hub_color}
- randomize={JS.push("randomize_color", target: @myself)}
- />
+ <.input_wrapper form={f} field={:hub_emoji} class="flex flex-col space-y-1">
+ Emoji
+ <.emoji_input id="fly-emoji-input" form={f} field={:hub_emoji} />
@@ -86,7 +81,7 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
case FlyClient.fetch_apps(token) do
{:ok, apps} ->
opts = select_options(apps)
- base = %Fly{access_token: token, hub_color: HexColor.random()}
+ base = %Fly{access_token: token, hub_emoji: "🚀"}
changeset = Fly.change_hub(base)
{:noreply,
@@ -103,10 +98,6 @@ defmodule LivebookWeb.Hub.New.FlyComponent do
end
end
- def handle_event("randomize_color", _, socket) do
- handle_event("validate", %{"fly" => %{"hub_color" => HexColor.random()}}, socket)
- end
-
def handle_event("save", %{"fly" => params}, socket) do
if socket.assigns.changeset.valid? do
case Fly.create_hub(socket.assigns.selected_app, params) do
diff --git a/lib/livebook_web/live/layout_helpers.ex b/lib/livebook_web/live/layout_helpers.ex
index bd31de2a8..e35926943 100644
--- a/lib/livebook_web/live/layout_helpers.ex
+++ b/lib/livebook_web/live/layout_helpers.ex
@@ -5,6 +5,7 @@ defmodule LivebookWeb.LayoutHelpers do
import LivebookWeb.UserHelpers
alias Phoenix.LiveView.JS
+ alias Livebook.Hubs.Provider
alias LivebookWeb.Router.Helpers, as: Routes
@doc """
@@ -165,15 +166,9 @@ defmodule LivebookWeb.LayoutHelpers do
end
defp sidebar_link(assigns) do
- assigns = assign_new(assigns, :icon_style, fn -> nil end)
-
~H"""
<%= live_redirect to: @to, class: "h-7 flex items-center hover:text-white #{sidebar_link_text_color(@to, @current)} border-l-4 #{sidebar_link_border_color(@to, @current)} hover:border-white" do %>
- <.remix_icon
- icon={@icon}
- class="text-lg leading-6 w-[56px] flex justify-center"
- style={@icon_style}
- />
+ <.remix_icon icon={@icon} class="text-lg leading-6 w-[56px] flex justify-center" />
<%= @title %>
@@ -181,6 +176,28 @@ defmodule LivebookWeb.LayoutHelpers do
"""
end
+ defp sidebar_hub_link(assigns) do
+ ~H"""
+ <%= live_redirect to: @to, class: "h-7 flex items-center hover:text-white #{sidebar_link_text_color(@to, @current)} border-l-4 #{sidebar_link_border_color(@to, @current)} hover:border-white" do %>
+
+
+ <%= @hub.emoji %>
+
+ <%= if Provider.connect(@hub.provider) do %>
+
+ <% end %>
+
+
+
+ <%= @hub.name %>
+
+ <% end %>
+ """
+ end
+
defp hub_section(assigns) do
~H"""
<%= if Livebook.Config.feature_flag_enabled?(:hub) do %>
@@ -191,10 +208,8 @@ defmodule LivebookWeb.LayoutHelpers do
<%= for hub <- @hubs do %>
- <.sidebar_link
- title={hub.name}
- icon="checkbox-blank-circle-fill"
- icon_style={"color: #{hub.color}"}
+ <.sidebar_hub_link
+ hub={hub}
to={Routes.hub_path(@socket, :edit, hub.id)}
current={@current_page}
/>
diff --git a/lib/livebook_web/live/session_live.ex b/lib/livebook_web/live/session_live.ex
index 1d4a3cc06..3941bed01 100644
--- a/lib/livebook_web/live/session_live.ex
+++ b/lib/livebook_web/live/session_live.ex
@@ -26,8 +26,7 @@ defmodule LivebookWeb.SessionLive do
Session.subscribe(session_id)
Secrets.subscribe()
- # TODO: Move this to Hubs.subscribe([:secrets]) and rename all "enterprise" to "hubs"
- EnterpriseClient.subscribe()
+ Hubs.subscribe(:secrets)
{data, client_id}
else
@@ -64,7 +63,7 @@ defmodule LivebookWeb.SessionLive do
autofocus_cell_id: autofocus_cell_id(data.notebook),
page_title: get_page_title(data.notebook.name),
livebook_secrets: Secrets.fetch_secrets() |> Map.new(&{&1.name, &1.value}),
- enterprise_secrets: fetch_enterprise_secrets(),
+ hub_secrets: get_hub_secrets(),
select_secret_ref: nil,
select_secret_options: nil
)
@@ -186,7 +185,7 @@ defmodule LivebookWeb.SessionLive do
<.secrets_list
data_view={@data_view}
livebook_secrets={@livebook_secrets}
- enterprise_secrets={@enterprise_secrets}
+ hub_secrets={@hub_secrets}
session={@session}
socket={@socket}
/>
@@ -739,13 +738,13 @@ defmodule LivebookWeb.SessionLive do
<%= if Livebook.Config.feature_flag_enabled?(:hub) do %>
- Enterprise secrets
+ Hub secrets
Available in all sessions
- <%= for {secret_name, secret_value} <- Enum.sort(@enterprise_secrets) do %>
+ <%= for {secret_name, secret_value} <- Enum.sort(@hub_secrets) do %>
assign(enterprise_secrets: fetch_enterprise_secrets())
+ |> assign(hub_secrets: get_hub_secrets())
|> put_flash(:info, "A new secret has been created on your Livebook Enterprise")}
end
def handle_info({:secret_updated, %Secrets.Secret{}}, socket) do
{:noreply,
socket
- |> assign(enterprise_secrets: fetch_enterprise_secrets())
+ |> assign(hub_secrets: get_hub_secrets())
|> put_flash(:info, "An existing secret has been updated on your Livebook Enterprise")}
end
@@ -2298,7 +2297,7 @@ defmodule LivebookWeb.SessionLive do
secret in secrets
end
- defp fetch_enterprise_secrets do
+ defp get_hub_secrets do
for connected_hub <- Hubs.get_connected_hubs(),
secret <- EnterpriseClient.list_cached_secrets(connected_hub.pid),
into: %{},
diff --git a/test/livebook/hubs/enterprise_client_test.exs b/test/livebook/hubs/enterprise_client_test.exs
index c801b4d76..0da621bde 100644
--- a/test/livebook/hubs/enterprise_client_test.exs
+++ b/test/livebook/hubs/enterprise_client_test.exs
@@ -6,7 +6,7 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
alias Livebook.Secrets.Secret
setup do
- EnterpriseClient.subscribe()
+ Livebook.Hubs.subscribe([:connection, :secrets])
:ok
end
@@ -15,21 +15,21 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
enterprise = build(:enterprise, url: url, token: token)
EnterpriseClient.start_link(enterprise)
- assert_receive {:connect, :ok, :connected}
+ assert_receive :hub_connected
end
test "rejects the websocket with invalid address", %{token: token} do
enterprise = build(:enterprise, url: "http://localhost:9999", token: token)
EnterpriseClient.start_link(enterprise)
- assert_receive {:connect, :error, "connection refused"}
+ assert_receive {:connection_error, "connection refused"}
end
test "rejects the web socket connection with invalid credentials", %{url: url} do
enterprise = build(:enterprise, url: url, token: "foo")
EnterpriseClient.start_link(enterprise)
- assert_receive {:connect, :error, reason}
+ assert_receive {:connection_error, reason}
assert reason =~ "the given token is invalid"
end
end
@@ -38,8 +38,7 @@ defmodule Livebook.Hubs.EnterpriseClientTest do
setup %{url: url, token: token} do
enterprise = build(:enterprise, url: url, token: token)
EnterpriseClient.start_link(enterprise)
-
- assert_receive {:connect, :ok, :connected}
+ assert_receive :hub_connected
:ok
end
diff --git a/test/livebook/hubs/provider_test.exs b/test/livebook/hubs/provider_test.exs
index 321afd4c5..c492d719b 100644
--- a/test/livebook/hubs/provider_test.exs
+++ b/test/livebook/hubs/provider_test.exs
@@ -10,7 +10,7 @@ defmodule Livebook.Hubs.ProviderTest do
assert Provider.normalize(fly) == %Metadata{
id: fly.id,
name: fly.hub_name,
- color: fly.hub_color,
+ emoji: fly.hub_emoji,
provider: fly
}
end
diff --git a/test/livebook/hubs_test.exs b/test/livebook/hubs_test.exs
index e9aeed9d0..f68a3f1d0 100644
--- a/test/livebook/hubs_test.exs
+++ b/test/livebook/hubs_test.exs
@@ -23,7 +23,7 @@ defmodule Livebook.HubsTest do
assert Hubs.get_metadatas() == [
%Hubs.Metadata{
id: "fly-livebook",
- color: fly.hub_color,
+ emoji: fly.hub_emoji,
name: fly.hub_name,
provider: fly
}
@@ -60,9 +60,9 @@ defmodule Livebook.HubsTest do
test "save_hub/1 updates hub" do
fly = insert_hub(:fly, id: "fly-foo2")
- Hubs.save_hub(%{fly | hub_color: "#FFFFFF"})
+ Hubs.save_hub(%{fly | hub_emoji: "🐈"})
refute Hubs.fetch_hub!("fly-foo2") == fly
- assert Hubs.fetch_hub!("fly-foo2").hub_color == "#FFFFFF"
+ assert Hubs.fetch_hub!("fly-foo2").hub_emoji == "🐈"
end
end
diff --git a/test/livebook_web/live/hub/edit_live_test.exs b/test/livebook_web/live/hub/edit_live_test.exs
index 3662e619f..abd3452c7 100644
--- a/test/livebook_web/live/hub/edit_live_test.exs
+++ b/test/livebook_web/live/hub/edit_live_test.exs
@@ -40,7 +40,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
attrs = %{
"hub_name" => "Personal Hub",
- "hub_color" => "#FF00FF"
+ "hub_emoji" => "🐈"
}
view
@@ -59,7 +59,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
assert render(view) =~ "Hub updated successfully"
- assert_hub(view, conn, %{hub | hub_color: attrs["hub_color"], hub_name: attrs["hub_name"]})
+ assert_hub(view, conn, %{hub | hub_emoji: attrs["hub_emoji"], hub_name: attrs["hub_name"]})
refute Hubs.fetch_hub!(hub.id) == hub
end
@@ -195,7 +195,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
hub = insert_hub(:enterprise)
{:ok, view, _html} = live(conn, Routes.hub_path(conn, :edit, hub.id))
- attrs = %{"hub_color" => "#FF00FF"}
+ attrs = %{"hub_emoji" => "🐈"}
view
|> element("#enterprise-form")
@@ -213,7 +213,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
assert render(view) =~ "Hub updated successfully"
- assert_hub(view, conn, %{hub | hub_color: attrs["hub_color"]})
+ assert_hub(view, conn, %{hub | hub_emoji: attrs["hub_emoji"]})
refute Hubs.fetch_hub!(hub.id) == hub
end
end
@@ -221,7 +221,7 @@ defmodule LivebookWeb.Hub.EditLiveTest do
defp assert_hub(view, conn, hub) do
hubs_html = view |> element("#hubs") |> render()
- assert hubs_html =~ ~s/style="color: #{hub.hub_color}"/
+ assert hubs_html =~ hub.hub_emoji
assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
assert hubs_html =~ hub.hub_name
end
diff --git a/test/livebook_web/live/hub/new/enterprise_component_test.exs b/test/livebook_web/live/hub/new/enterprise_component_test.exs
index 22c5968bf..e2a9a5f0e 100644
--- a/test/livebook_web/live/hub/new/enterprise_component_test.exs
+++ b/test/livebook_web/live/hub/new/enterprise_component_test.exs
@@ -37,7 +37,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
"url" => url,
"token" => token,
"hub_name" => "Enterprise",
- "hub_color" => "#FF00FF"
+ "hub_emoji" => "🐈"
}
view
@@ -58,7 +58,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
assert render(view) =~ "Hub added successfully"
hubs_html = view |> element("#hubs") |> render()
- assert hubs_html =~ ~s/style="color: #FF00FF"/
+ assert hubs_html =~ "🐈"
assert hubs_html =~ "/hub/enterprise-#{id}"
assert hubs_html =~ "Enterprise"
end
@@ -94,8 +94,13 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
stop_new_instance(name)
end
- test "fails to create existing hub", %{conn: conn, url: url, token: token} do
- node = EnterpriseServer.get_node()
+ test "fails to create existing hub", %{test: name, conn: conn} do
+ start_new_instance(name)
+
+ node = EnterpriseServer.get_node(name)
+ url = EnterpriseServer.url(name)
+ token = EnterpriseServer.token(name)
+
id = :erpc.call(node, Enterprise.Integration, :fetch_env!, [])
user = :erpc.call(node, Enterprise.Integration, :create_user, [])
@@ -135,7 +140,7 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
"url" => url,
"token" => token,
"hub_name" => "Enterprise",
- "hub_color" => "#FFFFFF"
+ "hub_emoji" => "🐈"
}
view
@@ -151,11 +156,13 @@ defmodule LivebookWeb.Hub.New.EnterpriseComponentTest do
|> render_submit(%{"enterprise" => attrs}) =~ "already exists"
hubs_html = view |> element("#hubs") |> render()
- assert hubs_html =~ ~s/style="color: #{hub.hub_color}"/
+ assert hubs_html =~ hub.hub_emoji
assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
assert hubs_html =~ hub.hub_name
assert Hubs.fetch_hub!(hub.id) == hub
+ after
+ stop_new_instance(name)
end
end
diff --git a/test/livebook_web/live/hub/new_live_test.exs b/test/livebook_web/live/hub/new_live_test.exs
index 45adc3b58..8c0c20e47 100644
--- a/test/livebook_web/live/hub/new_live_test.exs
+++ b/test/livebook_web/live/hub/new_live_test.exs
@@ -36,7 +36,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
"access_token" => "dummy access token",
"application_id" => "123456789",
"hub_name" => "My Foo Hub",
- "hub_color" => "#FF00FF"
+ "hub_emoji" => "🐈"
}
view
@@ -55,17 +55,11 @@ defmodule LivebookWeb.Hub.NewLiveTest do
assert render(view) =~ "Hub added successfully"
- assert view
- |> element("#hubs")
- |> render() =~ ~s/style="color: #FF00FF"/
+ hubs_html = view |> element("#hubs") |> render()
- assert view
- |> element("#hubs")
- |> render() =~ "/hub/fly-123456789"
-
- assert view
- |> element("#hubs")
- |> render() =~ "My Foo Hub"
+ assert hubs_html =~ "🐈"
+ assert hubs_html =~ "/hub/fly-123456789"
+ assert hubs_html =~ "My Foo Hub"
end
test "fails to create existing hub", %{conn: conn} do
@@ -87,7 +81,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
"access_token" => "dummy access token",
"application_id" => "foo",
"hub_name" => "My Foo Hub",
- "hub_color" => "#FF00FF"
+ "hub_emoji" => "🐈"
}
view
@@ -102,18 +96,7 @@ defmodule LivebookWeb.Hub.NewLiveTest do
|> element("#fly-form")
|> render_submit(%{"fly" => attrs}) =~ "already exists"
- assert view
- |> element("#hubs")
- |> render() =~ ~s/style="color: #{hub.hub_color}"/
-
- assert view
- |> element("#hubs")
- |> render() =~ Routes.hub_path(conn, :edit, hub.id)
-
- assert view
- |> element("#hubs")
- |> render() =~ hub.hub_name
-
+ assert_hub(view, conn, hub)
assert Hubs.fetch_hub!(hub.id) == hub
end
end
@@ -177,4 +160,12 @@ defmodule LivebookWeb.Hub.NewLiveTest do
%{"data" => %{"app" => app}}
end
+
+ defp assert_hub(view, conn, hub) do
+ hubs_html = view |> element("#hubs") |> render()
+
+ assert hubs_html =~ hub.hub_emoji
+ assert hubs_html =~ Routes.hub_path(conn, :edit, hub.id)
+ assert hubs_html =~ hub.hub_name
+ end
end
diff --git a/test/livebook_web/live/session_live/secrets_component_test.exs b/test/livebook_web/live/session_live/secrets_component_test.exs
index 90a8adc03..a3179a8f8 100644
--- a/test/livebook_web/live/session_live/secrets_component_test.exs
+++ b/test/livebook_web/live/session_live/secrets_component_test.exs
@@ -9,7 +9,8 @@ defmodule LivebookWeb.SessionLive.SecretsComponentTest do
describe "enterprise" do
setup %{url: url, token: token} do
- id = Livebook.Utils.random_id()
+ node = EnterpriseServer.get_node()
+ id = :erpc.call(node, Enterprise.Integration, :fetch_env!, [])
Livebook.Hubs.delete_hub("enterprise-#{id}")
enterprise =
@@ -21,8 +22,7 @@ defmodule LivebookWeb.SessionLive.SecretsComponentTest do
)
{:ok, session} = Sessions.create_session(notebook: Livebook.Notebook.new())
- Livebook.Hubs.EnterpriseClient.subscribe()
- Livebook.Hubs.connect_hubs()
+ Livebook.Hubs.subscribe(:secrets)
on_exit(fn ->
Session.close(session.pid)
diff --git a/test/support/factory.ex b/test/support/factory.ex
index 94c5950b2..1a117d847 100644
--- a/test/support/factory.ex
+++ b/test/support/factory.ex
@@ -17,7 +17,7 @@ defmodule Livebook.Factory do
%Livebook.Hubs.Fly{
id: "fly-foo-bar-baz",
hub_name: "My Personal Hub",
- hub_color: "#FF00FF",
+ hub_emoji: "🚀",
access_token: Livebook.Utils.random_cookie(),
organization_id: Livebook.Utils.random_id(),
organization_type: "PERSONAL",
@@ -36,7 +36,7 @@ defmodule Livebook.Factory do
%Livebook.Hubs.Enterprise{
id: "enterprise-#{id}",
hub_name: "Enterprise",
- hub_color: "#FF0000",
+ hub_emoji: "🏭",
external_id: id,
token: Livebook.Utils.random_cookie(),
url: "http://localhost"