add tags \o/
This commit is contained in:
parent
03256e4f9d
commit
ebacb7919c
20 changed files with 434 additions and 16 deletions
|
@ -5,9 +5,9 @@ defmodule Chiya.Notes do
|
||||||
|
|
||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Chiya.Repo
|
alias Chiya.Repo
|
||||||
alias Chiya.Notes.{Note, NoteImage, NoteNote, References}
|
alias Chiya.Notes.{Note, NoteImage, NoteNote, NoteTag}
|
||||||
|
|
||||||
@preloads [:channels, :images, :links_from, :links_to]
|
@preloads [:channels, :images, :links_from, :links_to, :tags]
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Returns the list of notes.
|
Returns the list of notes.
|
||||||
|
@ -158,6 +158,7 @@ defmodule Chiya.Notes do
|
||||||
note
|
note
|
||||||
|> Note.changeset(attrs)
|
|> Note.changeset(attrs)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
|
|> Chiya.Tags.TagUpdater.update_tags(attrs)
|
||||||
|> Chiya.Notes.References.update_references(attrs)
|
|> Chiya.Notes.References.update_references(attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -248,4 +249,18 @@ defmodule Chiya.Notes do
|
||||||
def change_note_image(%NoteImage{} = note_image, attrs \\ %{}) do
|
def change_note_image(%NoteImage{} = note_image, attrs \\ %{}) do
|
||||||
NoteImage.update_changeset(note_image, attrs)
|
NoteImage.update_changeset(note_image, attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_note_tag!(attrs \\ %{}) do
|
||||||
|
Repo.get_by!(NoteTag, attrs)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_note_tag(attrs \\ %{}) do
|
||||||
|
%NoteTag{}
|
||||||
|
|> NoteTag.changeset(attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_note_tag(%NoteTag{} = note_tag) do
|
||||||
|
Repo.delete(note_tag)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,8 +31,16 @@ defmodule Chiya.Notes.Note do
|
||||||
join_through: Chiya.Notes.NoteNote,
|
join_through: Chiya.Notes.NoteNote,
|
||||||
join_keys: [source_id: :id, target_id: :id]
|
join_keys: [source_id: :id, target_id: :id]
|
||||||
|
|
||||||
|
many_to_many :tags, Chiya.Tags.Tag,
|
||||||
|
join_through: Chiya.Notes.NoteTag,
|
||||||
|
join_keys: [note_id: :id, tag_id: :id]
|
||||||
|
|
||||||
has_many :images, Chiya.Notes.NoteImage
|
has_many :images, Chiya.Notes.NoteImage
|
||||||
|
|
||||||
|
field :tags_string, :string,
|
||||||
|
virtual: true,
|
||||||
|
default: ""
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
19
lib/chiya/notes/note_tag.ex
Normal file
19
lib/chiya/notes/note_tag.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule Chiya.Notes.NoteTag do
|
||||||
|
@moduledoc """
|
||||||
|
The NoteTag module
|
||||||
|
"""
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
schema "notes_tags" do
|
||||||
|
belongs_to :note, Chiya.Notes.Note
|
||||||
|
belongs_to :tag, Chiya.Tags.Tag
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(note_tag, attrs) do
|
||||||
|
note_tag
|
||||||
|
|> cast(attrs, [:note_id, :tag_id])
|
||||||
|
|> validate_required([:note_id, :tag_id])
|
||||||
|
end
|
||||||
|
end
|
|
@ -117,6 +117,7 @@ defmodule Chiya.Notes.References do
|
||||||
attrs = get_attrs(origin_note.id, linked_note.id)
|
attrs = get_attrs(origin_note.id, linked_note.id)
|
||||||
note_note = Chiya.Notes.get_note_note(attrs)
|
note_note = Chiya.Notes.get_note_note(attrs)
|
||||||
|
|
||||||
|
if note_note do
|
||||||
case Chiya.Notes.delete_note_note(note_note) do
|
case Chiya.Notes.delete_note_note(note_note) do
|
||||||
{:ok, _note_note} ->
|
{:ok, _note_note} ->
|
||||||
Logger.info("Reference to '#{slug}' deleted")
|
Logger.info("Reference to '#{slug}' deleted")
|
||||||
|
@ -124,6 +125,10 @@ defmodule Chiya.Notes.References do
|
||||||
error ->
|
error ->
|
||||||
Logger.warn(error)
|
Logger.warn(error)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
Logger.debug("Note '#{slug}' does not exist anymore.")
|
||||||
|
end
|
||||||
|
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
136
lib/chiya/tags.ex
Normal file
136
lib/chiya/tags.ex
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
defmodule Chiya.Tags do
|
||||||
|
@moduledoc """
|
||||||
|
The Tags context.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import Ecto.Query, warn: false
|
||||||
|
alias Chiya.Repo
|
||||||
|
|
||||||
|
alias Chiya.Tags.Tag
|
||||||
|
|
||||||
|
@preloads [:notes]
|
||||||
|
defp with_preloads(query), do: preload(query, ^@preloads)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Preloads a tag with all defined preloads.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> preload_tag(tag)
|
||||||
|
%Tag{}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def preload_tag(tag), do: Repo.preload(tag, @preloads)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns the list of tags.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> list_tags()
|
||||||
|
[%Tag{}, ...]
|
||||||
|
|
||||||
|
"""
|
||||||
|
def list_tags do
|
||||||
|
Tag
|
||||||
|
|> with_preloads()
|
||||||
|
|> order_by(:title)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single tag.
|
||||||
|
|
||||||
|
Raises `Ecto.NoResultsError` if the Tag does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_tag!(123)
|
||||||
|
%Tag{}
|
||||||
|
|
||||||
|
iex> get_tag!(456)
|
||||||
|
** (Ecto.NoResultsError)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def get_tag_by_slug!(slug), do: Repo.get_by!(Tag, slug: slug) |> preload_tag()
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Gets a single tag.
|
||||||
|
|
||||||
|
Returns nil if the Tag does not exist.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> get_tag(123)
|
||||||
|
%Tag{}
|
||||||
|
|
||||||
|
iex> get_tag(456)
|
||||||
|
nil
|
||||||
|
"""
|
||||||
|
def get_tag_by_slug(slug), do: Repo.get_by(Tag, slug: slug) |> preload_tag()
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Creates a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> create_tag(%{field: value})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> create_tag(%{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def create_tag(attrs \\ %{}) do
|
||||||
|
%Tag{}
|
||||||
|
|> Tag.changeset(attrs)
|
||||||
|
|> Repo.insert()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_tag(tag, %{field: new_value})
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> update_tag(tag, %{field: bad_value})
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def update_tag(%Tag{} = tag, attrs) do
|
||||||
|
tag
|
||||||
|
|> Tag.changeset(attrs)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Deletes a tag.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> delete_tag(tag)
|
||||||
|
{:ok, %Tag{}}
|
||||||
|
|
||||||
|
iex> delete_tag(tag)
|
||||||
|
{:error, %Ecto.Changeset{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def delete_tag(%Tag{} = tag) do
|
||||||
|
Repo.delete(tag)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Returns an `%Ecto.Changeset{}` for tracking tag changes.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> change_tag(tag)
|
||||||
|
%Ecto.Changeset{data: %Tag{}}
|
||||||
|
|
||||||
|
"""
|
||||||
|
def change_tag(%Tag{} = tag, attrs \\ %{}) do
|
||||||
|
Tag.changeset(tag, attrs)
|
||||||
|
end
|
||||||
|
end
|
32
lib/chiya/tags/tag.ex
Normal file
32
lib/chiya/tags/tag.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule Chiya.Tags.Tag do
|
||||||
|
@moduledoc """
|
||||||
|
The Tag Schema
|
||||||
|
"""
|
||||||
|
use Ecto.Schema
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Chiya.Tags.TagSlug
|
||||||
|
|
||||||
|
schema "tags" do
|
||||||
|
field :name, :string
|
||||||
|
field :slug, TagSlug.Type
|
||||||
|
|
||||||
|
field :content, :string
|
||||||
|
|
||||||
|
field :icon, :string
|
||||||
|
field :regex, :string
|
||||||
|
|
||||||
|
many_to_many :notes, Chiya.Notes.Note, join_through: "notes_tags"
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def changeset(tag, attrs) do
|
||||||
|
tag
|
||||||
|
|> cast(attrs, [:name, :content, :icon, :regex])
|
||||||
|
|> validate_required([:name])
|
||||||
|
|> unique_constraint(:name)
|
||||||
|
|> TagSlug.maybe_generate_slug()
|
||||||
|
|> TagSlug.unique_constraint()
|
||||||
|
end
|
||||||
|
end
|
3
lib/chiya/tags/tag_slug.ex
Normal file
3
lib/chiya/tags/tag_slug.ex
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
defmodule Chiya.Tags.TagSlug do
|
||||||
|
use EctoAutoslugField.Slug, from: :name, to: :slug
|
||||||
|
end
|
112
lib/chiya/tags/tag_updater.ex
Normal file
112
lib/chiya/tags/tag_updater.ex
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
defmodule Chiya.Tags.TagUpdater do
|
||||||
|
@moduledoc """
|
||||||
|
Updates tags for a schema like notes by adding new ones and removing old ones.
|
||||||
|
"""
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
alias Chiya.{Notes, Tags}
|
||||||
|
alias Chiya.Notes.Note
|
||||||
|
|
||||||
|
def update_tags({:ok, %Note{} = note}, attrs) do
|
||||||
|
update_tags(note, attrs)
|
||||||
|
|
||||||
|
{:ok, note}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Updates the tags for the given schema
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> update_tags(note, "foo,bar")
|
||||||
|
|
||||||
|
"""
|
||||||
|
def update_tags(schema, %{tags_string: new_tags} = attrs) when is_map(attrs) do
|
||||||
|
update_tags(schema, new_tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_tags(schema, %{"tags_string" => new_tags} = attrs) when is_map(attrs) do
|
||||||
|
update_tags(schema, new_tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_tags(schema, attrs) when is_map(attrs) do
|
||||||
|
schema
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_tags(schema, new_tags) when is_binary(new_tags) do
|
||||||
|
update_tags(schema, split_tags(new_tags))
|
||||||
|
end
|
||||||
|
|
||||||
|
def update_tags(%{tags: tags} = schema, new_tags) when is_list(new_tags) do
|
||||||
|
old_tags = Enum.map(tags, fn tag -> tag.name end)
|
||||||
|
|
||||||
|
Logger.info("Adding tags #{inspect(new_tags -- old_tags)}")
|
||||||
|
Logger.info("Removing tags #{inspect(old_tags -- new_tags)}")
|
||||||
|
|
||||||
|
schema
|
||||||
|
|> add_tags(new_tags -- old_tags)
|
||||||
|
|> remove_tags(old_tags -- new_tags)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp split_tags(tags_string) when is_binary(tags_string) do
|
||||||
|
tags_string
|
||||||
|
|> String.split(",")
|
||||||
|
|> Enum.map(&String.trim/1)
|
||||||
|
|> Enum.filter(&(String.length(&1) > 0))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_tags(schema, tags) do
|
||||||
|
Enum.each(tags, &add_tag(schema, &1))
|
||||||
|
schema
|
||||||
|
end
|
||||||
|
|
||||||
|
defp add_tag(%{id: schema_id} = schema, tag) when is_binary(tag) do
|
||||||
|
slug = Slugger.slugify_downcase(tag)
|
||||||
|
|
||||||
|
Logger.debug("Looking up tag [#{tag}] with slug [#{slug}]")
|
||||||
|
|
||||||
|
{:ok, tag} =
|
||||||
|
case Tags.get_tag(slug) do
|
||||||
|
nil ->
|
||||||
|
Logger.debug("Tag [#{tag}] does not exist. Creating.")
|
||||||
|
Tags.create_tag(%{name: tag})
|
||||||
|
|
||||||
|
tag ->
|
||||||
|
Logger.debug("Tag already exists. Returning.")
|
||||||
|
{:ok, tag}
|
||||||
|
end
|
||||||
|
|
||||||
|
case schema do
|
||||||
|
%Note{} ->
|
||||||
|
attrs = %{
|
||||||
|
note_id: schema_id,
|
||||||
|
tag_id: tag.id
|
||||||
|
}
|
||||||
|
|
||||||
|
{:ok, _note_tag} = Notes.create_note_tag(attrs)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_tags(schema, tags) do
|
||||||
|
Enum.each(tags, &remove_tag(schema, &1))
|
||||||
|
schema
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remove_tag(schema, tag) do
|
||||||
|
slug = Slugger.slugify_downcase(tag)
|
||||||
|
|
||||||
|
if tag = Tags.get_tag(slug) do
|
||||||
|
case schema do
|
||||||
|
%Note{} ->
|
||||||
|
attrs = %{tag_id: tag.id, note_id: schema.id}
|
||||||
|
note_tag = Notes.get_note_tag!(attrs)
|
||||||
|
|
||||||
|
{:ok, _} = Notes.delete_note_tag(note_tag)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Logger.warn("Tag with slug #{slug} was not removed.")
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -24,7 +24,12 @@ defmodule ChiyaWeb.NoteController do
|
||||||
|
|
||||||
def new(conn, _params) do
|
def new(conn, _params) do
|
||||||
changeset = Notes.change_note(%Note{})
|
changeset = Notes.change_note(%Note{})
|
||||||
render(conn, :new, changeset: changeset, channels: to_channel_options())
|
|
||||||
|
render(conn, :new,
|
||||||
|
changeset: changeset,
|
||||||
|
channels: to_channel_options(),
|
||||||
|
tags: []
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(conn, %{"note" => note_params}) do
|
def create(conn, %{"note" => note_params}) do
|
||||||
|
@ -50,7 +55,13 @@ defmodule ChiyaWeb.NoteController do
|
||||||
def edit(conn, %{"id" => id}) do
|
def edit(conn, %{"id" => id}) do
|
||||||
note = Notes.get_note_preloaded!(id)
|
note = Notes.get_note_preloaded!(id)
|
||||||
changeset = Notes.change_note(note)
|
changeset = Notes.change_note(note)
|
||||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
|
||||||
|
render(conn, :edit,
|
||||||
|
note: note,
|
||||||
|
changeset: changeset,
|
||||||
|
channels: to_channel_options(),
|
||||||
|
tags: note.tags
|
||||||
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def update(conn, %{"id" => id, "note" => note_params}) do
|
def update(conn, %{"id" => id, "note" => note_params}) do
|
||||||
|
@ -64,7 +75,11 @@ defmodule ChiyaWeb.NoteController do
|
||||||
|> redirect(to: ~p"/admin/notes/#{note}")
|
|> redirect(to: ~p"/admin/notes/#{note}")
|
||||||
|
|
||||||
{:error, %Ecto.Changeset{} = changeset} ->
|
{:error, %Ecto.Changeset{} = changeset} ->
|
||||||
render(conn, :edit, note: note, changeset: changeset, channels: to_channel_options())
|
render(conn, :edit,
|
||||||
|
note: note,
|
||||||
|
changeset: changeset,
|
||||||
|
channels: to_channel_options(),
|
||||||
|
tags: note.tags)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,13 @@ defmodule ChiyaWeb.NoteHTML do
|
||||||
attr :changeset, Ecto.Changeset, required: true
|
attr :changeset, Ecto.Changeset, required: true
|
||||||
attr :action, :string, required: true
|
attr :action, :string, required: true
|
||||||
attr :channels, :list, required: true
|
attr :channels, :list, required: true
|
||||||
|
attr :tags, :list, required: true
|
||||||
|
|
||||||
def note_form(assigns)
|
def note_form(assigns)
|
||||||
|
|
||||||
def selected_channels(changeset), do: Enum.map(changeset.data.channels, fn c -> c.id end)
|
def selected_channels(changeset), do:
|
||||||
|
Enum.map(changeset.data.channels, fn c -> c.id end)
|
||||||
|
|
||||||
|
def tags_to_string(tags), do:
|
||||||
|
Enum.map_join(tags, ", ", fn t -> t.name end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
<.note_form changeset={@changeset} action={~p"/admin/notes/#{@note}"} channels={@channels} />
|
<.note_form changeset={@changeset} action={~p"/admin/notes/#{@note}"} channels={@channels} tags={@tags} />
|
||||||
|
|
||||||
<.back navigate={~p"/admin/notes"}>Back to notes</.back>
|
<.back navigate={~p"/admin/notes"}>Back to notes</.back>
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
<:subtitle>Use this form to manage note records in your database.</:subtitle>
|
||||||
</.header>
|
</.header>
|
||||||
|
|
||||||
<.note_form changeset={@changeset} action={~p"/admin/notes"} channels={@channels} />
|
<.note_form changeset={@changeset} action={~p"/admin/notes"} channels={@channels} tags={@tags} />
|
||||||
|
|
||||||
<.back navigate={~p"/admin/notes"}>Back to notes</.back>
|
<.back navigate={~p"/admin/notes"}>Back to notes</.back>
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
options={Ecto.Enum.values(Chiya.Notes.Note, :kind)}
|
options={Ecto.Enum.values(Chiya.Notes.Note, :kind)}
|
||||||
/>
|
/>
|
||||||
<.input field={f[:url]} type="text" label="Url" />
|
<.input field={f[:url]} type="text" label="Url" />
|
||||||
|
<.input field={f[:tags_string]} type="text" label="Tags" value={tags_to_string(@tags)} />
|
||||||
<.input
|
<.input
|
||||||
field={f[:channels]}
|
field={f[:channels]}
|
||||||
type="select"
|
type="select"
|
||||||
|
@ -22,7 +23,9 @@
|
||||||
options={@channels}
|
options={@channels}
|
||||||
value={selected_channels(@changeset)}
|
value={selected_channels(@changeset)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<:actions>
|
<:actions>
|
||||||
<.button>Save Note</.button>
|
<.button>Save Note</.button>
|
||||||
</:actions>
|
</:actions>
|
||||||
</.simple_form>
|
</.simple_form>
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,16 @@ defmodule ChiyaWeb.PageController do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def tag(conn, %{"slug" => tag_slug}) do
|
||||||
|
tag = Chiya.Tags.get_tag_by_slug!(tag_slug)
|
||||||
|
|
||||||
|
render(conn, :tag,
|
||||||
|
layout: {ChiyaWeb.Layouts, "public.html"},
|
||||||
|
tag: tag,
|
||||||
|
page_title: tag.name
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def note(conn, %{"slug" => note_slug}) do
|
def note(conn, %{"slug" => note_slug}) do
|
||||||
note = Chiya.Notes.get_note_by_slug_preloaded!(note_slug)
|
note = Chiya.Notes.get_note_by_slug_preloaded!(note_slug)
|
||||||
|
|
||||||
|
|
|
@ -2,4 +2,7 @@ defmodule ChiyaWeb.PageHTML do
|
||||||
use ChiyaWeb, :html_public
|
use ChiyaWeb, :html_public
|
||||||
|
|
||||||
embed_templates "page_html/*"
|
embed_templates "page_html/*"
|
||||||
|
|
||||||
|
def tag_list([]), do: "No Tags"
|
||||||
|
def tag_list(tags), do: Enum.map_join(tags, ", ", fn t -> t.name end)
|
||||||
end
|
end
|
||||||
|
|
|
@ -9,6 +9,13 @@
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<span>Last Updated</span>
|
<span>Last Updated</span>
|
||||||
<time class="text-theme-primary font-semibold"><%= pretty_date(@note.updated_at) %></time>
|
<time class="text-theme-primary font-semibold"><%= pretty_date(@note.updated_at) %></time>
|
||||||
|
<span>·</span>
|
||||||
|
<span>Tags</span>
|
||||||
|
<span class="text-theme-primary font-semibold">
|
||||||
|
<%= for tag <- @note.tags do %>
|
||||||
|
<a href={~p"/t/#{tag.slug}"}><%= tag.name %></a>
|
||||||
|
<% end %>
|
||||||
|
</span>
|
||||||
<%= if @current_user do %>
|
<%= if @current_user do %>
|
||||||
<span>·</span>
|
<span>·</span>
|
||||||
<a href={~p"/admin/notes/#{@note}/edit"} class="text-theme-primary font-semibold">Edit</a>
|
<a href={~p"/admin/notes/#{@note}/edit"} class="text-theme-primary font-semibold">Edit</a>
|
||||||
|
|
20
lib/chiya_web/controllers/page_html/tag.html.heex
Normal file
20
lib/chiya_web/controllers/page_html/tag.html.heex
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<div class="mx-auto max-w-xl mx-4 lg:mx-0">
|
||||||
|
<h1 class="mt-16 text-[2rem] font-semibold font-serif leading-10 tracking-tighter text-theme-heading">
|
||||||
|
Tagged with “<%= @tag.name %>”
|
||||||
|
</h1>
|
||||||
|
<p class="mt-4 text-base leading-7 text-theme-base">
|
||||||
|
<%= @tag.content %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="w-full mt-6 sm:w-auto flex flex-col gap-1.5">
|
||||||
|
<%= for note <- @tag.notes do %>
|
||||||
|
<a
|
||||||
|
href={~p"/#{note.slug}"}
|
||||||
|
class="rounded -mx-2 -my-0.5 px-2 py-0.5 hover:bg-theme-primary/10 transition"
|
||||||
|
>
|
||||||
|
<span class="text-theme-primary text-lg font-semibold leading-8"><%= note.name %></span>
|
||||||
|
<span class="text-theme-base text-sm"><%= pretty_date(note.published_at) %></span>
|
||||||
|
</a>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</div>
|
|
@ -35,6 +35,7 @@ defmodule ChiyaWeb.NoteShowLive do
|
||||||
<:item title="Channels"><%= @channels %></:item>
|
<:item title="Channels"><%= @channels %></:item>
|
||||||
<:item title="Kind"><%= @note.kind %></:item>
|
<:item title="Kind"><%= @note.kind %></:item>
|
||||||
<:item title="Url"><%= @note.url %></:item>
|
<:item title="Url"><%= @note.url %></:item>
|
||||||
|
<:item title="Tags"><%= note_tags(@note.tags) %></:item>
|
||||||
<:item title="Links outgoing"><%= note_links(@note.links_to) %></:item>
|
<:item title="Links outgoing"><%= note_links(@note.links_to) %></:item>
|
||||||
<:item title="Links incoming"><%= note_links(@note.links_from) %></:item>
|
<:item title="Links incoming"><%= note_links(@note.links_from) %></:item>
|
||||||
</.list>
|
</.list>
|
||||||
|
@ -151,7 +152,6 @@ defmodule ChiyaWeb.NoteShowLive do
|
||||||
{:noreply, assign(socket, :note, Notes.get_note_preloaded!(socket.assigns.note.id))}
|
{:noreply, assign(socket, :note, Notes.get_note_preloaded!(socket.assigns.note.id))}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp note_links(notes) do
|
defp note_links(notes), do: Enum.map_join(notes, ", ", fn n -> n.name end)
|
||||||
Enum.map_join(notes, ", ", fn n -> n.name end)
|
defp note_tags(tags), do: Enum.map_join(tags, ", ", fn t -> t.name end)
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -117,6 +117,7 @@ defmodule ChiyaWeb.Router do
|
||||||
|
|
||||||
get "/:slug", PageController, :note
|
get "/:slug", PageController, :note
|
||||||
get "/c/:slug", PageController, :channel
|
get "/c/:slug", PageController, :channel
|
||||||
|
get "/t/:slug", PageController, :tag
|
||||||
get "/", PageController, :home
|
get "/", PageController, :home
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
24
priv/repo/migrations/20230410153445_add_tags.exs
Normal file
24
priv/repo/migrations/20230410153445_add_tags.exs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule Chiya.Repo.Migrations.AddTags do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create table(:tags) do
|
||||||
|
add :name, :string
|
||||||
|
add :slug, :string
|
||||||
|
add :content, :text
|
||||||
|
add :icon, :string
|
||||||
|
add :regex, :string
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
create table(:notes_tags) do
|
||||||
|
add :note_id, references(:notes, on_delete: :delete_all)
|
||||||
|
add :tag_id, references(:tags, on_delete: :delete_all)
|
||||||
|
end
|
||||||
|
|
||||||
|
create unique_index(:tags, [:slug])
|
||||||
|
create unique_index(:tags, [:name])
|
||||||
|
create unique_index(:notes_tags, [:note_id, :tag_id])
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue