Can add wishlisted records to a record set
This commit is contained in:
@@ -21,4 +21,13 @@ defmodule MusicLibrary.RecordSets.RecordSet do
|
|||||||
|> cast(attrs, [:name, :description])
|
|> cast(attrs, [:name, :description])
|
||||||
|> validate_required([:name])
|
|> validate_required([:name])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def count_by_status(record_set) do
|
||||||
|
record_set.items
|
||||||
|
|> Enum.frequencies_by(fn item ->
|
||||||
|
if item.record.purchased_at, do: :collected, else: :wishlisted
|
||||||
|
end)
|
||||||
|
|> Map.new()
|
||||||
|
|> Map.put(:total, Enum.count(record_set.items))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
{record_set.name}
|
{record_set.name}
|
||||||
</h2>
|
</h2>
|
||||||
<span class="ml-2 text-xs text-zinc-500 dark:text-zinc-400">
|
<span class="ml-2 text-xs text-zinc-500 dark:text-zinc-400">
|
||||||
{ngettext("1 record", "%{count} records", length(record_set.items))}
|
{gettext("%{collected}/%{total} records", RecordSet.count_by_status(record_set))}
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
<article
|
<article
|
||||||
@@ -92,9 +92,26 @@
|
|||||||
<div class="flex flex-wrap items-start gap-3 md:overflow-x-auto pb-2">
|
<div class="flex flex-wrap items-start gap-3 md:overflow-x-auto pb-2">
|
||||||
<div
|
<div
|
||||||
:for={item <- record_set.items}
|
:for={item <- record_set.items}
|
||||||
class="flex-none w-24 sm:w-32 group relative"
|
class={[
|
||||||
|
"flex-none w-24 sm:w-32 group relative",
|
||||||
|
is_nil(item.record.purchased_at) &&
|
||||||
|
"opacity-60 hover:opacity-100 transition-opacity"
|
||||||
|
]}
|
||||||
>
|
>
|
||||||
<.link navigate={~p"/collection/#{item.record}"}>
|
<.link
|
||||||
|
:if={item.record.purchased_at}
|
||||||
|
navigate={~p"/collection/#{item.record}"}
|
||||||
|
>
|
||||||
|
<MusicLibraryWeb.RecordComponents.record_cover
|
||||||
|
record={item.record}
|
||||||
|
class="rounded-lg aspect-square object-cover"
|
||||||
|
width={256}
|
||||||
|
/>
|
||||||
|
</.link>
|
||||||
|
<.link
|
||||||
|
:if={!item.record.purchased_at}
|
||||||
|
navigate={~p"/wishlist/#{item.record}"}
|
||||||
|
>
|
||||||
<MusicLibraryWeb.RecordComponents.record_cover
|
<MusicLibraryWeb.RecordComponents.record_cover
|
||||||
record={item.record}
|
record={item.record}
|
||||||
class="rounded-lg aspect-square object-cover"
|
class="rounded-lg aspect-square object-cover"
|
||||||
@@ -107,14 +124,13 @@
|
|||||||
placement="bottom-end"
|
placement="bottom-end"
|
||||||
>
|
>
|
||||||
<:toggle>
|
<:toggle>
|
||||||
<span class="rounded-full bg-zinc-900/60 p-0.5 cursor-pointer">
|
<span class="sr-only">{gettext("Actions")}</span>
|
||||||
<.icon
|
<.icon
|
||||||
name="hero-ellipsis-vertical"
|
name="hero-ellipsis-vertical"
|
||||||
class="h-4 w-4 text-white"
|
class="size-5 text-zinc-800 dark:text-zinc-200 cursor-pointer"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
data-slot="icon"
|
data-slot="icon"
|
||||||
/>
|
/>
|
||||||
</span>
|
|
||||||
</:toggle>
|
</:toggle>
|
||||||
<.dropdown_button
|
<.dropdown_button
|
||||||
:if={item.position > 0}
|
:if={item.position > 0}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
|
|
||||||
alias MusicLibrary.Collection
|
alias MusicLibrary.Collection
|
||||||
alias MusicLibrary.Records.Record
|
alias MusicLibrary.Records.Record
|
||||||
|
alias MusicLibrary.Wishlist
|
||||||
|
|
||||||
import MusicLibraryWeb.RecordComponents, only: [format_label: 1, type_label: 1]
|
import MusicLibraryWeb.RecordComponents, only: [format_label: 1, type_label: 1]
|
||||||
|
|
||||||
@@ -15,7 +16,7 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
{@title}
|
{@title}
|
||||||
</h1>
|
</h1>
|
||||||
<p class="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
|
<p class="text-sm text-zinc-500 dark:text-zinc-400 mt-1">
|
||||||
{gettext("Search your collection to add a record to this set.")}
|
{gettext("Search your records to add a record to this set.")}
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<form
|
<form
|
||||||
@@ -38,61 +39,83 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
/>
|
/>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<ul
|
<div
|
||||||
:if={@query != ""}
|
:if={@query != ""}
|
||||||
class="mt-4 divide-y divide-zinc-100 dark:divide-zinc-700 max-h-96 overflow-y-auto"
|
class="mt-4 max-h-96 overflow-y-auto"
|
||||||
>
|
>
|
||||||
<li
|
<p
|
||||||
:if={@results == []}
|
:if={@collected_results == [] and @wishlisted_results == []}
|
||||||
class="py-4 text-center text-sm text-zinc-500 dark:text-zinc-400"
|
class="py-4 text-center text-sm text-zinc-500 dark:text-zinc-400"
|
||||||
>
|
>
|
||||||
{gettext("No records found")}
|
{gettext("No records found")}
|
||||||
</li>
|
</p>
|
||||||
<li
|
|
||||||
:for={record <- @results}
|
<div :if={@collected_results != []}>
|
||||||
role="option"
|
<h3 class="text-xs font-medium uppercase tracking-wide text-zinc-500 dark:text-zinc-400 mb-1">
|
||||||
class={[
|
{gettext("Collected")}
|
||||||
"flex items-center gap-3 py-3 px-2 hover:bg-zinc-50 dark:hover:bg-zinc-800 cursor-pointer rounded-lg",
|
</h3>
|
||||||
"aria-selected:bg-zinc-100 dark:aria-selected:bg-zinc-700"
|
<ul class="divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
]}
|
<.record_result :for={record <- @collected_results} record={record} myself={@myself} />
|
||||||
phx-click="add_record"
|
</ul>
|
||||||
phx-target={@myself}
|
</div>
|
||||||
phx-value-record-id={record.id}
|
|
||||||
>
|
<div :if={@wishlisted_results != []} class={[@collected_results != [] && "mt-4"]}>
|
||||||
<div class="w-12 flex-none">
|
<h3 class="text-xs font-medium uppercase tracking-wide text-zinc-500 dark:text-zinc-400 mb-1">
|
||||||
<MusicLibraryWeb.RecordComponents.record_cover
|
{gettext("Wishlisted")}
|
||||||
record={record}
|
</h3>
|
||||||
class="rounded aspect-square object-cover"
|
<ul class="divide-y divide-zinc-100 dark:divide-zinc-700">
|
||||||
width={96}
|
<.record_result :for={record <- @wishlisted_results} record={record} myself={@myself} />
|
||||||
/>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0 flex-auto">
|
</div>
|
||||||
<p class="text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate">
|
|
||||||
{record.title}
|
|
||||||
</p>
|
|
||||||
<p class="text-xs text-zinc-500 dark:text-zinc-400 truncate">
|
|
||||||
{Record.artist_names(record)}
|
|
||||||
</p>
|
|
||||||
<p class="mt-1 text-xs leading-5 text-zinc-500 dark:text-zinc-400">
|
|
||||||
{Record.format_release_date(record.release_date)} · {type_label(record.type)} · {format_label(
|
|
||||||
record.format
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="flex-none">
|
|
||||||
<.icon
|
|
||||||
name="hero-plus-circle"
|
|
||||||
class="h-5 w-5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
|
||||||
aria-hidden="true"
|
|
||||||
data-slot="icon"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp record_result(assigns) do
|
||||||
|
~H"""
|
||||||
|
<li
|
||||||
|
role="option"
|
||||||
|
class={[
|
||||||
|
"flex items-center gap-3 py-3 px-2 hover:bg-zinc-50 dark:hover:bg-zinc-800 cursor-pointer rounded-lg",
|
||||||
|
"aria-selected:bg-zinc-100 dark:aria-selected:bg-zinc-700"
|
||||||
|
]}
|
||||||
|
phx-click="add_record"
|
||||||
|
phx-target={@myself}
|
||||||
|
phx-value-record-id={@record.id}
|
||||||
|
>
|
||||||
|
<div class="w-12 flex-none">
|
||||||
|
<MusicLibraryWeb.RecordComponents.record_cover
|
||||||
|
record={@record}
|
||||||
|
class="rounded aspect-square object-cover"
|
||||||
|
width={96}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="min-w-0 flex-auto">
|
||||||
|
<p class="text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate">
|
||||||
|
{@record.title}
|
||||||
|
</p>
|
||||||
|
<p class="text-xs text-zinc-500 dark:text-zinc-400 truncate">
|
||||||
|
{Record.artist_names(@record)}
|
||||||
|
</p>
|
||||||
|
<p class="mt-1 text-xs leading-5 text-zinc-500 dark:text-zinc-400">
|
||||||
|
{Record.format_release_date(@record.release_date)} · {type_label(@record.type)} · {format_label(
|
||||||
|
@record.format
|
||||||
|
)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="flex-none">
|
||||||
|
<.icon
|
||||||
|
name="hero-plus-circle"
|
||||||
|
class="h-5 w-5 text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300"
|
||||||
|
aria-hidden="true"
|
||||||
|
data-slot="icon"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def update(assigns, socket) do
|
def update(assigns, socket) do
|
||||||
existing_record_ids =
|
existing_record_ids =
|
||||||
@@ -102,27 +125,39 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
socket
|
socket
|
||||||
|> assign(assigns)
|
|> assign(assigns)
|
||||||
|> assign(:query, "")
|
|> assign(:query, "")
|
||||||
|> assign(:results, [])
|
|> assign(:collected_results, [])
|
||||||
|
|> assign(:wishlisted_results, [])
|
||||||
|> assign(:existing_record_ids, existing_record_ids)}
|
|> assign(:existing_record_ids, existing_record_ids)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("search", %{"query" => query}, socket) do
|
def handle_event("search", %{"query" => query}, socket) do
|
||||||
results =
|
{collected, wishlisted} =
|
||||||
if String.trim(query) == "" do
|
if String.trim(query) == "" do
|
||||||
[]
|
{[], []}
|
||||||
else
|
else
|
||||||
query
|
reject = fn record ->
|
||||||
|> Collection.search_records(limit: 20)
|
|
||||||
|> Enum.reject(fn record ->
|
|
||||||
MapSet.member?(socket.assigns.existing_record_ids, record.id)
|
MapSet.member?(socket.assigns.existing_record_ids, record.id)
|
||||||
end)
|
end
|
||||||
|
|
||||||
|
collected =
|
||||||
|
query
|
||||||
|
|> Collection.search_records(limit: 20)
|
||||||
|
|> Enum.reject(reject)
|
||||||
|
|
||||||
|
wishlisted =
|
||||||
|
query
|
||||||
|
|> Wishlist.search_records(limit: 20)
|
||||||
|
|> Enum.reject(reject)
|
||||||
|
|
||||||
|
{collected, wishlisted}
|
||||||
end
|
end
|
||||||
|
|
||||||
{:noreply,
|
{:noreply,
|
||||||
socket
|
socket
|
||||||
|> assign(:query, query)
|
|> assign(:query, query)
|
||||||
|> assign(:results, results)}
|
|> assign(:collected_results, collected)
|
||||||
|
|> assign(:wishlisted_results, wishlisted)}
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_event("add_record", %{"record-id" => record_id}, socket) do
|
def handle_event("add_record", %{"record-id" => record_id}, socket) do
|
||||||
@@ -132,8 +167,10 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
{:ok, updated_set} ->
|
{:ok, updated_set} ->
|
||||||
existing_record_ids = MapSet.put(socket.assigns.existing_record_ids, record_id)
|
existing_record_ids = MapSet.put(socket.assigns.existing_record_ids, record_id)
|
||||||
|
|
||||||
results =
|
reject = fn record -> record.id == record_id end
|
||||||
Enum.reject(socket.assigns.results, fn record -> record.id == record_id end)
|
|
||||||
|
collected_results = Enum.reject(socket.assigns.collected_results, reject)
|
||||||
|
wishlisted_results = Enum.reject(socket.assigns.wishlisted_results, reject)
|
||||||
|
|
||||||
notify_parent({:added, updated_set})
|
notify_parent({:added, updated_set})
|
||||||
put_toast!(:info, gettext("Record added to set"))
|
put_toast!(:info, gettext("Record added to set"))
|
||||||
@@ -142,7 +179,8 @@ defmodule MusicLibraryWeb.RecordSetLive.RecordPicker do
|
|||||||
socket
|
socket
|
||||||
|> assign(:record_set, updated_set)
|
|> assign(:record_set, updated_set)
|
||||||
|> assign(:existing_record_ids, existing_record_ids)
|
|> assign(:existing_record_ids, existing_record_ids)
|
||||||
|> assign(:results, results)}
|
|> assign(:collected_results, collected_results)
|
||||||
|
|> assign(:wishlisted_results, wishlisted_results)}
|
||||||
|
|
||||||
{:error, _changeset} ->
|
{:error, _changeset} ->
|
||||||
put_toast!(:error, gettext("Could not add record to set"))
|
put_toast!(:error, gettext("Could not add record to set"))
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ msgid "Choose which format to import"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
||||||
#: lib/music_library_web/live/stats_live/index.html.heex
|
#: lib/music_library_web/live/stats_live/index.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -277,6 +278,7 @@ msgid "Collected"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
||||||
#: lib/music_library_web/live/stats_live/index.html.heex
|
#: lib/music_library_web/live/stats_live/index.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -395,7 +397,6 @@ msgid "On Tour"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_components.ex
|
#: lib/music_library_web/components/record_components.ex
|
||||||
#: lib/music_library_web/live/record_set_live/index.html.heex
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "1 record"
|
msgid "1 record"
|
||||||
msgid_plural "%{count} records"
|
msgid_plural "%{count} records"
|
||||||
@@ -1778,11 +1779,6 @@ msgstr ""
|
|||||||
msgid "Save Set"
|
msgid "Save Set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Search your collection to add a record to this set."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/layouts/app.html.heex
|
#: lib/music_library_web/components/layouts/app.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sets"
|
msgid "Sets"
|
||||||
@@ -1797,3 +1793,13 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "e.g. Favorites, Road Trip, Sunday Morning"
|
msgid "e.g. Favorites, Road Trip, Sunday Morning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "Search your records to add a record to this set."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/record_set_live/index.html.heex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "%{collected}/%{total} records"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ msgid "Choose which format to import"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
||||||
#: lib/music_library_web/live/stats_live/index.html.heex
|
#: lib/music_library_web/live/stats_live/index.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -277,6 +278,7 @@ msgid "Collected"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/barcode_scanner.ex
|
#: lib/music_library_web/components/barcode_scanner.ex
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
#: lib/music_library_web/live/scrobbled_tracks_live/index.html.heex
|
||||||
#: lib/music_library_web/live/stats_live/index.html.heex
|
#: lib/music_library_web/live/stats_live/index.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -395,7 +397,6 @@ msgid "On Tour"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/components/record_components.ex
|
#: lib/music_library_web/components/record_components.ex
|
||||||
#: lib/music_library_web/live/record_set_live/index.html.heex
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "1 record"
|
msgid "1 record"
|
||||||
msgid_plural "%{count} records"
|
msgid_plural "%{count} records"
|
||||||
@@ -1778,11 +1779,6 @@ msgstr ""
|
|||||||
msgid "Save Set"
|
msgid "Save Set"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
|
||||||
#, elixir-autogen, elixir-format
|
|
||||||
msgid "Search your collection to add a record to this set."
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: lib/music_library_web/components/layouts/app.html.heex
|
#: lib/music_library_web/components/layouts/app.html.heex
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Sets"
|
msgid "Sets"
|
||||||
@@ -1797,3 +1793,13 @@ msgstr ""
|
|||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "e.g. Favorites, Road Trip, Sunday Morning"
|
msgid "e.g. Favorites, Road Trip, Sunday Morning"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/record_set_live/record_picker.ex
|
||||||
|
#, elixir-autogen, elixir-format, fuzzy
|
||||||
|
msgid "Search your records to add a record to this set."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: lib/music_library_web/live/record_set_live/index.html.heex
|
||||||
|
#, elixir-autogen, elixir-format
|
||||||
|
msgid "%{collected}/%{total} records"
|
||||||
|
msgstr ""
|
||||||
|
|||||||
Reference in New Issue
Block a user