Drop invalid uploads as they happen

This commit is contained in:
Chris McCord 2021-11-05 08:57:48 -04:00
parent d7ccc9282c
commit 3ff4ae2bea
5 changed files with 52 additions and 29 deletions

View file

@ -4,6 +4,15 @@ defmodule LiveBeatsWeb.LiveHelpers do
alias Phoenix.LiveView.JS alias Phoenix.LiveView.JS
def spinner(assigns) do
~H"""
<svg class="inline-block animate-spin h-2.5 w-2.5 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
</svg>
"""
end
def icon(assigns) do def icon(assigns) do
assigns = assigns =
assigns assigns

View file

@ -48,7 +48,8 @@ defmodule LiveBeatsWeb.SongLive.Index do
if connected?(socket) do if connected?(socket) do
MediaLibrary.subscribe(socket.assigns.current_user) MediaLibrary.subscribe(socket.assigns.current_user)
end end
{:ok, assign(socket, songs: list_songs(), active_id: nil)}
{:ok, assign(socket, songs: list_songs(), active_id: nil), temporary_assigns: [songs: []]}
end end
def handle_params(params, _url, socket) do def handle_params(params, _url, socket) do
@ -66,11 +67,6 @@ defmodule LiveBeatsWeb.SongLive.Index do
{:noreply, socket} {:noreply, socket}
end end
def handle_info({_ref, {:duration, entry_ref, result}}, socket) do
IO.inspect({:async_duration, entry_ref, result})
{:noreply, socket}
end
def handle_info({:play, %Song{} = song, _meta}, socket) do def handle_info({:play, %Song{} = song, _meta}, socket) do
{:noreply, play_song(socket, song)} {:noreply, play_song(socket, song)}
end end
@ -80,21 +76,20 @@ defmodule LiveBeatsWeb.SongLive.Index do
end end
defp pause_song(socket, song_id) do defp pause_song(socket, song_id) do
if old = Enum.find(socket.assigns.songs, fn song -> song.id == song_id end) do send_update(LiveBeatsWeb.SongLive.SongRow, id: "song-#{song_id}", action: :deactivate)
send_update(LiveBeatsWeb.SongLive.SongRow, id: "song-#{old.id}", action: :deactivate)
end
socket socket
end end
defp play_song(socket, %Song{} = song) do defp play_song(socket, %Song{} = song) do
socket = pause_song(socket, socket.assigns.active_id) send_update(LiveBeatsWeb.SongLive.SongRow, id: "song-#{song.id}", action: :activate)
next = Enum.find(socket.assigns.songs, &(&1.id == song.id))
if next do if socket.assigns.active_id do
send_update(LiveBeatsWeb.SongLive.SongRow, id: "song-#{next.id}", action: :activate) socket
|> pause_song(socket.assigns.active_id)
|> assign(active_id: song.id)
else
assign(socket, active_id: song.id)
end end
assign(socket, active_id: next.id)
end end
defp maybe_show_modal(socket) do defp maybe_show_modal(socket) do
@ -116,12 +111,6 @@ defmodule LiveBeatsWeb.SongLive.Index do
socket socket
end end
defp apply_action(socket, :edit, %{"id" => id}) do
socket
|> assign(:page_title, "Edit Song")
|> assign(:song, MediaLibrary.get_song!(id))
end
defp apply_action(socket, :new, _params) do defp apply_action(socket, :new, _params) do
socket socket
|> assign(:page_title, "Add Songs") |> assign(:page_title, "Add Songs")

View file

@ -11,7 +11,10 @@ defmodule LiveBeatsWeb.SongLive.SongEntryComponent do
<%= if @duration do %> <%= if @duration do %>
Title <span class="text-gray-400">(<%= MP3Stat.to_mmss(@duration) %>)</span> Title <span class="text-gray-400">(<%= MP3Stat.to_mmss(@duration) %>)</span>
<% else %> <% else %>
Title <span class="text-gray-400">(calculating duration...)</span> Title
<span class="text-gray-400">
(calculating duration <.spinner class="inline-block animate-spin h-2.5 w-2.5 text-gray-400"/>)
</span>
<% end %> <% end %>
</label> </label>
<input type="text" name={"songs[#{@ref}][title]"} value={@title} <input type="text" name={"songs[#{@ref}][title]"} value={@title}
@ -41,7 +44,7 @@ defmodule LiveBeatsWeb.SongLive.SongEntryComponent do
|> assign(:errors, changeset.errors) |> assign(:errors, changeset.errors)
|> assign(title: Ecto.Changeset.get_field(changeset, :title)) |> assign(title: Ecto.Changeset.get_field(changeset, :title))
|> assign(artist: Ecto.Changeset.get_field(changeset, :artist)) |> assign(artist: Ecto.Changeset.get_field(changeset, :artist))
|> assign(duration: IO.inspect(Ecto.Changeset.get_field(changeset, :duration))) |> assign(duration: Ecto.Changeset.get_field(changeset, :duration))
|> assign_new(:progress, fn -> 0 end)} |> assign_new(:progress, fn -> 0 end)}
end end
end end

