improvement: don't load data, don't check library versions

This commit is contained in:
Zach Daniel 2023-09-27 02:26:16 -04:00
parent 3bf97311a5
commit 20f1e277da
13 changed files with 79 additions and 975 deletions

View file

@ -270,16 +270,6 @@ window.addEventListener("phx:js:scroll-to", (e) => {
});
});
window.addEventListener("phx:selected-versions", (e) => {
if (cookiesAreAllowed()) {
const cookie = Object.keys(e.detail)
.map((key) => `${key}:${e.detail[key]}`)
.join(",");
setCookie("selected_versions", cookie)
}
});
window.addEventListener("phx:selected-types", (e) => {
if (cookiesAreAllowed()) {
const cookie = e.detail.types.join(",");
@ -305,12 +295,6 @@ window.addEventListener("keydown", (event) => {
}
});
window.addEventListener("phx:catalogue-call-to-action-dismissed", (event) => {
if (cookiesAreAllowed()) {
setCookie("catalogue_call_to_action_dismissed", "true")
}
});
window.addEventListener("phx:click-on-item", (event) => {
document.getElementById(event.detail.id).click();
document.getElementById("close-search").click();

View file

@ -328,24 +328,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
)
{arguments, filter} =
if Ash.Resource.Info.attribute(dsl_state, :library_version_id) do
{[
Transformer.build_entity!(
Ash.Resource.Dsl,
[:actions, :read],
:argument,
type: {:array, :uuid},
name: :library_versions
),
query_argument
],
Ash.Query.expr(
matches(query: arg(:query)) and
^ref(config.library_version_attribute) in ^arg(:library_versions)
)}
else
{[query_argument], Ash.Query.expr(matches(query: arg(:query)))}
end
Transformer.add_entity(
dsl_state,
@ -365,7 +348,7 @@ defmodule AshHq.Docs.Extensions.Search.Transformers.AddSearchStructure do
[:code_interface],
Transformer.build_entity!(Ash.Resource.Dsl, [:code_interface], :define,
name: :search,
args: [:query, :library_versions]
args: [:query]
)
)
end

View file

@ -26,10 +26,6 @@ defmodule AshHq.Docs.Search do
constraints trim?: false, allow_empty?: true
end
argument :library_versions, {:array, :uuid} do
allow_nil? false
end
argument :types, {:array, :string}
returns :build_results
@ -49,7 +45,6 @@ defmodule AshHq.Docs.Search do
read :search, element(:search_results), :search do
input %{
library_versions: arg(:library_versions),
query: arg(:query)
}
end

View file

@ -1,82 +0,0 @@
defmodule AshHqWeb.Components.Catalogue do
@moduledoc "Renders the catalogue of available packages"
use Surface.Component
alias Surface.Components.Form
alias Surface.Components.Form.Select
prop(id, :string, required: true)
prop(libraries, :list, required: true)
prop(selected_versions, :map, required: true)
prop(change_versions, :event, required: true)
prop(toggle, :any)
prop(show_catalogue_call_to_action, :boolean, default: false)
def render(assigns) do
~F"""
<div id={@id} class="w-full h-full overflow-y-auto p-6">
<div class="w-full flex justify-between">
<p class="text-xl font-bold">Select Ash libraries</p>
{#if @toggle}
<button id="close-search-versions" phx-click={@toggle} class="">
<Heroicons.Solid.XIcon class="h-6 w-6" />
</button>
{/if}
</div>
<div
:if={@show_catalogue_call_to_action}
class="text-sm my-2 text-base-light-700 dark:text-base-dark-50"
>
<Heroicons.Outline.ExclamationCircleIcon class="h-4 w-4 inline-block mb-0.5" />
You can see this screen at any time by pressing <Heroicons.Solid.PlusIcon class="h-4 w-4 inline-block mb-0.5" /> next to the list of included packages in the sidebar
</div>
<Form for={:selected_versions} change={@change_versions} class="lg:grid lg:grid-cols-2">
{#for library <- Enum.sort_by(@libraries, & &1.order)}
<div class="py-6 lg:p-6 lg:even:pl-0 lg:odd:pr-0 flex-grow border-b border-base-light-300 dark:border-base-dark-600">
<div class="flex justify-between items-center mb-1">
<div class={[
"font-bold text-lg",
"text-primary-light-600 dark:text-primary-dark-400": value(@selected_versions, library.id),
"text-base-light-500 dark:text-base-dark-300": !value(@selected_versions, library.id)
]}>{library.display_name}</div>
<Select
class="rounded-lg text-black dark:bg-base-dark-700 dark:text-white dark:border-0"
name={"versions[#{library.id}]"}
id={"#{@id}-selected_versions-#{library.id}"}
selected={value(@selected_versions, library.id)}
options={[{"hidden", nil}] ++ formatted_versions(library.versions)}
/>
</div>
{library.description}
</div>
{/for}
</Form>
<p class="mt-6">... and more coming soon! <img class="h-6 w-6 inline-block" src="/images/ash-logo.png"></p>
</div>
"""
end
defp formatted_versions(versions) do
versions
|> Enum.sort(&(Version.compare(&1.version, &2.version) != :lt))
|> Enum.map(fn version ->
{version.version, version.id}
end)
|> case do
[{latest, _} | rest] ->
[{"#{latest} (latest)", "latest"} | rest]
other ->
other
end
end
defp value(selected_versions, library_id) do
case selected_versions[library_id] do
nil -> nil
"" -> nil
value -> value
end
end
end

View file

@ -1,35 +0,0 @@
defmodule AshHqWeb.Components.CatalogueModal do
@moduledoc "The library catalogue modal"
use Surface.Component
alias AshHqWeb.Components.Catalogue
prop(id, :string, required: true)
prop(libraries, :list, required: true)
prop(selected_versions, :map, required: true)
prop(change_versions, :event, required: true)
prop(show_catalogue_call_to_action, :boolean)
def render(assigns) do
~F"""
<div
id={@id}
style="display: none;"
class="fixed flex justify-center align-middle w-screen h-full backdrop-blur-sm bg-white bg-opacity-10 z-50"
>
<div
:on-click-away={AshHqWeb.AppViewLive.toggle_catalogue()}
class="dark:text-white absolute rounded-xl left-1/2 top-1/2 transform -translate-x-1/2 -translate-y-1/2 w-3/4 max-w-[1200px] h-3/4 bg-white dark:bg-base-dark-850 border-2 dark:border-base-dark-900"
>
<Catalogue
id={"#{@id}-contents"}
libraries={@libraries}
selected_versions={@selected_versions}
change_versions={@change_versions}
show_catalogue_call_to_action={@show_catalogue_call_to_action}
/>
</div>
</div>
"""
end
end

View file

@ -10,7 +10,6 @@ defmodule AshHqWeb.Components.DocSidebar do
prop libraries, :any
prop remove_version, :any
prop selected_versions, :any
prop class, :any
prop sidebar_data, :any, default: []

View file

@ -10,7 +10,6 @@ defmodule AshHqWeb.Components.Docs.Functions do
prop(library, :any, required: true)
prop(library_version, :any, required: true)
prop(libraries, :list, required: true)
prop(selected_versions, :map, required: true)
def render(assigns) do
~F"""

View file

@ -4,7 +4,7 @@ defmodule AshHqWeb.Components.Search do
require Ash.Query
alias AshHqWeb.Components.{Catalogue, Icon}
alias AshHqWeb.Components.Icon
alias AshHqWeb.DocRoutes
alias Phoenix.LiveView.JS
alias Surface.Components.{Form, LivePatch}
@ -12,7 +12,6 @@ defmodule AshHqWeb.Components.Search do
prop(close, :event, required: true)
prop(libraries, :list, required: true)
prop(selected_versions, :map, required: true)
prop(selected_types, :list, required: true)
prop(change_types, :event, required: true)
prop(change_versions, :event, required: true)
@ -37,15 +36,6 @@ defmodule AshHqWeb.Components.Search do
:on-window-keydown="select-previous"
phx-key="ArrowUp"
>
<div class="h-full w-full" id="search-versions" style="display: none;">
<Catalogue
id="search-versions-contents"
toggle={toggle_libraries()}
libraries={@libraries}
selected_versions={@selected_versions}
change_versions={@change_versions}
/>
</div>
<div id="search-body" class="h-full" :on-window-keydown="select-next" phx-key="ArrowDown">
<div class="p-6 h-full grid gap-6 grid-rows-[max-content_auto_max-content]">
<button
@ -109,7 +99,7 @@ defmodule AshHqWeb.Components.Search do
{#if item.__struct__ != AshHq.Docs.Guide}
<a
class="block w-full text-left border-base-light-300 dark:border-base-dark-600"
href={DocRoutes.doc_link(item, @selected_versions)}
href={DocRoutes.doc_link(item)}
id={"result-#{item.id}"}
phx-click={@close}
>
@ -145,7 +135,7 @@ defmodule AshHqWeb.Components.Search do
{#if item.__struct__ == AshHq.Docs.Guide}
<LivePatch
class="block w-full text-left border-base-light-300 dark:border-base-dark-600"
to={DocRoutes.doc_link(item, @selected_versions)}
to={DocRoutes.doc_link(item)}
opts={id: "result-#{item.id}", "phx-click": @close}
>
<div class={
@ -287,34 +277,12 @@ defmodule AshHqWeb.Components.Search do
end
defp search(socket) do
if socket.assigns[:search] in [nil, ""] || socket.assigns[:selected_versions] in [nil, %{}] ||
socket.assigns[:selected_types] == [] do
assign(socket, results: %{}, item_list: [])
if socket.assigns.search in [nil, ""] do
socket
else
versions =
Enum.map(socket.assigns.selected_versions, fn
{library_id, "latest"} ->
Enum.find_value(socket.assigns.libraries, fn library ->
if library.id == library_id do
case AshHqWeb.Helpers.latest_version(library) do
nil ->
nil
version ->
version.id
end
end
end)
{_, version_id} ->
version_id
end)
|> Enum.reject(&(&1 == "" || is_nil(&1)))
%{result: item_list} =
AshHq.Docs.Search.run!(
socket.assigns.search,
versions,
%{types: socket.assigns[:selected_types]}
)

View file

@ -1,70 +0,0 @@
defmodule AshHqWeb.Components.VersionPills do
@moduledoc "Renders pills for selected versions"
use Surface.LiveComponent
prop selected_versions, :map, default: %{}
prop libraries, :list, default: []
prop add_version, :event
prop remove_version, :event
prop change_version, :event
prop editable, :boolean, default: true
prop toggle, :event
data adding_version, :boolean, default: false
def render(assigns) do
~F"""
<div class="flex flex-row flex-wrap align-center items-center ml-2 justify-start gap-2 flex-grow">
{#for library <- @libraries}
{#if @selected_versions[library.id] not in [nil, ""]}
<div class="flex flex-row flex-wrap contents-center items-center ml-1 px-2 py-1 bg-primary-light-500 dark:bg-primary-dark-300 hover:bg-primary-light-600 hover:dark:bg-primary-dark-400 text-black text-xs font-medium rounded-full">
{library.name}{#if selected_version(library, @selected_versions[library.id]) != "latest"}
| {selected_version(library, @selected_versions[library.id])}
{/if}
{#if @editable}
<button
:on-click={@remove_version}
phx-value-library={library.id}
class="flex items-center w-6 h-6"
><Heroicons.Outline.XIcon class="h-4 w-4 ml-1" /></button>
{/if}
</div>
{/if}
{/for}
{#if @editable}
{#if @toggle}
<button :on-click={@toggle}>
<Heroicons.Solid.PlusIcon class="h-4 w-4" />
</button>
{#else}
<button phx-click={AshHqWeb.AppViewLive.toggle_catalogue()}>
<Heroicons.Solid.PlusIcon class="h-4 w-4" />
</button>
{/if}
{/if}
</div>
"""
end
def update(assigns, socket) do
case assigns[:action] do
:close_add_version ->
{:ok, assign(socket, :adding_version, false)}
_ ->
{:ok, assign(socket, assigns)}
end
end
defp selected_version(library, selected_version) do
if selected_version == "latest" do
"latest"
else
Enum.find(library.versions, &(&1.id == selected_version)).version
end
end
def handle_event("add-version", _, socket) do
{:noreply, assign(socket, :adding_version, true)}
end
end

View file

@ -1,86 +1,61 @@
defmodule AshHqWeb.DocRoutes do
@moduledoc "Helpers for routing to results of searches"
def doc_link(entity, selected_versions \\ %{})
def doc_link(%AshHq.Docs.Library{name: name}, _selected_version) do
def doc_link(%AshHq.Docs.Library{name: name}) do
"https://hexdocs.pm/#{name}"
end
def doc_link(
%AshHq.Docs.MixTask{
def doc_link(%AshHq.Docs.MixTask{
library_name: library_name,
module_name: module_name
},
_selected_versions
) do
}) do
"https://hexdocs.pm/#{library_name}/#{module_name}.html"
end
def doc_link(
%AshHq.Docs.Module{
def doc_link(%AshHq.Docs.Module{
library_name: library_name,
name: name
},
_selected_versions
) do
}) do
"https://hexdocs.pm/#{library_name}/#{name}.html"
end
def doc_link(
%AshHq.Docs.Function{
def doc_link(%AshHq.Docs.Function{
name: name,
arity: arity,
module_name: module_name,
library_name: library_name
},
_selected_versions
) do
}) do
"https://hexdocs.pm/#{library_name}/#{module_name}.html##{name}/#{arity}"
end
def doc_link(
%AshHq.Docs.Guide{
def doc_link(%AshHq.Docs.Guide{
route: route,
library_version: %{
library_name: library_name,
version: version,
library_id: library_id
}
},
selected_versions
) do
"/docs/guides/#{library_name}/#{version(version, library_id, selected_versions)}/#{route}"
}) do
"/docs/guides/#{library_name}/latest/#{route}"
end
def doc_link(%AshHq.Docs.LibraryVersion{}, _selected_versions) do
def doc_link(%AshHq.Docs.LibraryVersion{}) do
raise "Shouldn't be called anymore"
end
def doc_link(
%AshHq.Docs.Extension{
def doc_link(%AshHq.Docs.Extension{
library_version: %{
library_name: library_name
},
module: module
},
_selected_versions
) do
}) do
"https://hexdocs.pm/#{library_name}/#{module}.html"
end
def doc_link(item, _selected_versions) do
def doc_link(item) do
"https://hexdocs.pm/#{item.library_name}/dsl-#{sanitize_name(String.trim_trailing(item.extension_module, ".Dsl"))}.html##{String.replace(item.sanitized_path, "/", "-")}"
end
defp version(version_name, library_id, selected_versions) do
if selected_versions[library_id] == "latest" do
"latest"
else
version_name
end
end
def sanitize_name(name, allow_forward_slash? \\ false) do
if allow_forward_slash? do
String.downcase(String.replace(to_string(name), ~r/[^A-Za-z0-9\/_]/, "-"))

View file

@ -14,14 +14,12 @@ defmodule AshHqWeb.Pages.Docs do
require Ash.Query
prop(change_versions, :event, required: true)
prop(selected_versions, :map, required: true)
prop(libraries, :list, default: [])
prop(uri, :string)
prop(remove_version, :event)
prop(add_version, :event)
prop(change_version, :event)
prop(params, :map, required: true)
prop(show_catalogue_call_to_action, :boolean)
data(library, :any)
data(docs, :any)
@ -37,8 +35,6 @@ defmodule AshHqWeb.Pages.Docs do
data(title, :string)
data(sidebar_data, :any)
data(not_found, :boolean, default: false)
data(dsl_target_extensions, :list)
data(dsl_target, :string)
@spec render(any) :: Phoenix.LiveView.Rendered.t()
def render(assigns) do
@ -68,7 +64,6 @@ defmodule AshHqWeb.Pages.Docs do
class="max-w-sm p-2 pr-4"
libraries={@libraries}
remove_version={@remove_version}
selected_versions={@selected_versions}
sidebar_data={@sidebar_data}
/>
</div>
@ -80,7 +75,6 @@ defmodule AshHqWeb.Pages.Docs do
class="hidden xl:block w-80"
libraries={@libraries}
remove_version={@remove_version}
selected_versions={@selected_versions}
sidebar_data={@sidebar_data}
/>
</div>
@ -96,26 +90,12 @@ defmodule AshHqWeb.Pages.Docs do
<div
id="docs-window"
:if={!@not_found}
class={classes([
"w-full shrink max-w-6xl bg-white dark:bg-base-dark-850 md:pr-8 md:mt-4 px-4 md:px-auto mx-auto overflow-x-auto overflow-y-hidden",
"prose prose-td:pl-0 dark:prose-invert": !@dsl_target
])}
class={classes([ "w-full shrink max-w-6xl bg-white dark:bg-base-dark-850 md:pr-8 md:mt-4 px-4 md:px-auto mx-auto overflow-x-auto overflow-y-hidden prose prose-td:pl-0 dark:prose-invert" ])}
>
<div
id="module-docs"
class="w-full nav-anchor text-black dark:text-white relative py-4 md:py-auto"
>
<.catalogue_call_to_action :if={@show_catalogue_call_to_action} />
{#if @module}
<h1>{@module.name} <SourceLink module_or_function={@module} library={@library} library_version={@library_version} /></h1>
{/if}
{#if @mix_task}
<h1>{@mix_task.name} <SourceLink
module_or_function={@mix_task}
library={@library}
library_version={@library_version}
/></h1>
{/if}
<div class="flex flex-col float-right">
<.github_guide_link
:if={@guide}
@ -133,62 +113,7 @@ defmodule AshHqWeb.Pages.Docs do
{#if @docs}
<.docs doc_path={@doc_path} docs={@docs} />
{/if}
{#if @dsl_target_extensions}
{#for extension <- @dsl_target_extensions}
<div class="prose dark:prose-invert mb-8">
<h1>
{#if extension.default_for_target}
{extension.target}
{#else}
{extension.module}
{/if}
</h1>
{raw(extension.doc_html)}
</div>
{#for dsl <- extension.dsls |> Enum.filter(&(&1.path == []))}
{render_dsl(assigns, extension, dsl)}
{/for}
{/for}
{/if}
</div>
{#if @module}
<Functions
header="Types"
type={:type}
functions={@module.functions}
library={@library}
library_version={@library_version}
libraries={@libraries}
selected_versions={@selected_versions}
/>
<Functions
header="Callbacks"
type={:callback}
functions={@module.functions}
library={@library}
library_version={@library_version}
libraries={@libraries}
selected_versions={@selected_versions}
/>
<Functions
header="Functions"
type={:function}
functions={@module.functions}
library={@library}
library_version={@library_version}
libraries={@libraries}
selected_versions={@selected_versions}
/>
<Functions
header="Macros"
type={:macro}
functions={@module.functions}
library={@library}
library_version={@library_version}
libraries={@libraries}
selected_versions={@selected_versions}
/>
{/if}
<footer class="p-2 sm:justify-center">
<div class="md:flex md:justify-around items-center">
@ -201,138 +126,13 @@ defmodule AshHqWeb.Pages.Docs do
</div>
</footer>
</div>
{#if @module}
<div class="sidebar-container hidden lg:block lg:w-72 sticky top-36 xl:top-20 shrink-0 overflow-y-auto overflow-x-hidden dark:bg-base-dark-850 bg-opacity-70 mt-4">
<ModuleRightNav functions={@module.functions} module={@module.name} />
</div>
{#else}
{#if @dsl_target}
<div class="sidebar-container hidden lg:block lg:w-72 sticky top-36 xl:top-20 shrink-0 overflow-y-auto overflow-x-hidden dark:bg-base-dark-850 bg-opacity-70 mt-4">
<DslRightNav dsls={Enum.flat_map(@dsl_target_extensions, & &1.dsls)} dsl_target={@dsl_target} />
</div>
{#else}
<!-- empty div to preserve flex row spacing -->
<div />
{/if}
{/if}
</div>
</div>
"""
end
def render_dsl(assigns, extension, dsl) do
options =
Enum.filter(
extension.options,
&(&1.path == dsl.path ++ [dsl.name])
)
|> Enum.split_with(& &1.argument_index)
|> then(fn {args, not_args} ->
Enum.sort_by(args, & &1.argument_index) ++ not_args
end)
arguments =
options
|> Enum.filter(& &1.argument_index)
|> Enum.sort_by(& &1.argument_index)
arg_count = Enum.count(arguments)
~F"""
<div class={classes(["mb-12 mt-4"])}>
<div class="flex flex-col">
<div class="w-full">
<div class="text-2xl font-bold nav-anchor" id={String.replace(dsl.sanitized_path, "/", "-")}>
<div class="flex flex-row items-center bg-opacity-50 py-1 bg-base-light-200 dark:bg-base-dark-750 w-full border-l-2 border-primary-light-400 dark:border-primary-dark-400 pl-2">
<div class="flex flex-row items-center justify-between w-full pr-2">
<div class="text-lg w-full font-semibold">
{dsl.name}
{#for {arg, i} <- Enum.with_index(arguments)}
<LivePatch
class={"after:content-[',']": i != arg_count - 1}
to={"##{String.replace(arg.sanitized_path, "/", "-")}-#{DocRoutes.sanitize_name(arg.name)}"}
>
<span class="italic text-primary-light-600 dark:text-primary-dark-400 hover:dark:text-primary-dark-500 hover:text-primary-light-700">
{arg.name}</span>{#if arg.name in dsl.optional_args}
\\ {Map.get(dsl.arg_defaults, arg.name, "nil")}
{/if}</LivePatch>
{/for}
</div>
<a href={"##{String.replace(dsl.sanitized_path, "/", "-")}"}>
<Heroicons.Outline.LinkIcon class="h-3 w-3 mr-2" />
</a>
</div>
</div>
</div>
{#if dsl.requires_extension}
<div class="w-full pt-4">
Requires Extension: <span class="text-primary-light-600">{dsl.requires_extension}</span>
</div>
{/if}
<div class="prose dark:prose-invert my-4">
{raw(dsl.doc_html)}
</div>
</div>
{#if !Enum.empty?(options)}
{#case modules_in_scope(dsl, extension, @libraries, @selected_versions)}
{#match []}
{#match imports}
<div class="mt-2 mb-6">
<div>Imported Modules:</div>
<ul>
{#for mod <- imports}
<li class="list-disc">
<LivePatch to={DocRoutes.doc_link(mod, @selected_versions)}>
<span class="text-primary-light-600 dark:text-primary-dark-400 hover:dark:text-primary-dark-500 hover:text-primary-light-700">
{mod.name}
</span>
</LivePatch>
</li>
{/for}
</ul>
</div>
{/case}
{/if}
</div>
<div
:if={!Enum.empty?(options)}
class="grid grid-cols-[min-content_auto] w-full border-b border-gray-300 dark:border-gray-600 pb-2 mb-12"
>
{#for option <- options}
<div
class="flex flex-col border-t border-gray-300 dark:border-gray-600 py-3 nav-anchor pr-4"
id={"#{String.replace(option.sanitized_path, "/", "-")}-#{DocRoutes.sanitize_name(option.name)}"}
>
<div class="flex flex-row align-middle leading-7">
<LivePatch
to={"##{String.replace(option.sanitized_path, "/", "-")}-#{DocRoutes.sanitize_name(option.name)}"}
class="text-primary-light-600 dark:text-primary-dark-400 hover:dark:text-primary-dark-500 hover:text-primary-light-700 pr-2"
>
{option.name}
</LivePatch>
{render_tags(assigns, dsl, option)}
</div>
<span class="break-keep text-sm">
{option.type}
</span>
</div>
<div class="prose dark:prose-invert border-t border-gray-300 dark:border-gray-600 py-3">
{raw(option.doc_html)}
</div>
{/for}
</div>
{#case child_dsls(extension, dsl)}
{#match []}
{#match children}
{#for child <- children}
{render_dsl(assigns, extension, child)}
{/for}
{/case}
</div>
"""
end
def github_guide_link(assigns) do
~F"""
<a
@ -388,28 +188,8 @@ defmodule AshHqWeb.Pages.Docs do
"""
end
def catalogue_call_to_action(assigns) do
~F"""
<div
class="bg-blue-500 text-white px-4 py-2 mb-8 cursor-pointer flex justify-between items-center"
id="catalogue-call-to-action"
:on-click={AshHqWeb.AppViewLive.toggle_catalogue()}
>
<span>View the full range of Ash libraries for authentication, soft deletion, GraphQL and more</span>
<button
id="dismiss-catalogue"
class="h-6 w-6 cursor-pointer"
:on-click="dismiss_catalogue_call_to_action"
>
<Heroicons.Outline.XIcon class="h-6 w-6" />
</button>
</div>
"""
end
def update(assigns, socket) do
if socket.assigns[:loaded_once?] &&
assigns[:selected_versions] == socket.assigns[:selected_versions] do
if socket.assigns[:loaded_once?] do
{:ok, socket |> assign(Map.delete(assigns, :libraries)) |> load_docs()}
else
{:ok,
@ -422,49 +202,6 @@ defmodule AshHqWeb.Pages.Docs do
end
end
defp modules_in_scope(nil, _, _, _), do: []
defp modules_in_scope(_, nil, _, _), do: []
defp modules_in_scope(dsl, %{dsls: dsls}, libraries, selected_versions) do
dsl_path = dsl.path ++ [dsl.name]
dsls
|> Enum.filter(fn potential_parent ->
List.starts_with?(dsl_path, potential_parent.path ++ [potential_parent.name])
end)
|> Enum.flat_map(fn dsl ->
dsl.imports || []
end)
|> Enum.flat_map(fn mod_name ->
case find_module(libraries, selected_versions, mod_name) do
nil ->
Logger.warning("No such module found called #{inspect(mod_name)}")
[]
module ->
[module]
end
end)
end
defp find_module(libraries, selected_versions, mod_name) do
Enum.find_value(libraries, fn library ->
version = selected_version(library, selected_versions[library.id])
if version do
Enum.find(version.modules, &(&1.name == mod_name))
end
end)
end
defp selected_version(library, selected_version) do
if selected_version == "latest" do
latest_version(library)
else
Enum.find(library.versions, &(&1.id == selected_version))
end
end
defp child_dsls(_, nil), do: []
defp child_dsls(nil, _), do: []
@ -534,62 +271,8 @@ defmodule AshHqWeb.Pages.Docs do
|> Ash.Query.new()
|> load_for_search()
modules_query =
AshHq.Docs.Module
|> Ash.Query.sort(order: :asc)
|> load_for_search()
mix_tasks_query =
AshHq.Docs.MixTask
|> Ash.Query.sort(order: :asc)
|> load_for_search()
extensions_query =
AshHq.Docs.Extension
|> Ash.Query.sort(order: :asc)
|> load_for_search()
new_libraries =
socket.assigns.libraries
|> Enum.flat_map(fn library ->
latest_version = AshHqWeb.Helpers.latest_version(library)
Enum.filter(library.versions, fn version ->
(latest_version && version.id == latest_version.id) ||
version.id == socket.assigns[:selected_versions][library.id] ||
(socket.assigns[:library_version] &&
socket.assigns[:library_version].id == version.id) ||
(socket.assigns.params["version"] &&
socket.assigns.params["version"] ==
version.version)
end)
end)
|> AshHq.Docs.load!(
[
extensions: extensions_query,
guides: guides_query,
modules: modules_query,
mix_tasks: mix_tasks_query
],
lazy?: true
)
|> Enum.reduce(socket.assigns.libraries, fn library_version, libraries ->
Enum.map(libraries, fn library ->
if library.id == library_version.library_id do
Map.update!(library, :versions, fn versions ->
Enum.map(versions, fn current_version ->
if current_version.id == library_version.id do
library_version
else
current_version
end
end)
end)
else
library
end
end)
end)
AshHq.Docs.load!(socket.assigns.libraries, versions: [guides: guides_query])
assign(socket, :libraries, new_libraries)
end
@ -597,64 +280,16 @@ defmodule AshHqWeb.Pages.Docs do
def load_docs(socket) do
socket
|> assign_library()
|> assign_dsl_target()
|> assign_guide()
|> assign_module()
|> assign_mix_task()
|> assign_fallback_guide()
|> assign_docs()
end
defp assign_dsl_target(socket) do
if socket.assigns[:params]["dsl_target"] do
extensions =
socket.assigns.libraries
|> Enum.map(fn library ->
selected_version(library, socket.assigns.selected_versions[library.id])
end)
|> Enum.reject(&is_nil/1)
|> Enum.flat_map(& &1.extensions)
|> Enum.filter(fn extension ->
AshHqWeb.DocRoutes.sanitize_name(extension.target) ==
socket.assigns[:params]["dsl_target"]
end)
dsls_query =
AshHq.Docs.Dsl
|> Ash.Query.sort(order: :asc)
|> load_for_search()
|> Ash.Query.load([:doc_html, :path])
options_query =
AshHq.Docs.Option
|> Ash.Query.sort(order: :asc)
|> load_for_search()
|> Ash.Query.load([:doc_html, :path])
extensions = AshHq.Docs.load!(extensions, dsls: dsls_query, options: options_query)
target_name =
case Enum.at(extensions, 0) do
%{target: target} -> target
_ -> nil
end
assign(socket,
dsl_target_extensions: extensions,
dsl_target: target_name,
not_found: Enum.empty?(extensions)
)
else
assign(socket, dsl_target_extensions: nil, dsl_target: nil)
end
end
defp assign_sidebar_content(socket) do
sidebar_data =
guides_by_category_and_library(
socket.assigns[:libraries],
socket.assigns[:library_version],
socket.assigns[:selected_versions],
socket.assigns[:guide]
)
@ -663,9 +298,11 @@ defmodule AshHqWeb.Pages.Docs do
@start_guides ["Tutorials", "Topics", "How To", "Misc"]
defp guides_by_category_and_library(libraries, library_version, selected_versions, active_guide) do
defp guides_by_category_and_library(libraries, library_version, active_guide) do
libraries
|> Enum.map(&{&1, selected_version(&1, library_version, selected_versions)})
|> Enum.map(fn library ->
{library, Enum.at(library.versions, 0)}
end)
|> Enum.filter(fn {_library, version} -> version != nil end)
|> Enum.sort_by(fn {library, _version} -> library.order end)
|> Enum.flat_map(fn {library, %{guides: guides}} ->
@ -675,7 +312,7 @@ defmodule AshHqWeb.Pages.Docs do
%{
id: guide.id,
name: guide.name,
to: DocRoutes.doc_link(guide, selected_versions),
to: DocRoutes.doc_link(guide),
active?: active_guide && active_guide.id == guide.id
}
end)
@ -685,22 +322,6 @@ defmodule AshHqWeb.Pages.Docs do
|> partially_alphabetically_sort(@start_guides, [])
end
defp selected_version(library, library_version, selected_versions) do
selected_version = selected_versions[library.id]
if library_version && library_version.library_id == library.id do
library_version
else
if selected_version == "latest" do
AshHqWeb.Helpers.latest_version(library)
else
if selected_version not in [nil, ""] do
Enum.find(library.versions, &(&1.id == selected_version))
end
end
end
end
defp partially_alphabetically_sort(keyed_list, first, last) do
{first_items, rest} =
Enum.split_with(keyed_list, fn {key, _} ->
@ -806,51 +427,10 @@ defmodule AshHqWeb.Pages.Docs do
)
library ->
socket =
if socket.assigns[:params]["version"] do
library_version =
case socket.assigns[:params]["version"] do
"latest" ->
AshHqWeb.Helpers.latest_version(library)
version ->
Enum.find(
library.versions,
&(&1.version == version)
)
end
if is_nil(library_version) do
assign(socket,
not_found: true,
library_version: AshHqWeb.Helpers.latest_version(library)
)
else
socket =
assign(
socket,
library_version: library_version
)
if socket.assigns.params["version"] != "latest" &&
(!socket.assigns[:library] ||
socket.assigns.params["library"] !=
socket.assigns.library.name) do
new_selected_versions =
Map.put(socket.assigns.selected_versions, library.id, library_version.id)
socket
|> assign(selected_versions: new_selected_versions)
|> push_event("selected-versions", new_selected_versions)
else
socket
end
end
else
assign(socket, :library_version, nil)
end
|> assign(:library, library)
|> assign(:library_version, AshHqWeb.Helpers.latest_version(library))
assign(socket, :library, library)
end
end
@ -878,95 +458,8 @@ defmodule AshHqWeb.Pages.Docs do
DocRoutes.sanitize_name(guide.name) == DocRoutes.sanitize_name(guide_name)
end
defp assign_module(socket) do
if socket.assigns.library && socket.assigns.library_version &&
socket.assigns[:params]["module"] do
module =
Enum.find(
socket.assigns.library_version.modules,
&(&1.sanitized_name == socket.assigns[:params]["module"])
)
if is_nil(module) do
assign(socket, module: nil, not_found: true)
else
functions_query =
AshHq.Docs.Function
|> Ash.Query.sort(name: :asc, arity: :asc)
|> load_for_search()
|> Ash.Query.load(:doc_html)
assign(socket,
module: AshHq.Docs.load!(module, [functions: functions_query], lazy?: true)
)
end
else
assign(socket, :module, nil)
end
end
defp assign_mix_task(socket) do
if socket.assigns.library && socket.assigns.library_version &&
socket.assigns[:params]["mix_task"] do
mix_task =
Enum.find(
socket.assigns.library_version.mix_tasks,
&(&1.sanitized_name == socket.assigns[:params]["mix_task"])
)
if mix_task do
assign(socket,
mix_task: mix_task
)
else
assign(socket, mix_task: nil, not_found: true)
end
else
assign(socket, :mix_task, nil)
end
end
defp assign_docs(socket) do
cond do
socket.assigns.dsl_target_extensions ->
target_name = socket.assigns.dsl_target
title =
if target_name do
"DSL Docs"
else
target_name
end
send(self(), {:page_title, title})
assign(socket,
docs: nil,
title: target_name,
description: "View the documentation for #{target_name} on Ash HQ.",
doc_path: [target_name]
)
socket.assigns.module ->
send(self(), {:page_title, socket.assigns.module.name})
assign(socket,
docs: socket.assigns.module |> reselect_and_get!(:doc_html),
title: "Module: #{socket.assigns.module.name}",
description: "View the documentation for #{socket.assigns.module.name} on Ash HQ.",
doc_path: [socket.assigns.library.name, socket.assigns.module.name]
)
socket.assigns.mix_task ->
send(self(), {:page_title, socket.assigns.mix_task.name})
assign(socket,
docs: socket.assigns.mix_task |> reselect_and_get!(:doc_html),
title: "Mix Task: #{socket.assigns.mix_task.name}",
description: "View the documentation for #{socket.assigns.mix_task.name} on Ash HQ.",
doc_path: [socket.assigns.library.name, socket.assigns.mix_task.name]
)
socket.assigns.guide ->
send(self(), {:page_title, socket.assigns.guide.name})

View file

@ -4,9 +4,7 @@ defmodule AshHqWeb.SessionPlug do
@cookies_to_replicate [
"theme",
"selected_versions",
"selected_types",
"catalogue_call_to_action_dismissed"
"selected_types"
]
def init(_), do: []

View file

@ -13,7 +13,6 @@ defmodule AshHqWeb.AppViewLive do
import AshHqWeb.Tails
data(configured_theme, :string, default: :system)
data(selected_versions, :map, default: %{})
data(libraries, :list, default: [])
data(selected_types, :map, default: %{})
data(current_user, :map)
@ -71,17 +70,9 @@ defmodule AshHqWeb.AppViewLive do
libraries={@libraries}
selected_types={@selected_types}
change_types="change-types"
selected_versions={@selected_versions}
change_versions="change-versions"
remove_version="remove_version"
/>
<CatalogueModal
id="catalogue-box"
libraries={@libraries}
selected_versions={@selected_versions}
change_versions="change-versions"
show_catalogue_call_to_action={@show_catalogue_call_to_action}
/>
<button id="search-button" class="hidden" phx-click={AshHqWeb.AppViewLive.toggle_search()} />
<div
id="main-container"
@ -114,9 +105,7 @@ defmodule AshHqWeb.AppViewLive do
params={@params}
remove_version="remove_version"
change_versions="change-versions"
selected_versions={@selected_versions}
libraries={@libraries}
show_catalogue_call_to_action={@show_catalogue_call_to_action}
/>
{#match :user_settings}
<UserSettings id="user_settings" current_user={@current_user} />
@ -197,34 +186,6 @@ defmodule AshHqWeb.AppViewLive do
{:noreply, assign(socket, :page_title, "Ash Framework - #{title}")}
end
def handle_event("dismiss_catalogue_call_to_action", _, socket) do
{:noreply,
socket
|> assign(:show_catalogue_call_to_action, false)
|> push_event("catalogue-call-to-action-dismissed", %{})}
end
def handle_event("remove_version", %{"library" => library}, socket) do
new_selected_versions = Map.put(socket.assigns.selected_versions, library, "")
{:noreply,
socket
|> set_selected_library_versions(new_selected_versions)}
end
def handle_event("add_version", %{"library" => library}, socket) do
new_selected_versions = Map.put(socket.assigns.selected_versions, library, "latest")
{:noreply,
socket
|> set_selected_library_versions(new_selected_versions)}
end
def handle_event("change-versions", %{"versions" => versions}, socket) do
{:noreply,
socket
|> set_selected_library_versions(versions)}
end
def handle_event("change-types", %{"types" => types}, socket) do
types =
@ -275,24 +236,6 @@ defmodule AshHqWeb.AppViewLive do
socket = Context.put(socket, platform: socket.assigns.platform)
configured_theme = session["theme"] || "system"
configured_library_versions =
case session["selected_versions"] do
nil ->
%{}
"" ->
%{}
value ->
value
|> String.split(",")
|> Map.new(fn str ->
str
|> String.split(":")
|> List.to_tuple()
end)
end
all_types = AshHq.Docs.Extensions.Search.Types.types()
selected_types =
@ -312,37 +255,17 @@ defmodule AshHqWeb.AppViewLive do
libraries = AshHq.Docs.Library.read!(load: [versions: versions_query])
selected_versions =
Enum.reduce(libraries, configured_library_versions, fn library, acc ->
Map.put_new(acc, library.id, "latest")
end)
{:ok,
socket
|> assign(
:show_catalogue_call_to_action,
session["catalogue_call_to_action_dismissed"] != "true"
)
|> assign(:libraries, libraries)
|> assign(
:selected_versions,
selected_versions
)
|> assign(
:selected_types,
selected_types
)
|> assign(configured_theme: configured_theme)
|> set_selected_library_versions(selected_versions)
|> push_event("selected_types", %{types: selected_types})}
end
defp set_selected_library_versions(socket, library_versions) do
socket
|> assign(:selected_versions, library_versions)
|> push_event("selected-versions", library_versions)
end
def toggle_search(js \\ %JS{}) do
js
|> JS.toggle(
@ -361,24 +284,6 @@ defmodule AshHqWeb.AppViewLive do
|> JS.dispatch("js:focus", to: "#search-input")
end
def toggle_catalogue(js \\ %JS{}) do
js
|> JS.toggle(
to: "#catalogue-box",
in: {
"transition ease-in duration-100",
"opacity-0",
"opacity-100"
},
out: {
"transition ease-out duration-75",
"opacity-100",
"opacity-0"
}
)
|> JS.dispatch("phx:js:scroll-to", detail: %{id: "catalogue-box"})
end
def close_search(js \\ %JS{}) do
js
|> JS.hide(
@ -388,12 +293,4 @@ defmodule AshHqWeb.AppViewLive do
|> JS.hide(transition: "fade-out", to: "#search-versions")
|> JS.show(transition: "fade-in", to: "#search-body")
end
def close_catalogue(js \\ %JS{}) do
js
|> JS.hide(
transition: "fade-out",
to: "#catalogue-box"
)
end
end