diff --git a/lib/chiya/notes.ex b/lib/chiya/notes.ex index d214935..5fd7cb4 100644 --- a/lib/chiya/notes.ex +++ b/lib/chiya/notes.ex @@ -6,9 +6,9 @@ defmodule Chiya.Notes do import Ecto.Query, warn: false alias Chiya.Repo - alias Chiya.Notes.Note + alias Chiya.Notes.{Note, NoteImage} - @preloads [:channels] + @preloads [:channels, :images] @doc """ Returns the list of notes. @@ -129,4 +129,28 @@ defmodule Chiya.Notes do def change_note(%Note{} = note, attrs \\ %{}) do Note.changeset(note, attrs) end + + @doc """ + Creates a note image and attaches it to a note + """ + def create_note_image(attrs) do + case %NoteImage{} + |> NoteImage.insert_changeset(attrs) + |> Repo.insert() do + {:ok, note_image} -> + note_image + |> change_note_image(attrs) + |> Repo.update() + + {:error, changeset} -> + {:error, changeset} + end + end + + @doc """ + Returns an `%Ecto.Changeset{}` for tracking note_image changes. + """ + def change_note_image(%NoteImage{} = note_image, attrs \\ %{}) do + NoteImage.changeset(note_image, attrs) + end end diff --git a/lib/chiya/notes/note.ex b/lib/chiya/notes/note.ex index e785246..8128fbb 100644 --- a/lib/chiya/notes/note.ex +++ b/lib/chiya/notes/note.ex @@ -11,11 +11,13 @@ defmodule Chiya.Notes.Note do field :url, :string many_to_many :channels, Chiya.Channels.Channel, - # join_through: Chiya.Channels.ChannelNote, + # join_through: Chiya.Channels.ChannelNote, join_through: "channels_notes", join_keys: [note: :id, channel: :id], on_replace: :delete + has_many :images, Chiya.Notes.NoteImage + timestamps() end diff --git a/lib/chiya/notes/note_image.ex b/lib/chiya/notes/note_image.ex new file mode 100644 index 0000000..aa7c9c8 --- /dev/null +++ b/lib/chiya/notes/note_image.ex @@ -0,0 +1,28 @@ +defmodule Chiya.Notes.NoteImage do + use Ecto.Schema + use Waffle.Ecto.Schema + import Ecto.Changeset + + schema "note_images" do + field :content, :string, default: "" + field :path, Chiya.Uploaders.NoteImage.Type + field :note_id, :id + + timestamps() + end + + @doc false + def changeset(note_image, attrs) do + note_image + |> cast(attrs, [:content, :note_id]) + |> cast_attachments(attrs, [:path], allow_paths: true) + |> validate_required([:path, :note_id]) + end + + @doc false + def insert_changeset(note_image, attrs) do + note_image + |> cast(attrs, [:note_id]) + |> validate_required([:note_id]) + end +end diff --git a/lib/chiya_web.ex b/lib/chiya_web.ex index 5b9c497..b314bf8 100644 --- a/lib/chiya_web.ex +++ b/lib/chiya_web.ex @@ -44,6 +44,7 @@ defmodule ChiyaWeb do import Plug.Conn import ChiyaWeb.Gettext + import Phoenix.LiveView.Controller unquote(verified_routes()) end diff --git a/lib/chiya_web/controllers/note_controller.ex b/lib/chiya_web/controllers/note_controller.ex index 091029b..83bd2ce 100644 --- a/lib/chiya_web/controllers/note_controller.ex +++ b/lib/chiya_web/controllers/note_controller.ex @@ -29,8 +29,9 @@ defmodule ChiyaWeb.NoteController do end def show(conn, %{"id" => id}) do - note = Notes.get_note!(id) - render(conn, :show, note: note) + # note = Notes.get_note!(id) + # render(conn, :show, note: note) + live_render(conn, NoteShowLive, session: %{ "note_id" => id }) end def edit(conn, %{"id" => id}) do diff --git a/lib/chiya_web/live/note_show_live.ex b/lib/chiya_web/live/note_show_live.ex new file mode 100644 index 0000000..e71f6d6 --- /dev/null +++ b/lib/chiya_web/live/note_show_live.ex @@ -0,0 +1,106 @@ +defmodule ChiyaWeb.NoteShowLive do + use ChiyaWeb, :live_view + + alias Chiya.Notes + + @impl true + def render(assigns) do + ~H""" + <.header> + Note <%= @note.id %> + <:subtitle>This is a note record from your database. + <:actions> + <.link href={~p"/admin/notes/#{@note}/edit"}> + <.button>Edit note + + + + + <.list> + <:item title="Name"><%= @note.name %> + <:item title="Content"><%= @note.content %> + <:item title="Slug"><%= @note.slug %> + <:item title="Published at"><%= @note.published_at %> + <:item title="Kind"><%= @note.kind %> + <:item title="Url"><%= @note.url %> + + + <.line /> + +
+ <%= for image <- @note.images do %> +
+ +

Delete image

