defmodule GitGudWeb.ModerationComponents do
@moduledoc """
Renders moderation-aware bodies for issues, issue comments, PRs, and
PR comments.
When a row's `moderated_at` is set, its `body` has been replaced with
a moderator note (carried on `moderation_note`). The pre-replacement
body lives on `original_body`. We render:
* Always: a small "Removed by moderator" banner with the note.
* For admins + the original author: a collapsed `<details>` with
the original body, rendered as Markdown like the live version.
`<.moderated_body>` is the shared component used by both issue and PR
show pages. The caller passes the schema struct, the viewer (the
current `User` or nil), the repo (for cross-reference resolution),
and the editor theme.
"""
use Phoenix.Component
import GitGudWeb.CoreComponents, only: [icon: 1]
alias GitGud.Markdown
attr :target, :map, required: true, doc: "issue / issue_comment / PR / pr_comment"
attr :viewer, :map, default: nil, doc: "current user (or nil) for visibility check"
attr :repo, :map, required: true
attr :theme, :string, default: "onedark"
attr :empty_text, :string, default: "No description."
def moderated_body(assigns) do
assigns = assign(assigns, :show_original?, can_view_original?(assigns.target, assigns.viewer))
~H"""
<%= cond do %>
<% moderated?(@target) -> %>
<div class="rounded border border-warning/40 bg-warning/10 p-3 text-sm space-y-2">
<p class="font-medium">
<.icon name="hero-shield-exclamation" class="size-4 inline" /> Removed by moderator
</p>
<p :if={@target.moderation_note && @target.moderation_note != ""} class="opacity-80 text-xs">
{@target.moderation_note}
</p>
<details :if={@show_original?} class="mt-1">
<summary class="cursor-pointer text-xs opacity-70 hover:opacity-100">
Show original
</summary>
<div class="prose max-w-none mt-2 text-sm">
{Phoenix.HTML.raw(Markdown.to_html(@target.original_body || "", theme: @theme, repo: @repo))}
</div>
</details>
</div>
<% @target.body && @target.body != "" -> %>
<div class="prose max-w-none">
{Phoenix.HTML.raw(Markdown.to_html(@target.body, theme: @theme, repo: @repo))}
</div>
<% true -> %>
<p class="opacity-50">{@empty_text}</p>
<% end %>
"""
end
defp moderated?(%{moderated_at: ts}) when not is_nil(ts), do: true
defp moderated?(_), do: false
@doc """
Visibility rule for the "Show original" toggle:
- Admins always see it.
- The original author sees it.
- Everyone else: no.
"""
def can_view_original?(_target, nil), do: false
def can_view_original?(target, %{id: viewer_id, is_admin: is_admin}) do
cond do
is_admin -> true
Map.get(target, :author_id) == viewer_id -> true
true -> false
end
end
def can_view_original?(_target, _viewer), do: false
end
3.0 KiB · text
5af55d9