defmodule AshHqWeb.AppViewLive do use Surface.LiveView, container: {:div, class: "h-full"} alias AshHq.Docs.Extensions.RenderMarkdown alias AshHqWeb.Components.{Search, SearchBar} alias AshHqWeb.Pages.{Docs, Home, LogIn, Register, ResetPassword, UserSettings} alias AshHqWeb.Router.Helpers, as: Routes alias Phoenix.LiveView.JS alias Surface.Components.{Link, LiveRedirect} require Ash.Query data configured_theme, :string, default: :system data searching, :boolean, default: false data selected_versions, :map, default: %{} data libraries, :list, default: [] data selected_types, :map, default: %{} data sidebar_state, :map, default: %{} data current_user, :map data library, :any, default: nil data extension, :any, default: nil data docs, :any, default: nil data library_version, :any, default: nil data guide, :any, default: nil data doc_path, :list, default: [] data dsls, :list, default: [] data dsl, :any, default: nil data options, :list, default: [] data module, :any, default: nil def render(assigns) do ~F"""
{#for flash <- List.wrap(live_flash(@flash, :error))} {/for} {#for flash <- List.wrap(live_flash(@flash, :info))} {/for} {#case @live_action} {#match :home} {#match :docs_dsl} {#match :user_settings} {#match :log_in} {#match :register} {#match :reset_password} {/case} """ end defp toggle_account_dropdown(js \\ %JS{}) do js |> JS.toggle( to: "#account-dropdown", in: { "transition ease-out duration-100", "opacity-0 scale-95", "opacity-100 scale-100" }, out: { "transition ease-in duration-75", "opacity-100 scale-100", "opacity-0 scale-05" } ) end # {#if @current_user} #
#
# #
# #
# {#else} # # Sign In # # {/if} def handle_params(params, uri, socket) do {:noreply, socket |> assign(params: params, uri: uri) |> load_docs()} end def handle_event("collapse_sidebar", %{"id" => id}, socket) do new_state = Map.put(socket.assigns.sidebar_state, id, "closed") {:noreply, socket |> assign(:sidebar_state, new_state) |> push_event("sidebar-state", new_state)} end def handle_event("expand_sidebar", %{"id" => id}, socket) do new_state = Map.put(socket.assigns.sidebar_state, id, "open") {:noreply, socket |> assign(:sidebar_state, new_state) |> push_event("sidebar-state", new_state)} end def handle_event("change-versions", %{"versions" => versions}, socket) do {:noreply, socket |> assign(:selected_versions, versions) |> load_docs() |> push_event("selected-versions", versions)} end def handle_event("change-types", %{"types" => types}, socket) do types = types |> Enum.filter(fn {_, value} -> value == "true" end) |> Enum.map(&elem(&1, 0)) {:noreply, socket |> assign( :selected_types, types ) |> push_event("selected-types", %{types: types})} end def handle_event("toggle_theme", _, socket) do theme = case socket.assigns.configured_theme do "light" -> "dark" "dark" -> "system" "system" -> "light" end {:noreply, socket |> assign(:configured_theme, theme) |> push_event("set_theme", %{theme: theme})} end def handle_info({:new_sidebar_state, new_state}, socket) do {:noreply, socket |> assign(:sidebar_state, new_state) |> push_event("sidebar-state", new_state)} end defp load_docs(%{assigns: %{live_action: :docs_dsl}} = socket) do new_libraries = socket.assigns.libraries |> Enum.map(fn library -> latest_version = AshHqWeb.Helpers.latest_version(library) Map.update!(library, :versions, fn versions -> Enum.map(versions, fn version -> if (socket.assigns[:selected_versions][library.id] in ["latest", nil, ""] && latest_version && version.id == latest_version.id) || version.id == socket.assigns[:selected_versions][library.id] do dsls_query = AshHq.Docs.Dsl |> Ash.Query.sort(order: :asc) |> load_for_search(socket.assigns[:params]["dsl_path"]) options_query = AshHq.Docs.Option |> Ash.Query.sort(order: :asc) |> load_for_search(socket.assigns[:params]["dsl_path"]) functions_query = AshHq.Docs.Function |> Ash.Query.sort(name: :asc, arity: :asc) |> load_for_search(socket.assigns[:params]["module"]) guides_query = AshHq.Docs.Guide |> Ash.Query.new() |> load_for_search(socket.assigns[:params]["guide"]) modules_query = AshHq.Docs.Module |> Ash.Query.sort(order: :asc) |> Ash.Query.load(functions: functions_query) |> load_for_search(socket.assigns[:params]["module"]) extensions_query = AshHq.Docs.Extension |> Ash.Query.sort(order: :asc) |> Ash.Query.load(options: options_query, dsls: dsls_query) |> load_for_search(socket.assigns[:params]["extension"]) AshHq.Docs.load!(version, extensions: extensions_query, guides: guides_query, modules: modules_query ) else version end end) end) end) socket |> assign(:libraries, new_libraries) |> assign_library() |> assign_extension() |> assign_guide() |> assign_module() |> assign_dsl() |> assign_docs() end defp load_docs(socket), do: socket def mount(_params, session, socket) do 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 = case session["selected_types"] do nil -> AshHq.Docs.Extensions.Search.Types.types() types -> types |> String.split(",") |> Enum.filter(&(&1 in all_types)) end sidebar_state = case session["sidebar_state"] do nil -> %{} value -> value |> String.split(",") |> Map.new(fn str -> str |> String.split(":") |> List.to_tuple() end) end versions_query = AshHq.Docs.LibraryVersion |> Ash.Query.sort(version: :desc) 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(:libraries, libraries) |> assign( :selected_versions, selected_versions ) |> assign( :selected_types, selected_types ) |> assign(:selected_versions, selected_versions) |> assign(configured_theme: configured_theme, sidebar_state: sidebar_state) |> push_event("selected-versions", selected_versions) |> push_event("selected_types", %{types: selected_types})} end def toggle_search(js \\ %JS{}) do js |> JS.dispatch("js:noscroll-main", to: "#search-box") |> JS.toggle( to: "#search-box", in: { "transition ease-in duration-100", "opacity-0", "opacity-100" }, out: { "transition ease-out duration-75", "opacity-100", "opacity-0" } ) |> JS.dispatch("js:focus", to: "#search-input") end def close_search(js \\ %JS{}) do js |> JS.dispatch("js:noscroll-main", to: "#search-box") |> JS.hide( transition: "fade-out", to: "#search-box" ) |> JS.dispatch("js:focus", to: "#search-input") end defp load_for_search(query, docs_for) do query |> Ash.Query.load(AshHq.Docs.Extensions.Search.load_for_search(query.resource)) |> deselect_doc_attributes() |> load_docs_for(docs_for) end defp load_docs_for(query, nil), do: query defp load_docs_for(query, []), do: query defp load_docs_for(query, true) do query.resource |> AshHq.Docs.Extensions.RenderMarkdown.render_attributes() |> Enum.reduce(query, fn {source, target}, query -> Ash.Query.select(query, [source, target]) end) end defp load_docs_for(query, name) when is_list(name) do Ash.Query.load(query, html_for: %{for: Enum.join(name, "/")}) end defp load_docs_for(query, name) do Ash.Query.load(query, html_for: %{for: name}) end defp deselect_doc_attributes(query) do query.resource |> AshHq.Docs.Extensions.RenderMarkdown.render_attributes() |> Enum.reduce(query, fn {source, target}, query -> Ash.Query.deselect(query, [source, target]) end) end defp assign_guide(socket) do guide = cond do socket.assigns[:params]["guide"] && socket.assigns.library_version -> Enum.find(socket.assigns.library_version.guides, fn guide -> guide.route == Enum.join(socket.assigns[:params]["guide"], "/") end) true -> nil end assign(socket, :guide, guide) end defp assign_dsl(socket) do case socket.assigns[:params]["dsl_path"] do nil -> assign(socket, :dsl, nil) path -> path = Enum.join(path, "/") dsl = Enum.find( socket.assigns.extension.dsls, &(&1.sanitized_path == path) ) new_state = Map.put(socket.assigns.sidebar_state, dsl.id, "open") unless socket.assigns.sidebar_state[dsl.id] == "open" do send(self(), {:new_sidebar_state, new_state}) end socket |> assign( :dsl, dsl ) end 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"]) ) assign(socket, module: module ) else assign(socket, :module, nil) end end defp assign_docs(socket) do cond do socket.assigns.module -> assign(socket, docs: socket.assigns.module.html_for, doc_path: [socket.assigns.library.name, socket.assigns.module.name], options: [] ) socket.assigns.dsl -> assign(socket, docs: socket.assigns.dsl.html_for, doc_path: [ socket.assigns.library.name, socket.assigns.extension.name ] ++ socket.assigns.dsl.path ++ [socket.assigns.dsl.name], options: Enum.filter( socket.assigns.extension.options, &(&1.path == socket.assigns.dsl.path ++ [socket.assigns.dsl.name]) ) ) socket.assigns.extension -> assign(socket, docs: socket.assigns.extension.html_for, doc_path: [socket.assigns.library.name, socket.assigns.extension.name], options: [] ) socket.assigns.guide -> assign(socket, docs: socket.assigns.guide.html_for, doc_path: [socket.assigns.library.name, socket.assigns.guide.name], options: [] ) true -> assign(socket, docs: "", doc_path: [], dsls: [], options: []) end end defp assign_extension(socket) do if socket.assigns.library_version && socket.assigns[:params]["extension"] do extensions = socket.assigns.library_version.extensions assign(socket, extension: Enum.find(extensions, fn extension -> extension.sanitized_name == socket.assigns[:params]["extension"] end) ) else assign(socket, :extension, nil) end end defp assign_library(socket) do case Enum.find( socket.assigns.libraries, &(&1.name == socket.assigns.params["library"]) ) do nil -> assign(socket, library: nil, library_version: nil) 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 library_version do 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 else assign(socket, :library_version, nil) end else assign(socket, :library_version, nil) end assign(socket, :library, library) end end end