Add support for anchor links during navigation (#1327)

This commit is contained in:
Alexander Koutmos 2022-08-04 15:24:19 -04:00 committed by GitHub
parent 6853bbd9a1
commit e8920257cd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 49 additions and 13 deletions

View file

@ -1,5 +1,8 @@
defmodule LivebookWeb.SessionHelpers do defmodule LivebookWeb.SessionHelpers do
import Phoenix.LiveView import Phoenix.LiveView
alias Phoenix.LiveView.Socket
alias Livebook.Session
alias LivebookWeb.Router.Helpers, as: Routes alias LivebookWeb.Router.Helpers, as: Routes
@doc """ @doc """
@ -8,7 +11,7 @@ defmodule LivebookWeb.SessionHelpers do
Accepts the same options as `Livebook.Sessions.create_session/1`. Accepts the same options as `Livebook.Sessions.create_session/1`.
""" """
@spec create_session(Phoenix.LiveView.Socket.t(), keyword()) :: Phoenix.LiveView.Socket.t() @spec create_session(Socket.t(), keyword()) :: Socket.t()
def create_session(socket, opts \\ []) do def create_session(socket, opts \\ []) do
# Revert persistence options to default values if there is # Revert persistence options to default values if there is
# no file attached to the new session # no file attached to the new session
@ -21,19 +24,36 @@ defmodule LivebookWeb.SessionHelpers do
case Livebook.Sessions.create_session(opts) do case Livebook.Sessions.create_session(opts) do
{:ok, session} -> {:ok, session} ->
push_redirect(socket, to: Routes.session_path(socket, :page, session.id)) redirect_path = session_path(socket, session.id, opts)
push_redirect(socket, to: redirect_path)
{:error, reason} -> {:error, reason} ->
put_flash(socket, :error, "Failed to create session: #{reason}") put_flash(socket, :error, "Failed to create session: #{reason}")
end end
end end
@doc """
Generate the session path based on the provided options.
"""
@spec session_path(Socket.t(), Session.id(), keyword()) :: String.t()
def session_path(socket, session_id, opts \\ []) do
socket
|> Routes.session_path(:page, session_id)
|> maybe_add_url_hash(opts)
end
defp maybe_add_url_hash(redirect_path, opts) do
case opts[:url_hash] do
nil -> redirect_path
url_hash -> "#{redirect_path}##{url_hash}"
end
end
@doc """ @doc """
Formats the given list of notebook import messages and puts Formats the given list of notebook import messages and puts
into the warning flash. into the warning flash.
""" """
@spec put_import_warnings(Phoenix.LiveView.Socket.t(), list(String.t())) :: @spec put_import_warnings(Socket.t(), list(String.t())) :: Socket.t()
Phoenix.LiveView.Socket.t()
def put_import_warnings(socket, messages) def put_import_warnings(socket, messages)
def put_import_warnings(socket, []), do: socket def put_import_warnings(socket, []), do: socket

View file

@ -674,7 +674,7 @@ defmodule LivebookWeb.SessionLive do
def handle_params( def handle_params(
%{"path_parts" => path_parts}, %{"path_parts" => path_parts},
_url, requested_url,
%{assigns: %{live_action: :catch_all}} = socket %{assigns: %{live_action: :catch_all}} = socket
) do ) do
if socket.assigns.policy.edit do if socket.assigns.policy.edit do
@ -685,7 +685,7 @@ defmodule LivebookWeb.SessionLive do
end) end)
path = Path.join(path_parts) path = Path.join(path_parts)
{:noreply, handle_relative_path(socket, path)} {:noreply, handle_relative_path(socket, path, requested_url)}
else else
{:noreply, socket |> put_flash(:error, "No access to navigate") |> redirect_to_self()} {:noreply, socket |> put_flash(:error, "No access to navigate") |> redirect_to_self()}
end end
@ -1130,10 +1130,10 @@ defmodule LivebookWeb.SessionLive do
def handle_info(_message, socket), do: {:noreply, socket} def handle_info(_message, socket), do: {:noreply, socket}
defp handle_relative_path(socket, path) do defp handle_relative_path(socket, path, requested_url) do
cond do cond do
String.ends_with?(path, LiveMarkdown.extension()) -> String.ends_with?(path, LiveMarkdown.extension()) ->
handle_relative_notebook_path(socket, path) handle_relative_notebook_path(socket, path, requested_url)
true -> true ->
socket socket
@ -1145,7 +1145,7 @@ defmodule LivebookWeb.SessionLive do
end end
end end
defp handle_relative_notebook_path(socket, relative_path) do defp handle_relative_notebook_path(socket, relative_path, requested_url) do
resolution_location = location(socket.private.data) resolution_location = location(socket.private.data)
case resolution_location do case resolution_location do
@ -1162,10 +1162,13 @@ defmodule LivebookWeb.SessionLive do
case session_id_by_location(origin) do case session_id_by_location(origin) do
{:ok, session_id} -> {:ok, session_id} ->
push_redirect(socket, to: Routes.session_path(socket, :page, session_id)) redirect_path =
session_path(socket, session_id, url_hash: get_url_hash(requested_url))
push_redirect(socket, to: redirect_path)
{:error, :none} -> {:error, :none} ->
open_notebook(socket, origin) open_notebook(socket, origin, requested_url)
{:error, :many} -> {:error, :many} ->
origin_str = origin_str =
@ -1188,7 +1191,14 @@ defmodule LivebookWeb.SessionLive do
defp location(%{file: file}) when is_map(file), do: {:file, file} defp location(%{file: file}) when is_map(file), do: {:file, file}
defp location(%{origin: origin}), do: origin defp location(%{origin: origin}), do: origin
defp open_notebook(socket, origin) do defp get_url_hash(requested_url) do
case String.split(requested_url, "#") do
[_, url_hash] -> url_hash
_ -> nil
end
end
defp open_notebook(socket, origin, requested_url) do
case Notebook.ContentLoader.fetch_content_from_location(origin) do case Notebook.ContentLoader.fetch_content_from_location(origin) do
{:ok, content} -> {:ok, content} ->
{notebook, messages} = Livebook.LiveMarkdown.notebook_from_livemd(content) {notebook, messages} = Livebook.LiveMarkdown.notebook_from_livemd(content)
@ -1196,10 +1206,16 @@ defmodule LivebookWeb.SessionLive do
# If the current session has no path, fork the notebook # If the current session has no path, fork the notebook
fork? = socket.private.data.file == nil fork? = socket.private.data.file == nil
{file, notebook} = file_and_notebook(fork?, origin, notebook) {file, notebook} = file_and_notebook(fork?, origin, notebook)
url_hash = get_url_hash(requested_url)
socket socket
|> put_import_warnings(messages) |> put_import_warnings(messages)
|> create_session(notebook: notebook, origin: origin, file: file) |> create_session(
notebook: notebook,
origin: origin,
file: file,
url_hash: url_hash
)
{:error, message} -> {:error, message} ->
socket socket