+
+ <% end %> +
+ + <.line /> + + <.header> + Note Images + <:subtitle>Add images here + + + <.simple_form + for={@image_form} + id="image_form" + phx-submit="update_image" + phx-change="validate_image" + multipart={true} + > + <.live_upload upload={@uploads.note_images} /> + + <:actions> + <.button phx-disable-with="Changing...">Add Images + + + + <.back navigate={~p"/admin/notes"}>Back to notes + """ + end + + @impl true + def mount(%{"id" => note_id}, _session, socket) do + image_changeset = Notes.change_note_image(%Chiya.Notes.NoteImage{}) + + {:ok, + socket + |> assign(:note, Notes.get_note_preloaded!(note_id)) + |> assign(:uploaded_files, []) + |> assign(:image_form, to_form(image_changeset)) + |> allow_upload(:note_images, accept: ~w(.jpg .jpeg .gif .png), max_entries: 100)} + end + + def handle_event("validate_image", _params, socket) do + {:noreply, socket} + end + + @impl Phoenix.LiveView + def handle_event("cancel-upload", %{"ref" => ref}, socket) do + {:noreply, cancel_upload(socket, :note_images, ref)} + end + + @impl Phoenix.LiveView + def handle_event("update_image", _params, socket) do + uploaded_files = + consume_uploaded_entries(socket, :note_images, fn %{path: path}, _entry -> + {:ok, _note_image} = + Notes.create_note_image(%{ + path: path, + note_id: socket.assigns.note.id + }) + + {:ok, path} + end) + + {:noreply, + socket + |> update(:uploaded_files, &(&1 ++ uploaded_files)) + |> assign(:note, Notes.get_note_preloaded!(socket.assigns.note.id))} + end +end diff --git a/lib/chiya_web/live/user_settings_live.ex b/lib/chiya_web/live/user_settings_live.ex index ae0bad8..57973ad 100644 --- a/lib/chiya_web/live/user_settings_live.ex +++ b/lib/chiya_web/live/user_settings_live.ex @@ -143,7 +143,10 @@ defmodule ChiyaWeb.UserSettingsLive do {:ok, path} end) - {:noreply, update(socket, :uploaded_files, &(&1 ++ uploaded_files))} + {:noreply, + socket + |> update(:uploaded_files, &(&1 ++ uploaded_files)) + |> assign(:user, Accounts.get_user!(user.id))} end def handle_event("validate_email", params, socket) do diff --git a/lib/chiya_web/router.ex b/lib/chiya_web/router.ex index a9cebf0..db48a3f 100644 --- a/lib/chiya_web/router.ex +++ b/lib/chiya_web/router.ex @@ -51,8 +51,10 @@ defmodule ChiyaWeb.Router do pipe_through [:browser, :require_authenticated_user] resources "/channels", ChannelController - resources "/notes", NoteController + resources "/notes", NoteController, except: [:show] resources "/settings", SettingController, singleton: true + + live "/notes/:id", NoteShowLive, :show end ## Authentication routes diff --git a/lib/chiya_web/uploaders/note_image.ex b/lib/chiya_web/uploaders/note_image.ex index d6ad52b..90670f5 100644 --- a/lib/chiya_web/uploaders/note_image.ex +++ b/lib/chiya_web/uploaders/note_image.ex @@ -5,12 +5,11 @@ defmodule Chiya.Uploaders.NoteImage do # Include ecto support (requires package waffle_ecto installed): # use Waffle.Ecto.Definition - @versions [:original, :dithered, :thumb, :thumb_dithered] + @versions [:original, :full, :full_dithered, :thumb, :thumb_dithered] # Whitelist file extensions: - def validate({file, _}) do - _file_extension = file.file_name |> Path.extname() |> String.downcase() - + def validate({_file, _}) do + # _file_extension = file.file_name |> Path.extname() |> String.downcase() # case Enum.member?(~w(.jpg .jpeg .gif .png), file_extension) do # true -> :ok # false -> {:error, "invalid file type"} @@ -20,8 +19,12 @@ defmodule Chiya.Uploaders.NoteImage do :ok end + def transform(:full, _) do + {:convert, "-strip -resize 1500000@ -gravity center -format png", :png} + end + # Define a resize transformation: - def transform(:dithered, _) do + def transform(:full_dithered, _) do {:convert, "-strip -resize 1500000@ -colors 24 -dither FloydSteinberg -format png", :png} end @@ -43,21 +46,12 @@ defmodule Chiya.Uploaders.NoteImage do end # Override the storage directory: - def storage_dir(_version, {_file, _scope}) do - "uploads/user/avatar" + def storage_dir(_version, {_file, %{id: image_id, note_id: note_id}}) do + "uploads/note/#{note_id}/#{image_id}" end # Provide a default URL if there hasn't been a file uploaded # def default_url(version, scope) do # "/images/avatars/default_#{version}.png" # end - - # Specify custom headers for s3 objects - # Available options are [:cache_control, :content_disposition, - # :content_encoding, :content_length, :content_type, - # :expect, :expires, :storage_class, :website_redirect_location] - # - # def s3_object_headers(version, {file, scope}) do - # [content_type: MIME.from_path(file.file_name)] - # end end diff --git a/priv/repo/migrations/20230308191004_create_note_images.exs b/priv/repo/migrations/20230308191004_create_note_images.exs new file mode 100644 index 0000000..77e0c6c --- /dev/null +++ b/priv/repo/migrations/20230308191004_create_note_images.exs @@ -0,0 +1,15 @@ +defmodule Chiya.Repo.Migrations.CreateNoteImages do + use Ecto.Migration + + def change do + create table(:note_images) do + add :path, :string + add :content, :text + add :note_id, references(:notes, on_delete: :nothing) + + timestamps() + end + + create index(:note_images, [:note_id]) + end +end