mirror of
https://github.com/livebook-dev/livebook.git
synced 2025-10-18 01:16:29 +08:00
Allow users to select the notebook's hub (#1744)
This commit is contained in:
parent
dd5e1af23b
commit
e73af96b86
4 changed files with 142 additions and 51 deletions
|
@ -577,6 +577,14 @@ defmodule Livebook.Session do
|
||||||
GenServer.cast(pid, {:unset_secret, self(), secret_name})
|
GenServer.cast(pid, {:unset_secret, self(), secret_name})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Sends a hub selection to the server.
|
||||||
|
"""
|
||||||
|
@spec set_notebook_hub(pid(), String.t()) :: :ok
|
||||||
|
def set_notebook_hub(pid, id) do
|
||||||
|
GenServer.cast(pid, {:set_notebook_hub, self(), id})
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Sends save request to the server.
|
Sends save request to the server.
|
||||||
|
|
||||||
|
@ -1238,6 +1246,12 @@ defmodule Livebook.Session do
|
||||||
{:noreply, handle_operation(state, operation)}
|
{:noreply, handle_operation(state, operation)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_cast({:set_notebook_hub, client_pid, id}, state) do
|
||||||
|
client_id = client_id(state, client_pid)
|
||||||
|
operation = {:set_notebook_hub, client_id, id}
|
||||||
|
{:noreply, handle_operation(state, operation)}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:DOWN, ref, :process, _, reason}, %{runtime_monitor_ref: ref} = state) do
|
def handle_info({:DOWN, ref, :process, _, reason}, %{runtime_monitor_ref: ref} = state) do
|
||||||
broadcast_error(
|
broadcast_error(
|
||||||
|
@ -1805,6 +1819,10 @@ defmodule Livebook.Session do
|
||||||
notify_update(state)
|
notify_update(state)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp after_operation(state, _prev_state, {:set_notebook_hub, _client_id, _id}) do
|
||||||
|
notify_update(state)
|
||||||
|
end
|
||||||
|
|
||||||
defp after_operation(state, _prev_state, _operation), do: state
|
defp after_operation(state, _prev_state, _operation), do: state
|
||||||
|
|
||||||
defp handle_actions(state, actions) do
|
defp handle_actions(state, actions) do
|
||||||
|
|
|
@ -34,10 +34,12 @@ defmodule Livebook.Session.Data do
|
||||||
:secrets,
|
:secrets,
|
||||||
:mode,
|
:mode,
|
||||||
:apps,
|
:apps,
|
||||||
:app_data
|
:app_data,
|
||||||
|
:hub
|
||||||
]
|
]
|
||||||
|
|
||||||
alias Livebook.{Notebook, Delta, Runtime, JSInterop, FileSystem}
|
alias Livebook.{Notebook, Delta, Runtime, JSInterop, FileSystem}
|
||||||
|
alias Livebook.Hubs.Provider
|
||||||
alias Livebook.Users.User
|
alias Livebook.Users.User
|
||||||
alias Livebook.Notebook.{Cell, Section, AppSettings}
|
alias Livebook.Notebook.{Cell, Section, AppSettings}
|
||||||
alias Livebook.Utils.Graph
|
alias Livebook.Utils.Graph
|
||||||
|
@ -58,7 +60,8 @@ defmodule Livebook.Session.Data do
|
||||||
secrets: %{(name :: String.t()) => value :: String.t()},
|
secrets: %{(name :: String.t()) => value :: String.t()},
|
||||||
mode: session_mode(),
|
mode: session_mode(),
|
||||||
apps: list(app()),
|
apps: list(app()),
|
||||||
app_data: nil | app_data()
|
app_data: nil | app_data(),
|
||||||
|
hub: Provider.t()
|
||||||
}
|
}
|
||||||
|
|
||||||
@type section_info :: %{
|
@type section_info :: %{
|
||||||
|
@ -218,6 +221,7 @@ defmodule Livebook.Session.Data do
|
||||||
| {:delete_app, client_id(), Livebook.Session.id()}
|
| {:delete_app, client_id(), Livebook.Session.id()}
|
||||||
| {:app_unregistered, client_id()}
|
| {:app_unregistered, client_id()}
|
||||||
| {:app_stop, client_id()}
|
| {:app_stop, client_id()}
|
||||||
|
| {:set_notebook_hub, client_id(), String.t()}
|
||||||
|
|
||||||
@type action ::
|
@type action ::
|
||||||
:connect_runtime
|
:connect_runtime
|
||||||
|
@ -273,7 +277,8 @@ defmodule Livebook.Session.Data do
|
||||||
secrets: %{},
|
secrets: %{},
|
||||||
mode: opts[:mode],
|
mode: opts[:mode],
|
||||||
apps: [],
|
apps: [],
|
||||||
app_data: app_data
|
app_data: app_data,
|
||||||
|
hub: Livebook.Hubs.fetch_hub!("personal-hub")
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
data
|
||||||
|
@ -907,6 +912,13 @@ defmodule Livebook.Session.Data do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def apply_operation(data, {:set_notebook_hub, _client_id, id}) do
|
||||||
|
data
|
||||||
|
|> with_actions()
|
||||||
|
|> set_notebook_hub(id)
|
||||||
|
|> wrap_ok()
|
||||||
|
end
|
||||||
|
|
||||||
# ===
|
# ===
|
||||||
|
|
||||||
defp with_actions(data, actions \\ []), do: {data, actions}
|
defp with_actions(data, actions \\ []), do: {data, actions}
|
||||||
|
@ -1823,6 +1835,11 @@ defmodule Livebook.Session.Data do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp set_notebook_hub(data_actions, id) do
|
||||||
|
hub = Livebook.Hubs.fetch_hub!(id)
|
||||||
|
set!(data_actions, hub: hub)
|
||||||
|
end
|
||||||
|
|
||||||
defp set_smart_cell_definitions(data_actions, smart_cell_definitions) do
|
defp set_smart_cell_definitions(data_actions, smart_cell_definitions) do
|
||||||
data_actions
|
data_actions
|
||||||
|> set!(smart_cell_definitions: smart_cell_definitions)
|
|> set!(smart_cell_definitions: smart_cell_definitions)
|
||||||
|
|
|
@ -228,11 +228,11 @@ defmodule LivebookWeb.SessionLive do
|
||||||
global_status={@data_view.global_status}
|
global_status={@data_view.global_status}
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="w-full max-w-screen-lg px-4 sm:pl-8 sm:pr-16 md:pl-16 pt-4 sm:py-5 mx-auto"
|
class="relative w-full max-w-screen-lg px-4 sm:pl-8 sm:pr-16 md:pl-16 pt-4 sm:py-5 mx-auto"
|
||||||
data-el-notebook-content
|
data-el-notebook-content
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="flex items-center pb-4 mb-2 space-x-4 border-b border-gray-200"
|
class="flex flex-wrap items-center pb-4 mb-2 border-b border-gray-200"
|
||||||
data-el-notebook-headline
|
data-el-notebook-headline
|
||||||
data-focusable-id="notebook"
|
data-focusable-id="notebook"
|
||||||
id="notebook"
|
id="notebook"
|
||||||
|
@ -240,51 +240,81 @@ defmodule LivebookWeb.SessionLive do
|
||||||
data-on-value-change="set_notebook_name"
|
data-on-value-change="set_notebook_name"
|
||||||
data-metadata="notebook"
|
data-metadata="notebook"
|
||||||
>
|
>
|
||||||
<h1
|
<div class="flex grow">
|
||||||
class="grow p-1 -ml-1 text-3xl font-semibold text-gray-800 border border-transparent rounded-lg whitespace-pre-wrap"
|
<h1
|
||||||
tabindex="0"
|
class="p-1 text-3xl font-semibold text-gray-800 border border-transparent rounded-lg whitespace-pre-wrap"
|
||||||
id="notebook-heading"
|
tabindex="0"
|
||||||
data-el-heading
|
id="notebook-heading"
|
||||||
spellcheck="false"
|
data-el-heading
|
||||||
phx-no-format
|
spellcheck="false"
|
||||||
><%= @data_view.notebook_name %></h1>
|
phx-no-format
|
||||||
<.menu id="session-menu">
|
><%= @data_view.notebook_name %></h1>
|
||||||
<:toggle>
|
</div>
|
||||||
<button class="icon-button" aria-label="open notebook menu">
|
|
||||||
<.remix_icon icon="more-2-fill" class="text-xl" />
|
<div class="flex justify-between items-center pt-1 w-full md:w-auto md:justify-inherit md:items-baseline">
|
||||||
</button>
|
<div class="md:absolute md:left-0">
|
||||||
</:toggle>
|
<.menu position={:bottom_left} id="notebook-hub-menu">
|
||||||
<.menu_item>
|
<:toggle>
|
||||||
<.link patch={~p"/sessions/#{@session.id}/export/livemd"} role="menuitem">
|
<span class="tooltip right distant relative" data-tooltip="Select one hub">
|
||||||
<.remix_icon icon="download-2-line" />
|
<div class="border-2 rounded-full border-gray-200 hover:border-gray-400 focus:border-gray-400 w-[40px] h-[40px] flex items-center justify-center">
|
||||||
<span>Export</span>
|
<span aria-label={@data_view.notebook_hub.hub_name}>
|
||||||
</.link>
|
<%= @data_view.notebook_hub.hub_emoji %>
|
||||||
</.menu_item>
|
</span>
|
||||||
<.menu_item>
|
</div>
|
||||||
<button role="menuitem" phx-click="erase_outputs">
|
</span>
|
||||||
<.remix_icon icon="eraser-fill" />
|
</:toggle>
|
||||||
<span>Erase outputs</span>
|
<.menu_item :for={hub <- @saved_hubs}>
|
||||||
</button>
|
<button
|
||||||
</.menu_item>
|
id={"select-hub-#{hub.id}"}
|
||||||
<.menu_item>
|
phx-click={JS.push("select_hub", value: %{id: hub.id})}
|
||||||
<button role="menuitem" phx-click="fork_session">
|
aria-label={hub.name}
|
||||||
<.remix_icon icon="git-branch-line" />
|
role="menuitem"
|
||||||
<span>Fork</span>
|
>
|
||||||
</button>
|
<%= hub.emoji %>
|
||||||
</.menu_item>
|
<span class="ml-2"><%= hub.name %></span>
|
||||||
<.menu_item>
|
</button>
|
||||||
<a role="menuitem" href={live_dashboard_process_path(@session.pid)} target="_blank">
|
</.menu_item>
|
||||||
<.remix_icon icon="dashboard-2-line" />
|
</.menu>
|
||||||
<span>See on Dashboard</span>
|
</div>
|
||||||
</a>
|
|
||||||
</.menu_item>
|
<.menu id="session-menu">
|
||||||
<.menu_item variant={:danger}>
|
<:toggle>
|
||||||
<.link navigate={~p"/home/sessions/#{@session.id}/close"} role="menuitem">
|
<button class="icon-button" aria-label="open notebook menu">
|
||||||
<.remix_icon icon="close-circle-line" />
|
<.remix_icon icon="more-2-fill" class="text-xl" />
|
||||||
<span>Close</span>
|
</button>
|
||||||
</.link>
|
</:toggle>
|
||||||
</.menu_item>
|
<.menu_item>
|
||||||
</.menu>
|
<.link patch={~p"/sessions/#{@session.id}/export/livemd"} role="menuitem">
|
||||||
|
<.remix_icon icon="download-2-line" />
|
||||||
|
<span>Export</span>
|
||||||
|
</.link>
|
||||||
|
</.menu_item>
|
||||||
|
<.menu_item>
|
||||||
|
<button role="menuitem" phx-click="erase_outputs">
|
||||||
|
<.remix_icon icon="eraser-fill" />
|
||||||
|
<span>Erase outputs</span>
|
||||||
|
</button>
|
||||||
|
</.menu_item>
|
||||||
|
<.menu_item>
|
||||||
|
<button role="menuitem" phx-click="fork_session">
|
||||||
|
<.remix_icon icon="git-branch-line" />
|
||||||
|
<span>Fork</span>
|
||||||
|
</button>
|
||||||
|
</.menu_item>
|
||||||
|
<.menu_item>
|
||||||
|
<a role="menuitem" href={live_dashboard_process_path(@session.pid)} target="_blank">
|
||||||
|
<.remix_icon icon="dashboard-2-line" />
|
||||||
|
<span>See on Dashboard</span>
|
||||||
|
</a>
|
||||||
|
</.menu_item>
|
||||||
|
<.menu_item variant={:danger}>
|
||||||
|
<.link navigate={~p"/home/sessions/#{@session.id}/close"} role="menuitem">
|
||||||
|
<.remix_icon icon="close-circle-line" />
|
||||||
|
<span>Close</span>
|
||||||
|
</.link>
|
||||||
|
</.menu_item>
|
||||||
|
</.menu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<.live_component
|
<.live_component
|
||||||
|
@ -1158,6 +1188,12 @@ defmodule LivebookWeb.SessionLive do
|
||||||
)}
|
)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("select_hub", %{"id" => id}, socket) do
|
||||||
|
Session.set_notebook_hub(socket.assigns.session.pid, id)
|
||||||
|
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_info({:operation, operation}, socket) do
|
def handle_info({:operation, operation}, socket) do
|
||||||
{:noreply, handle_operation(socket, operation)}
|
{:noreply, handle_operation(socket, operation)}
|
||||||
|
@ -1764,7 +1800,8 @@ defmodule LivebookWeb.SessionLive do
|
||||||
secrets: data.secrets,
|
secrets: data.secrets,
|
||||||
apps_status: apps_status(data),
|
apps_status: apps_status(data),
|
||||||
app_settings: data.notebook.app_settings,
|
app_settings: data.notebook.app_settings,
|
||||||
apps: data.apps
|
apps: data.apps,
|
||||||
|
notebook_hub: data.hub
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1448,4 +1448,23 @@ defmodule LivebookWeb.SessionLiveTest do
|
||||||
refute render(view) =~ "/apps/#{slug}"
|
refute render(view) =~ "/apps/#{slug}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "hubs" do
|
||||||
|
test "selects the notebook hub", %{conn: conn, session: session} do
|
||||||
|
hub = insert_hub(:fly)
|
||||||
|
id = hub.id
|
||||||
|
|
||||||
|
Session.subscribe(session.id)
|
||||||
|
{:ok, view, _} = live(conn, ~p"/sessions/#{session.id}")
|
||||||
|
|
||||||
|
assert %Livebook.Hubs.Personal{id: "personal-hub"} = Session.get_data(session.pid).hub
|
||||||
|
|
||||||
|
view
|
||||||
|
|> element(~s/#select-hub-#{id}/)
|
||||||
|
|> render_click()
|
||||||
|
|
||||||
|
assert_receive {:operation, {:set_notebook_hub, _, ^id}}
|
||||||
|
assert Session.get_data(session.pid).hub == hub
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Reference in a new issue