5.2 KiB · text 5af55d9
defmodule GitGudWeb.RepoLive.Header do
@moduledoc """
Shared chrome for repo-scoped pages (show, tree, blob, …).
Renders the title row + the small "Issues · Labels · Forks · Compare ·
Packages · Actions · Settings" link strip + the Fork button.
Pages that include the header should call `assign_chrome/2` from their
mount so `has_packages?`, `can_admin?`, and `latest_run` are loaded
alongside the repo itself.
"""
use Phoenix.Component
use Phoenix.VerifiedRoutes,
endpoint: GitGudWeb.Endpoint,
router: GitGudWeb.Router
import GitGudWeb.CoreComponents, only: [icon: 1]
import GitGudWeb.AvatarComponent, only: [avatar: 1]
alias GitGud.Organizations
alias GitGud.Packages
alias GitGud.Workflows
attr :repo, :map, required: true
attr :handle, :string, required: true
attr :current_scope, :map, default: nil
attr :has_packages?, :boolean, default: false
attr :can_admin?, :boolean, default: false
attr :latest_run, :any, default: nil
def header(assigns) do
~H"""
<header class="flex items-start justify-between">
<div class="space-y-1">
<h1 class={[
"text-2xl font-semibold font-mono flex items-center gap-2",
GitGud.Repositories.profile_repo?(@repo, @handle) && "opacity-70"
]}>
<.link navigate={namespace_path(@repo, @handle)} class="link link-hover flex items-center gap-1.5">
<.avatar name={@handle} size="sm" alt={@handle} />
{@handle}
</.link><span class="opacity-50">/</span><.link
navigate={~p"/r/#{@handle}/#{@repo.name}"}
class="link link-hover"
>{@repo.name}</.link>
<span
:if={GitGud.Repositories.profile_repo?(@repo, @handle)}
class="badge badge-sm badge-ghost font-sans text-xs"
title="This repo's README powers the profile page"
>
profile
</span>
</h1>
<p :if={@repo.description} class="opacity-70">{@repo.description}</p>
<p class="text-xs opacity-60">
{@repo.visibility} ·
<.link navigate={~p"/r/#{@handle}/#{@repo.name}/issues"} class="link link-hover">
Issues
</.link>
·
<.link navigate={~p"/r/#{@handle}/#{@repo.name}/labels"} class="link link-hover">
Labels
</.link>
·
<.link navigate={~p"/r/#{@handle}/#{@repo.name}/forks"} class="link link-hover">
Forks
</.link>
·
<.link
navigate={~p"/r/#{@handle}/#{@repo.name}/compare"}
class="link link-hover"
>
Compare
</.link>
·
<.link
navigate={~p"/r/#{@handle}/#{@repo.name}/actions"}
class="link link-hover"
>
Actions
</.link>
<.link
:if={@latest_run}
navigate={~p"/r/#{@handle}/#{@repo.name}/actions/#{@latest_run.number}"}
class={["badge badge-xs ml-1", run_badge(@latest_run.status)]}
title={"latest run ##{@latest_run.number}: #{@latest_run.status}"}
>
{@latest_run.status}
</.link>
<%= if @has_packages? do %>
·
<.link
navigate={~p"/r/#{@handle}/#{@repo.name}/packages"}
class="link link-hover"
>
Packages
</.link>
<% end %>
<%= if @can_admin? do %>
·
<.link
navigate={~p"/r/#{@handle}/#{@repo.name}/settings"}
class="link link-hover"
>
Settings
</.link>
<% end %>
</p>
</div>
<.link
:if={@current_scope && @current_scope.user}
navigate={~p"/r/#{@handle}/#{@repo.name}/fork"}
class="btn btn-sm"
id="fork-button"
>
<.icon name="hero-arrow-trending-up" class="size-4" /> Fork
</.link>
</header>
"""
end
defp namespace_path(%{organization_id: oid}, handle) when is_integer(oid),
do: ~p"/orgs/#{handle}"
defp namespace_path(_, handle), do: ~p"/u/#{handle}"
defp run_badge("success"), do: "badge-success"
defp run_badge("running"), do: "badge-primary"
defp run_badge("queued"), do: "badge-warning"
defp run_badge("failure"), do: "badge-error"
defp run_badge(_), do: "badge-neutral"
@doc """
Loads the cheap supporting assigns the header reads: `:has_packages?`,
`:can_admin?`, `:latest_run`. Call from `mount/3` after the `:repo`
and `:current_scope` are set.
"""
@spec assign_chrome(Phoenix.LiveView.Socket.t(), map()) :: Phoenix.LiveView.Socket.t()
def assign_chrome(socket, repo) do
user = socket.assigns[:current_scope] && socket.assigns.current_scope.user
socket
|> Phoenix.Component.assign(:has_packages?, Packages.list_packages(repo) != [])
|> Phoenix.Component.assign(:can_admin?, user && admin?(user, repo))
|> Phoenix.Component.assign(:latest_run, Workflows.latest_run(repo))
end
defp admin?(user, %{owner_id: uid, organization_id: nil}), do: user.id == uid
defp admin?(user, %{organization: %{} = org}), do: Organizations.org_admin?(org, user)
defp admin?(_, _), do: false
end