View file

@ -21,7 +21,7 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
{:ok, {:ok,
socket socket
|> assign(assigns) |> assign(assigns)
|> assign(changesets: %{}) |> assign(changesets: %{}, error_messages: [])
|> allow_upload(:mp3, |> allow_upload(:mp3,
song_id: song.id, song_id: song.id,
auto_upload: true, auto_upload: true,
@ -34,7 +34,7 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
@impl true @impl true
def handle_event("validate", %{"_target" => ["mp3"]}, socket) do def handle_event("validate", %{"_target" => ["mp3"]}, socket) do
{:noreply, socket} {:noreply, drop_invalid_uploads(socket)}
end end
def handle_event("validate", %{"songs" => songs_params, "_target" => ["songs", _, _]}, socket) do def handle_event("validate", %{"songs" => songs_params, "_target" => ["songs", _, _]}, socket) do
@ -56,7 +56,7 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
defp consume_entry(socket, ref, store_func) when is_function(store_func) do defp consume_entry(socket, ref, store_func) when is_function(store_func) do
{entries, []} = uploaded_entries(socket, :mp3) {entries, []} = uploaded_entries(socket, :mp3)
entry = Enum.find(entries, fn entry -> entry.ref == ref end) entry = Enum.find(entries, fn entry -> entry.ref == ref end)
consume_uploaded_entry(socket, entry, fn meta -> store_func.(meta.path) end) consume_uploaded_entry(socket, entry, fn meta -> {:ok, store_func.(meta.path)} end)
end end
def handle_event("save", %{"songs" => song_params}, socket) do def handle_event("save", %{"songs" => song_params}, socket) do
@ -123,6 +123,7 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
{:noreply, put_new_changeset(socket, entry)} {:noreply, put_new_changeset(socket, entry)}
end end
defp file_error(%{kind: :dropped} = assigns), do: ~H|dropped (exceeds limit of 10 files)|
defp file_error(%{kind: :too_large} = assigns), do: ~H|larger than 10MB| defp file_error(%{kind: :too_large} = assigns), do: ~H|larger than 10MB|
defp file_error(%{kind: :not_accepted} = assigns), do: ~H|not a valid MP3 file| defp file_error(%{kind: :not_accepted} = assigns), do: ~H|not a valid MP3 file|
defp file_error(%{kind: :too_many_files} = assigns), do: ~H|too many files| defp file_error(%{kind: :too_many_files} = assigns), do: ~H|too many files|
@ -134,4 +135,25 @@ defmodule LiveBeatsWeb.SongLive.UploadFormComponent do
socket socket
end end
end end
defp drop_invalid_uploads(socket) do
%{uploads: uploads} = socket.assigns
{new_socket, error_messages, _index} =
Enum.reduce(uploads.mp3.entries, {socket, [], 0}, fn entry, {socket, msgs, i} ->
if i >= @max_songs do
{cancel_upload(socket, :mp3, entry.ref), [{entry.client_name, :dropped} | msgs], i + 1}
else
case upload_errors(uploads.mp3, entry) do
[first | _] ->
{cancel_upload(socket, :mp3, entry.ref), [{entry.client_name, first} | msgs], i + 1}
[] ->
{socket, msgs, i + 1}
end
end
end)
assign(new_socket, error_messages: error_messages)
end
end end

View file

@ -19,7 +19,7 @@
<!-- upload --> <!-- upload -->
<div class="sm:grid sm:border-t sm:border-gray-200 sm:pt-5"> <div class="sm:grid sm:border-t sm:border-gray-200 sm:pt-5">
<div class="mt-1 sm:mt-0" phx-drop-target={@uploads.mp3.ref}> <div class="mt-1 sm:mt-0" phx-drop-target={@uploads.mp3.ref}>
<%= if Enum.any?(@uploads.mp3.errors) do %> <%= if Enum.any?(@error_messages) do %>
<div class="rounded-md bg-red-50 p-4 mb-2"> <div class="rounded-md bg-red-50 p-4 mb-2">
<div class="flex"> <div class="flex">
<div class="flex-shrink-0"> <div class="flex-shrink-0">
@ -31,8 +31,8 @@
</h3> </h3>
<div class="mt-2 text-sm text-red-700"> <div class="mt-2 text-sm text-red-700">
<ul role="list" class="list-disc pl-5 space-y-1"> <ul role="list" class="list-disc pl-5 space-y-1">
<%= for {_ref, error} <- @uploads.mp3.errors do %> <%= for {client_name, error} <- @error_messages do %>
<li><.file_error kind={error} /></li> <li><%= client_name %>: <.file_error kind={error} /></li>
<% end %> <% end %>
</ul> </ul>
</div> </div>