3.8 KiB · text History 06a9b53
defmodule GitGud.Syms do
@moduledoc """
"Syms" — stars, but with a set of symbols.
A user marks a repository with any of the instance's configured
symbols, several at once if they like. The set lives in config:
config :git_gud, syms: ["⭐", "🚀", "💔", "⁉️", "❤️"]
An empty list disables the feature: nothing renders, and marking is
refused. Dropping a symbol from the list only hides it — the rows stay
and reappear if it is added back, so an edit never destroys marks.
Everything read back through this module is filtered to the currently
configured set, so retired symbols stay invisible until then.
"""
import Ecto.Query, warn: false
alias GitGud.Accounts.User
alias GitGud.Repo
alias GitGud.Repositories.Repository
alias GitGud.Syms.RepositorySym
@default_symbols ["⭐", "🚀", "💔", "⁉️", "❤️"]
@doc """
The instance's symbol set, in configured order. Non-list or
non-binary entries are ignored rather than crashing a page render.
"""
def symbols do
case Application.get_env(:git_gud, :syms, @default_symbols) do
list when is_list(list) -> list |> Enum.filter(&is_binary/1) |> Enum.uniq()
_ -> []
end
end
@doc "False when the instance has configured no symbols."
def enabled?, do: symbols() != []
@doc "Is this one of the instance's current symbols?"
def known?(symbol), do: symbol in symbols()
@doc """
Add or remove one mark. Returns `{:ok, :marked | :unmarked}`, or
`{:error, :unknown_symbol}` for anything not currently configured —
which also covers the whole feature being disabled.
"""
def toggle(%User{id: uid}, %Repository{id: rid}, symbol) do
if known?(symbol) do
case Repo.get_by(RepositorySym, user_id: uid, repository_id: rid, symbol: symbol) do
nil ->
%RepositorySym{user_id: uid, repository_id: rid, symbol: symbol}
|> Ecto.Changeset.change()
|> Repo.insert()
|> case do
{:ok, _} -> {:ok, :marked}
err -> err
end
row ->
Repo.delete(row)
{:ok, :unmarked}
end
else
{:error, :unknown_symbol}
end
end
@doc "The symbols this user has put on this repo, configured ones only."
def marks(%User{id: uid}, %Repository{id: rid}) do
current = symbols()
from(s in RepositorySym,
where: s.user_id == ^uid and s.repository_id == ^rid,
select: s.symbol
)
|> Repo.all()
|> Enum.filter(&(&1 in current))
end
def marks(nil, _repo), do: []
@doc """
Counts per symbol for a repository, as `%{symbol => count}`. Only
configured symbols appear; symbols with no marks are absent.
"""
def counts(%Repository{id: rid}), do: counts_by(:repository_id, rid)
@doc "Counts per symbol across everything a user has marked."
def counts_for_user(%User{id: uid}), do: counts_by(:user_id, uid)
defp counts_by(field, id) do
current = symbols()
from(s in RepositorySym,
where: field(s, ^field) == ^id,
group_by: s.symbol,
select: {s.symbol, count(s.id)}
)
|> Repo.all()
|> Enum.filter(fn {sym, _} -> sym in current end)
|> Map.new()
end
@doc """
Repositories `user` marked with `symbol`, newest mark first.
Returns `[]` for a symbol the instance no longer configures. Callers
are responsible for visibility filtering — see
`GitGud.Repositories.can_read?/2` equivalents at the call site.
"""
def list_marked(%User{id: uid}, symbol) do
if known?(symbol) do
from(s in RepositorySym,
join: r in Repository,
on: r.id == s.repository_id,
where: s.user_id == ^uid and s.symbol == ^symbol,
order_by: [desc: s.inserted_at],
select: r
)
|> Repo.all()
|> Repo.preload([:owner, :organization])
else
[]
end
end
end