From 35de4c9b1f5cbf308998bb8e47305dd8e2e24d98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lui=CC=81s=20Arteiro?= Date: Mon, 30 Oct 2023 21:11:40 +0000 Subject: [PATCH] feat: Automatically consuming entries. #1 --- README.md | 161 +++++++++++++++++++++++++++ lib/app_web/live/page_live.ex | 41 ++++++- lib/app_web/live/page_live.html.heex | 50 +++++---- 3 files changed, 229 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 3ee992c..7959707 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ within `Phoenix`! - [0. Creating a fresh `Phoenix` project](#0-creating-a-fresh-phoenix-project) - [1. Installing initial dependencies](#1-installing-initial-dependencies) - [2. Adding `LiveView` capabilities to our project](#2-adding-liveview-capabilities-to-our-project) + - [3. Receiving image files](#3-receiving-image-files) - [_Please_ Star the repo! ⭐️](#please-star-the-repo-️) @@ -279,6 +280,166 @@ This means we've successfully added `LiveView` and changed our view! +## 3. Receiving image files + +Now, let's start by receiving some image files. +In order to classify them, we need to have access to begin with, +right? + +With `LiveView`, +we can easily do this by using +[`allow_upload/3`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#allow_upload/3) +when mounting our `LiveView`. +With this function, we can easily accept +file uploads with progress. +We can define file types, max number of entries, +max file size, +validate the uploaded file and much more! + +Firstly, +let's make some changes to +`lib/app_web/live/page_live.html.heex`. + +```html +<.flash_group flash={@flash} /> +
+
+
+
+

Image Upload

+

Drag your images and they'll be uploaded to the cloud! ☁️

+

You may add up to <%= @uploads.image_list.max_entries %> exhibits at a time.

+ + +
+ +
+
+
+ +
+ +

or drag and drop

+
+

PNG, JPG, GIF up to 10MB

+
+
+
+
+
+
+
+
+``` + +We've added a few features: + +- used [`<.live_file_input/>`](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#live_file_input/1) +for `LiveView` file upload. +We've wrapped this component +with an element that is annotated with the `phx-drop-target` attribute +pointing to the DOM `id` of the file input. +- because `<.live_file_input/>` is being used, +we need to annotate its wrapping element +with `phx-submit` and `phx-change`, +as per https://hexdocs.pm/phoenix_live_view/uploads.html#render-reactive-elements. + +Because we've added these bindings, +we need to add the event handlers in +`lib/app_web/live/imgup_live.ex`. +Open it and update it to: + +```elixir +defmodule AppWeb.PageLive do + use AppWeb, :live_view + + @impl true + def mount(_params, _session, socket) do + {:ok, + socket + |> assign(:uploaded_files, []) + |> allow_upload(:image_list, + accept: ~w(image/*), + auto_upload: true, + progress: &handle_progress/3, + max_entries: 1, + chunk_size: 64_000 + )} + end + + @impl true + def handle_event("validate", _params, socket) do + {:noreply, socket} + end + + @impl true + def handle_event("remove-selected", %{"ref" => ref}, socket) do + {:noreply, cancel_upload(socket, :image_list, ref)} + end + + @impl true + def handle_event("save", _params, socket) do + {:noreply, socket} + end + + defp handle_progress(:image_list, entry, socket) do + if entry.done? do + uploaded_file = + consume_uploaded_entry(socket, entry, fn %{} = _meta -> + {:ok, entry} + end) + end + + {:noreply, socket} + end +end +``` + +- when `mount/3`ing the LiveView, +we are creating a list of uploaded images and assigning it to the socket +`uploaded_files`. +Additionally, we are using the `allow_upload/3` function to define our upload configuration. +The most important settings here are `auto_upload` set to `true` +and the `progress` fields. +By configuring these two properties, +we are telling `LiveView` that *whenever the person uploads a file*, +**it is processed immediately and consumed**. +- the `progress` field is handled by the `handle_progress/3` function. +We consume the file in this function by using +[`consume_uploaded_entry/3`](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html#consume_uploaded_entry/3). +Whilst consuming the entry/file, +we can access its path and then use it to our heart's content. +*For now*, we don't need to use it. +But we will in the future to feed our image classifier with it! +After the callback function is executed, +this function "consumes the entry", +essentially deleting the image from the temporary folder +and removing it from the uploaded files list. +- the `"validate"`, `"remove-selected"`, `"save"` event handlers +are called whenever the person uploads the image, +wants to remove it from the list of uploaded images +and when wants to submit the form, +respectively. +You may see that we're not doing much with these handlers; +we're simply replying with a `:noreply` +because we don't need to do anything with them. + +And that's it! +If you run `mix phx.server`, +nothing will change. +However, if you print the `uploaded_file` # _Please_ Star the repo! ⭐️ diff --git a/lib/app_web/live/page_live.ex b/lib/app_web/live/page_live.ex index 023e79a..fbb1c17 100644 --- a/lib/app_web/live/page_live.ex +++ b/lib/app_web/live/page_live.ex @@ -3,6 +3,45 @@ defmodule AppWeb.PageLive do @impl true def mount(_params, _session, socket) do - {:ok, socket} + {:ok, + socket + |> assign(:uploaded_files, []) + |> allow_upload(:image_list, + accept: ~w(image/*), + auto_upload: true, + progress: &handle_progress/3, + max_entries: 1, + chunk_size: 64_000 + )} + end + + @impl true + def handle_event("validate", _params, socket) do + {:noreply, socket} + end + + @impl true + def handle_event("remove-selected", %{"ref" => ref}, socket) do + {:noreply, cancel_upload(socket, :image_list, ref)} + end + + @impl true + def handle_event("save", _params, socket) do + {:noreply, socket} + end + + defp handle_progress(:image_list, entry, socket) do + if entry.done? do + uploaded_file = + consume_uploaded_entry(socket, entry, fn %{} = meta -> + file_path = meta.path + + # Do something with file path and then consume entry. + # It will remove the uploaded file from the temporary folder and remove it from the uploaded_files list + {:ok, entry} + end) + end + + {:noreply, socket} end end diff --git a/lib/app_web/live/page_live.html.heex b/lib/app_web/live/page_live.html.heex index 69edbbc..32140fd 100644 --- a/lib/app_web/live/page_live.html.heex +++ b/lib/app_web/live/page_live.html.heex @@ -1,35 +1,41 @@ <.flash_group flash={@flash} />
-
-
-
-

Image Classifier

-

Drag your images and we'll run an AI model to caption it!

+
+
+

Image Upload

+

Drag your images and they'll be uploaded to the cloud! ☁️

+

You may add up to <%= @uploads.image_list.max_entries %> exhibits at a time.

-
+ +
-
-
-
- -
- -

or drag and drop

-
-

PNG, JPG, GIF up to 10MB

+
+
+
+ +
+ +

or drag and drop

+

PNG, JPG, GIF up to 10MB

-
- +