defmodule GitGudWeb.RepoLive.Blob do
use GitGudWeb, :live_view
alias GitGud.Git
alias GitGud.Repositories
alias GitGud.Repositories.Storage
@max_inline_bytes 1_000_000
@impl true
def mount(%{"owner" => owner, "name" => name, "ref" => ref, "path" => path}, _session, socket) do
repo =
Repositories.get_repository_by_path!(owner, name)
|> GitGud.Repo.preload([:owner, :organization])
path = path |> List.wrap() |> Enum.join("/")
{:ok, head_sha} = Repositories.resolve(repo, ref)
{:ok, commit} = Repositories.get_commit(repo, head_sha)
case Repositories.get_tree_entry_at(repo, commit.tree_sha, path) do
{:ok, %{type: "blob", sha: sha, name: bname}} ->
{:ok, meta} = Repositories.get_blob_meta(repo, sha)
body =
cond do
meta.is_binary -> {:binary, meta.size}
meta.size > @max_inline_bytes -> {:too_large, meta.size}
true -> load_text(repo, sha, bname, socket.assigns[:editor_theme])
end
{:ok,
socket
|> assign(:repo, repo)
|> assign(:handle, Storage.repo_handle(repo))
|> GitGudWeb.RepoLive.Header.assign_chrome(repo)
|> assign(:ref, ref)
|> assign(:path, path)
|> assign(:blob_name, bname)
|> assign(:meta, meta)
|> assign(:body, body)
|> assign(:head_sha, head_sha)
|> assign(:page_title, "#{owner}/#{name}: #{path}")}
_ ->
{:ok, push_navigate(socket, to: ~p"/r/#{owner}/#{name}")}
end
end
defp load_text(repo, sha, name, theme) do
case Repositories.get_blob_bytes(repo, sha) do
{:ok, bytes} ->
cond do
String.ends_with?(String.downcase(name), ".md") ->
{:markdown, bytes, GitGud.Markdown.to_html(bytes, theme: theme)}
true ->
{:source, bytes,
GitGud.SyntaxHighlight.highlight(bytes, filename: name, theme: theme)}
end
_ ->
{:error, "unreadable"}
end
end
@impl true
def render(assigns) do
~H"""
<Layouts.app flash={@flash} current_scope={@current_scope}>
<div class="space-y-4">
<GitGudWeb.RepoLive.Header.header
repo={@repo}
handle={@handle}
current_scope={@current_scope}
has_packages?={@has_packages?}
can_admin?={@can_admin?}
latest_run={@latest_run}
/>
<nav class="text-sm font-mono">
<.link navigate={~p"/r/#{@handle}/#{@repo.name}"} class="link link-hover">
{@repo.name}
</.link>
<span class="opacity-50"> @ </span>
<span>{@ref}</span>
<span class="opacity-50"> / </span>
<span>{@path}</span>
</nav>
<div class="flex items-center justify-between text-xs opacity-70">
<span>{format_size(@meta.size)} · {if @meta.is_binary, do: "binary", else: "text"}</span>
<span class="font-mono">{Git.short(@head_sha)}</span>
</div>
<article class="border border-base-300 rounded-md overflow-hidden">
<%= case @body do %>
<% {:markdown, _src, html} -> %>
<div class="prose max-w-none p-6">
{Phoenix.HTML.raw(html)}
</div>
<% {:source, _src, html} -> %>
<div class="text-xs overflow-x-auto bg-base-200">
{Phoenix.HTML.raw(html)}
</div>
<% {:binary, _size} -> %>
<p class="p-6 text-sm opacity-70">Binary file. Use <code>git</code> to view.</p>
<% {:too_large, _size} -> %>
<p class="p-6 text-sm opacity-70">File too large to render in-browser.</p>
<% {:error, msg} -> %>
<p class="p-6 text-sm text-error">Error: {msg}</p>
<% end %>
</article>
</div>
</Layouts.app>
"""
end
defp format_size(b) when b < 1024, do: "#{b} B"
defp format_size(b) when b < 1_000_000, do: "#{Float.round(b / 1024, 1)} KiB"
defp format_size(b), do: "#{Float.round(b / 1_048_576, 1)} MiB"
end
4.0 KiB · text
5af55d9