Extract record list component
This commit is contained in:
@@ -71,6 +71,8 @@ defmodule MusicLibraryWeb do
|
|||||||
quote do
|
quote do
|
||||||
use Phoenix.Component
|
use Phoenix.Component
|
||||||
|
|
||||||
|
use Gettext, backend: MusicLibraryWeb.Gettext
|
||||||
|
|
||||||
# Import convenience functions from controllers
|
# Import convenience functions from controllers
|
||||||
import Phoenix.Controller,
|
import Phoenix.Controller,
|
||||||
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
|
only: [get_csrf_token: 0, view_module: 1, view_template: 1]
|
||||||
|
|||||||
@@ -0,0 +1,204 @@
|
|||||||
|
defmodule MusicLibraryWeb.RecordComponents do
|
||||||
|
use MusicLibraryWeb, :html
|
||||||
|
|
||||||
|
alias Phoenix.LiveView.JS
|
||||||
|
alias MusicLibrary.Records
|
||||||
|
|
||||||
|
attr :record_show_path, :any, required: true
|
||||||
|
attr :record_edit_path, :any, required: true
|
||||||
|
attr :records, :list, required: true
|
||||||
|
|
||||||
|
def record_list(assigns) do
|
||||||
|
~H"""
|
||||||
|
<ul
|
||||||
|
class="divide-y divide-zinc-100 dark:divide-slate-300/30 mt-5"
|
||||||
|
role="list"
|
||||||
|
id="records"
|
||||||
|
phx-update="stream"
|
||||||
|
>
|
||||||
|
<li
|
||||||
|
:for={{id, record} <- @records}
|
||||||
|
phx-click={JS.navigate(@record_show_path.(record))}
|
||||||
|
class="flex justify-between gap-x-6 py-5 hover:bg-zinc-50 dark:hover:bg-zinc-800 px-2 -mx-2 md:px-4 md:-mx-4 cursor-pointer"
|
||||||
|
id={id}
|
||||||
|
>
|
||||||
|
<div class="flex min-w-0 gap-x-4 items-center">
|
||||||
|
<img
|
||||||
|
class="w-20 flex-none rounded-lg"
|
||||||
|
alt={record.title}
|
||||||
|
src={~p"/covers/#{record.id}?vsn=#{record.cover_hash || ""}"}
|
||||||
|
/>
|
||||||
|
<div class="min-w-0 flex-auto">
|
||||||
|
<h1 class="text-sm leading-6 text-zinc-700">
|
||||||
|
<.link
|
||||||
|
:for={artist <- record.artists}
|
||||||
|
class="text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
|
||||||
|
navigate={~p"/artists/#{artist.musicbrainz_id}"}
|
||||||
|
>
|
||||||
|
{artist.name}
|
||||||
|
</.link>
|
||||||
|
</h1>
|
||||||
|
<h2 class="mt-1 flex font-semibold text-sm sm:text-base leading-5 text-zinc-700 dark:text-zinc-300 text-wrap">
|
||||||
|
{record.title}
|
||||||
|
</h2>
|
||||||
|
<p class="mt-1 text-xs leading-5 text-zinc-500 dark:text-zinc-400">
|
||||||
|
{Records.Record.format_release(record.release)}
|
||||||
|
<span class="sm:hidden">
|
||||||
|
· {Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
||||||
|
record.type
|
||||||
|
)}
|
||||||
|
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
||||||
|
·
|
||||||
|
<span class="sr-only">
|
||||||
|
{gettext("Number of included records")}
|
||||||
|
</span>
|
||||||
|
<span class={[
|
||||||
|
"inline-flex items-center rounded-full",
|
||||||
|
"px-2 py-1 text-xs font-medium",
|
||||||
|
"ring-1 ring-inset",
|
||||||
|
"bg-gray-50 dark:bg-gray-400/10",
|
||||||
|
"text-gray-600 dark:text-gray-500",
|
||||||
|
"ring-gray-500/10 dark:ring-gray-400/20"
|
||||||
|
]}>
|
||||||
|
{Records.Record.child_release_groups_count(record)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex shrink-0 items-center gap-x-6">
|
||||||
|
<div class="hidden sm:flex sm:flex-col sm:items-end">
|
||||||
|
<p class="text-xs leading-6 text-zinc-900 dark:text-zinc-300">
|
||||||
|
{Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
||||||
|
record.type
|
||||||
|
)}
|
||||||
|
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
||||||
|
·
|
||||||
|
<span class="sr-only">
|
||||||
|
{gettext("Number of included records")}
|
||||||
|
</span>
|
||||||
|
<span class={[
|
||||||
|
"inline-flex items-center rounded-full",
|
||||||
|
"px-2 py-1 text-xs font-medium",
|
||||||
|
"ring-1 ring-inset",
|
||||||
|
"bg-gray-50 dark:bg-gray-400/10",
|
||||||
|
"text-gray-600 dark:text-gray-500",
|
||||||
|
"ring-gray-500/10 dark:ring-gray-400/20"
|
||||||
|
]}>
|
||||||
|
{Records.Record.child_release_groups_count(record)}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<%!-- TODO: replace with OSS version --%>
|
||||||
|
<div class="relative flex-none">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-300"
|
||||||
|
aria-expanded="false"
|
||||||
|
aria-haspopup="true"
|
||||||
|
phx-click={toggle_actions_menu(record.id)}
|
||||||
|
phx-click-away={close_actions_menu(record.id)}
|
||||||
|
>
|
||||||
|
<span class="sr-only">{gettext("Open options")}</span>
|
||||||
|
<.icon
|
||||||
|
name="hero-ellipsis-vertical"
|
||||||
|
class="-mt-1 h-5 w-5"
|
||||||
|
aria-hidden="true"
|
||||||
|
data-slot="icon"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<!--
|
||||||
|
Dropdown menu, show/hide based on menu state.
|
||||||
|
|
||||||
|
Entering: "transition ease-out duration-100"
|
||||||
|
From: "transform opacity-0 scale-95"
|
||||||
|
To: "transform opacity-100 scale-100"
|
||||||
|
Leaving: "transition ease-in duration-75"
|
||||||
|
From: "transform opacity-100 scale-100"
|
||||||
|
To: "transform opacity-0 scale-95"
|
||||||
|
-->
|
||||||
|
<.focus_wrap
|
||||||
|
id={"actions-#{record.id}"}
|
||||||
|
class={[
|
||||||
|
"hidden pointer-events-auto absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-zinc-800 py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
|
||||||
|
]}
|
||||||
|
role="menu"
|
||||||
|
aria-orientation="vertical"
|
||||||
|
aria-labelledby="options-menu-0-button"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<.link
|
||||||
|
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
id={"actions-#{record.id}-show"}
|
||||||
|
navigate={@record_show_path.(record)}
|
||||||
|
>
|
||||||
|
{gettext("Show")}
|
||||||
|
</.link>
|
||||||
|
<a
|
||||||
|
href={musicbrainz_url(record)}
|
||||||
|
target=".blank"
|
||||||
|
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
id={"actions-#{record.id}-musicbrainz"}
|
||||||
|
>
|
||||||
|
{gettext("View on MusicBrainz")}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<.link
|
||||||
|
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
id={"actions-#{record.id}-edit"}
|
||||||
|
patch={@record_edit_path.(record)}
|
||||||
|
>
|
||||||
|
{gettext("Edit")}
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<.link
|
||||||
|
:if={!record.purchased_at}
|
||||||
|
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
id={"actions-#{record.id}-purchase"}
|
||||||
|
phx-click={JS.push("purchase", value: %{id: record.id})}
|
||||||
|
>
|
||||||
|
{gettext("Purchase")}
|
||||||
|
</.link>
|
||||||
|
|
||||||
|
<.link
|
||||||
|
class="block px-3 py-1 text-sm leading-6 text-red-900 hover:bg-red-50 dark:text-red-700 dark:hover:bg-red-900/30 dark:hover:text-red-600"
|
||||||
|
role="menuitem"
|
||||||
|
tabindex="-1"
|
||||||
|
id={"actions-#{record.id}-delete"}
|
||||||
|
phx-click={JS.push("delete", value: %{id: record.id}) |> hide("##{id}")}
|
||||||
|
data-confirm={gettext("Are you sure?")}
|
||||||
|
>
|
||||||
|
{gettext("Delete")}
|
||||||
|
</.link>
|
||||||
|
</.focus_wrap>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
"""
|
||||||
|
end
|
||||||
|
|
||||||
|
defp musicbrainz_url(record) do
|
||||||
|
"https://musicbrainz.org/release-group/#{record.musicbrainz_id}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp toggle_actions_menu(record_id) do
|
||||||
|
JS.toggle(to: "#actions-#{record_id}")
|
||||||
|
|> JS.toggle_class("pointer-events-none", to: "#records > li")
|
||||||
|
end
|
||||||
|
|
||||||
|
defp close_actions_menu(record_id) do
|
||||||
|
JS.hide(to: "#actions-#{record_id}")
|
||||||
|
|> JS.remove_class("pointer-events-none", to: "#records > li")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
defmodule MusicLibraryWeb.CollectionLive.Index do
|
defmodule MusicLibraryWeb.CollectionLive.Index do
|
||||||
use MusicLibraryWeb, :live_view
|
use MusicLibraryWeb, :live_view
|
||||||
import MusicLibraryWeb.Pagination
|
import MusicLibraryWeb.Pagination
|
||||||
|
import MusicLibraryWeb.RecordComponents
|
||||||
|
|
||||||
alias MusicLibrary.Records
|
alias MusicLibrary.Records
|
||||||
alias MusicLibrary.Collection
|
alias MusicLibrary.Collection
|
||||||
@@ -160,18 +161,4 @@ defmodule MusicLibraryWeb.CollectionLive.Index do
|
|||||||
|
|
||||||
~p"/collection?#{qs}"
|
~p"/collection?#{qs}"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp musicbrainz_url(record) do
|
|
||||||
"https://musicbrainz.org/release-group/#{record.musicbrainz_id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
defp toggle_actions_menu(record_id) do
|
|
||||||
JS.toggle(to: "#actions-#{record_id}")
|
|
||||||
|> JS.toggle_class("pointer-events-none", to: "#records > li")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp close_actions_menu(record_id) do
|
|
||||||
JS.hide(to: "#actions-#{record_id}")
|
|
||||||
|> JS.remove_class("pointer-events-none", to: "#records > li")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,170 +28,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul
|
<.record_list
|
||||||
class="divide-y divide-zinc-100 dark:divide-slate-300/30 mt-5"
|
records={@streams.records}
|
||||||
role="list"
|
record_show_path={fn record -> ~p"/collection/#{record}" end}
|
||||||
id="records"
|
record_edit_path={fn record -> ~p"/collection/#{record}/edit" end}
|
||||||
phx-update="stream"
|
/>
|
||||||
>
|
|
||||||
<li
|
|
||||||
:for={{id, record} <- @streams.records}
|
|
||||||
phx-click={JS.navigate(~p"/collection/#{record}")}
|
|
||||||
class="flex justify-between gap-x-6 py-5 hover:bg-zinc-50 dark:hover:bg-zinc-800 px-2 -mx-2 md:px-4 md:-mx-4 cursor-pointer"
|
|
||||||
id={id}
|
|
||||||
>
|
|
||||||
<div class="flex min-w-0 gap-x-4 items-center">
|
|
||||||
<img
|
|
||||||
class="w-20 flex-none rounded-lg"
|
|
||||||
alt={record.title}
|
|
||||||
src={~p"/covers/#{record.id}?vsn=#{record.cover_hash || ""}"}
|
|
||||||
/>
|
|
||||||
<div class="min-w-0 flex-auto">
|
|
||||||
<h1 class="text-sm leading-6 text-zinc-700">
|
|
||||||
<.link
|
|
||||||
:for={artist <- record.artists}
|
|
||||||
class="text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
|
|
||||||
navigate={~p"/artists/#{artist.musicbrainz_id}"}
|
|
||||||
>
|
|
||||||
{artist.name}
|
|
||||||
</.link>
|
|
||||||
</h1>
|
|
||||||
<h2 class="mt-1 flex font-semibold text-sm sm:text-base leading-5 text-zinc-700 dark:text-zinc-300 text-wrap">
|
|
||||||
{record.title}
|
|
||||||
</h2>
|
|
||||||
<p class="mt-1 text-xs leading-5 text-zinc-500 dark:text-zinc-400">
|
|
||||||
{Records.Record.format_release(record.release)}
|
|
||||||
<span class="sm:hidden">
|
|
||||||
· {Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
|
||||||
record.type
|
|
||||||
)}
|
|
||||||
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
|
||||||
·
|
|
||||||
<span class="sr-only">
|
|
||||||
{gettext("Number of included records")}
|
|
||||||
</span>
|
|
||||||
<span class={[
|
|
||||||
"inline-flex items-center rounded-full",
|
|
||||||
"px-2 py-1 text-xs font-medium",
|
|
||||||
"ring-1 ring-inset",
|
|
||||||
"bg-gray-50 dark:bg-gray-400/10",
|
|
||||||
"text-gray-600 dark:text-gray-500",
|
|
||||||
"ring-gray-500/10 dark:ring-gray-400/20"
|
|
||||||
]}>
|
|
||||||
{Records.Record.child_release_groups_count(record)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex shrink-0 items-center gap-x-6">
|
|
||||||
<div class="hidden sm:flex sm:flex-col sm:items-end">
|
|
||||||
<p class="text-xs leading-6 text-zinc-900 dark:text-zinc-300">
|
|
||||||
{Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
|
||||||
record.type
|
|
||||||
)}
|
|
||||||
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
|
||||||
·
|
|
||||||
<span class="sr-only">
|
|
||||||
{gettext("Number of included records")}
|
|
||||||
</span>
|
|
||||||
<span class={[
|
|
||||||
"inline-flex items-center rounded-full",
|
|
||||||
"px-2 py-1 text-xs font-medium",
|
|
||||||
"ring-1 ring-inset",
|
|
||||||
"bg-gray-50 dark:bg-gray-400/10",
|
|
||||||
"text-gray-600 dark:text-gray-500",
|
|
||||||
"ring-gray-500/10 dark:ring-gray-400/20"
|
|
||||||
]}>
|
|
||||||
{Records.Record.child_release_groups_count(record)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<%!-- TODO: replace with OSS version --%>
|
|
||||||
<div class="relative flex-none">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-300"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-haspopup="true"
|
|
||||||
phx-click={toggle_actions_menu(record.id)}
|
|
||||||
phx-click-away={close_actions_menu(record.id)}
|
|
||||||
>
|
|
||||||
<span class="sr-only">{gettext("Open options")}</span>
|
|
||||||
<.icon
|
|
||||||
name="hero-ellipsis-vertical"
|
|
||||||
class="-mt-1 h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
data-slot="icon"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<!--
|
|
||||||
Dropdown menu, show/hide based on menu state.
|
|
||||||
|
|
||||||
Entering: "transition ease-out duration-100"
|
|
||||||
From: "transform opacity-0 scale-95"
|
|
||||||
To: "transform opacity-100 scale-100"
|
|
||||||
Leaving: "transition ease-in duration-75"
|
|
||||||
From: "transform opacity-100 scale-100"
|
|
||||||
To: "transform opacity-0 scale-95"
|
|
||||||
-->
|
|
||||||
<.focus_wrap
|
|
||||||
id={"actions-#{record.id}"}
|
|
||||||
class={[
|
|
||||||
"hidden pointer-events-auto absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-zinc-800 py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
|
|
||||||
]}
|
|
||||||
role="menu"
|
|
||||||
aria-orientation="vertical"
|
|
||||||
aria-labelledby="options-menu-0-button"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-show"}
|
|
||||||
navigate={~p"/collection/#{record}"}
|
|
||||||
>
|
|
||||||
{gettext("Show")}
|
|
||||||
</.link>
|
|
||||||
<a
|
|
||||||
href={musicbrainz_url(record)}
|
|
||||||
target=".blank"
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-musicbrainz"}
|
|
||||||
>
|
|
||||||
{gettext("View on MusicBrainz")}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-edit"}
|
|
||||||
patch={~p"/collection/#{record}/edit"}
|
|
||||||
>
|
|
||||||
{gettext("Edit")}
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-red-900 hover:bg-red-50 dark:text-red-700 dark:hover:bg-red-900/30 dark:hover:text-red-600"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-delete"}
|
|
||||||
phx-click={JS.push("delete", value: %{id: record.id}) |> hide("##{id}")}
|
|
||||||
data-confirm={gettext("Are you sure?")}
|
|
||||||
>
|
|
||||||
{gettext("Delete")}
|
|
||||||
</.link>
|
|
||||||
</.focus_wrap>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<.modal
|
<.modal
|
||||||
:if={@live_action == :edit}
|
:if={@live_action == :edit}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
defmodule MusicLibraryWeb.WishlistLive.Index do
|
defmodule MusicLibraryWeb.WishlistLive.Index do
|
||||||
use MusicLibraryWeb, :live_view
|
use MusicLibraryWeb, :live_view
|
||||||
import MusicLibraryWeb.Pagination
|
import MusicLibraryWeb.Pagination
|
||||||
|
import MusicLibraryWeb.RecordComponents
|
||||||
|
|
||||||
alias MusicLibrary.Wishlist
|
alias MusicLibrary.Wishlist
|
||||||
alias MusicLibrary.Records
|
alias MusicLibrary.Records
|
||||||
@@ -173,18 +174,4 @@ defmodule MusicLibraryWeb.WishlistLive.Index do
|
|||||||
|
|
||||||
~p"/wishlist?#{qs}"
|
~p"/wishlist?#{qs}"
|
||||||
end
|
end
|
||||||
|
|
||||||
defp musicbrainz_url(record) do
|
|
||||||
"https://musicbrainz.org/release-group/#{record.musicbrainz_id}"
|
|
||||||
end
|
|
||||||
|
|
||||||
defp toggle_actions_menu(record_id) do
|
|
||||||
JS.toggle(to: "#actions-#{record_id}")
|
|
||||||
|> JS.toggle_class("pointer-events-none", to: "#records > li")
|
|
||||||
end
|
|
||||||
|
|
||||||
defp close_actions_menu(record_id) do
|
|
||||||
JS.hide(to: "#actions-#{record_id}")
|
|
||||||
|> JS.remove_class("pointer-events-none", to: "#records > li")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -28,180 +28,11 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul
|
<.record_list
|
||||||
class="divide-y divide-zinc-100 dark:divide-slate-300/30 mt-5"
|
records={@streams.records}
|
||||||
role="list"
|
record_show_path={fn record -> ~p"/wishlist/#{record}" end}
|
||||||
id="records"
|
record_edit_path={fn record -> ~p"/wishlist/#{record}/edit" end}
|
||||||
phx-update="stream"
|
/>
|
||||||
>
|
|
||||||
<li
|
|
||||||
:for={{id, record} <- @streams.records}
|
|
||||||
phx-click={JS.navigate(~p"/wishlist/#{record}")}
|
|
||||||
class="flex justify-between gap-x-6 py-5 hover:bg-zinc-50 dark:hover:bg-zinc-800 px-2 -mx-2 md:px-4 md:-mx-4 cursor-pointer"
|
|
||||||
id={id}
|
|
||||||
>
|
|
||||||
<div class="flex min-w-0 gap-x-4 items-center">
|
|
||||||
<img
|
|
||||||
class="w-20 flex-none rounded-lg"
|
|
||||||
alt={record.title}
|
|
||||||
src={~p"/covers/#{record.id}?vsn=#{record.cover_hash || ""}"}
|
|
||||||
/>
|
|
||||||
<div class="min-w-0 flex-auto">
|
|
||||||
<h1 class="text-sm leading-6">
|
|
||||||
<.link
|
|
||||||
:for={artist <- record.artists}
|
|
||||||
class="text-zinc-700 hover:text-zinc-500 dark:text-zinc-400 dark:hover:text-zinc-300"
|
|
||||||
navigate={~p"/artists/#{artist.musicbrainz_id}"}
|
|
||||||
>
|
|
||||||
{artist.name}
|
|
||||||
</.link>
|
|
||||||
</h1>
|
|
||||||
<h2 class="mt-1 flex font-semibold text-sm sm:text-base leading-5 text-zinc-700 dark:text-zinc-300 text-wrap">
|
|
||||||
{record.title}
|
|
||||||
</h2>
|
|
||||||
<p class="mt-1 text-xs leading-5 text-zinc-500 dark:text-zinc-400">
|
|
||||||
{Records.Record.format_release(record.release)}
|
|
||||||
<span class="sm:hidden">
|
|
||||||
· {Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
|
||||||
record.type
|
|
||||||
)}
|
|
||||||
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
|
||||||
·
|
|
||||||
<span class="sr-only">
|
|
||||||
{gettext("Number of included records")}
|
|
||||||
</span>
|
|
||||||
<span class={[
|
|
||||||
"inline-flex items-center rounded-full",
|
|
||||||
"px-2 py-1 text-xs font-medium",
|
|
||||||
"ring-1 ring-inset",
|
|
||||||
"bg-gray-50 dark:bg-gray-400/10",
|
|
||||||
"text-gray-600 dark:text-gray-500",
|
|
||||||
"ring-gray-500/10 dark:ring-gray-400/20"
|
|
||||||
]}>
|
|
||||||
{Records.Record.child_release_groups_count(record)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="flex shrink-0 items-center gap-x-6">
|
|
||||||
<div class="hidden sm:flex sm:flex-col sm:items-end">
|
|
||||||
<p class="text-xs leading-6 text-zinc-900 dark:text-zinc-300">
|
|
||||||
{Records.Record.format_long_label(record.format)} · {Records.Record.type_long_label(
|
|
||||||
record.type
|
|
||||||
)}
|
|
||||||
<span :if={Records.Record.child_release_groups_count(record) > 0}>
|
|
||||||
·
|
|
||||||
<span class="sr-only">
|
|
||||||
{gettext("Number of included records")}
|
|
||||||
</span>
|
|
||||||
<span class={[
|
|
||||||
"inline-flex items-center rounded-full",
|
|
||||||
"px-2 py-1 text-xs font-medium",
|
|
||||||
"ring-1 ring-inset",
|
|
||||||
"bg-gray-50 dark:bg-gray-400/10",
|
|
||||||
"text-gray-600 dark:text-gray-500",
|
|
||||||
"ring-gray-500/10 dark:ring-gray-400/20"
|
|
||||||
]}>
|
|
||||||
{Records.Record.child_release_groups_count(record)}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<%!-- TODO: replace with OSS version --%>
|
|
||||||
<div class="relative flex-none">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="text-zinc-500 hover:text-zinc-900 dark:text-zinc-400 dark:hover:text-zinc-300"
|
|
||||||
aria-expanded="false"
|
|
||||||
aria-haspopup="true"
|
|
||||||
phx-click={toggle_actions_menu(record.id)}
|
|
||||||
phx-click-away={close_actions_menu(record.id)}
|
|
||||||
>
|
|
||||||
<span class="sr-only">{gettext("Open options")}</span>
|
|
||||||
<.icon
|
|
||||||
name="hero-ellipsis-vertical"
|
|
||||||
class="-mt-1 h-5 w-5"
|
|
||||||
aria-hidden="true"
|
|
||||||
data-slot="icon"
|
|
||||||
/>
|
|
||||||
</button>
|
|
||||||
<!--
|
|
||||||
Dropdown menu, show/hide based on menu state.
|
|
||||||
|
|
||||||
Entering: "transition ease-out duration-100"
|
|
||||||
From: "transform opacity-0 scale-95"
|
|
||||||
To: "transform opacity-100 scale-100"
|
|
||||||
Leaving: "transition ease-in duration-75"
|
|
||||||
From: "transform opacity-100 scale-100"
|
|
||||||
To: "transform opacity-0 scale-95"
|
|
||||||
-->
|
|
||||||
<.focus_wrap
|
|
||||||
id={"actions-#{record.id}"}
|
|
||||||
class={[
|
|
||||||
"hidden pointer-events-auto absolute right-0 z-10 mt-2 w-48 origin-top-right rounded-md bg-white dark:bg-zinc-800 py-2 shadow-lg ring-1 ring-zinc-900/5 focus:outline-none"
|
|
||||||
]}
|
|
||||||
role="menu"
|
|
||||||
aria-orientation="vertical"
|
|
||||||
aria-labelledby="options-menu-0-button"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-show"}
|
|
||||||
navigate={~p"/wishlist/#{record}"}
|
|
||||||
>
|
|
||||||
{gettext("Show")}
|
|
||||||
</.link>
|
|
||||||
<a
|
|
||||||
href={musicbrainz_url(record)}
|
|
||||||
target=".blank"
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-musicbrainz"}
|
|
||||||
>
|
|
||||||
{gettext("View on MusicBrainz")}
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-edit"}
|
|
||||||
patch={~p"/wishlist/#{record}/edit"}
|
|
||||||
>
|
|
||||||
{gettext("Edit")}
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-zinc-900 dark:text-zinc-400 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-700"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-purchase"}
|
|
||||||
phx-click={JS.push("purchase", value: %{id: record.id})}
|
|
||||||
>
|
|
||||||
{gettext("Purchase")}
|
|
||||||
</.link>
|
|
||||||
|
|
||||||
<.link
|
|
||||||
class="block px-3 py-1 text-sm leading-6 text-red-900 hover:bg-red-50 dark:text-red-700 dark:hover:bg-red-900/30 dark:hover:text-red-600"
|
|
||||||
role="menuitem"
|
|
||||||
tabindex="-1"
|
|
||||||
id={"actions-#{record.id}-delete"}
|
|
||||||
phx-click={JS.push("delete", value: %{id: record.id}) |> hide("##{id}")}
|
|
||||||
data-confirm={gettext("Are you sure?")}
|
|
||||||
>
|
|
||||||
{gettext("Delete")}
|
|
||||||
</.link>
|
|
||||||
</.focus_wrap>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<.modal
|
<.modal
|
||||||
:if={@live_action == :edit}
|
:if={@live_action == :edit}
|
||||||
|
|||||||
+22
-30
@@ -16,9 +16,8 @@ msgstr ""
|
|||||||
msgid "Actions"
|
msgid "Actions"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:186
|
#: lib/music_library_web/components/record_components.ex:179
|
||||||
#: lib/music_library_web/live/collection_live/show.html.heex:73
|
#: lib/music_library_web/live/collection_live/show.html.heex:73
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:196
|
|
||||||
#: lib/music_library_web/live/wishlist_live/show.html.heex:73
|
#: lib/music_library_web/live/wishlist_live/show.html.heex:73
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Are you sure?"
|
msgid "Are you sure?"
|
||||||
@@ -52,7 +51,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/music_library_web/components/layouts/app.html.heex:14
|
#: lib/music_library_web/components/layouts/app.html.heex:14
|
||||||
#: lib/music_library_web/live/artist_live/show.html.heex:61
|
#: lib/music_library_web/live/artist_live/show.html.heex:61
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:77
|
#: lib/music_library_web/live/collection_live/index.ex:78
|
||||||
#: lib/music_library_web/live/collection_live/show.ex:122
|
#: lib/music_library_web/live/collection_live/show.ex:122
|
||||||
#: lib/music_library_web/live/collection_live/show.ex:139
|
#: lib/music_library_web/live/collection_live/show.ex:139
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
@@ -64,28 +63,26 @@ msgstr ""
|
|||||||
msgid "Cover art"
|
msgid "Cover art"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:188
|
#: lib/music_library_web/components/record_components.ex:181
|
||||||
#: lib/music_library_web/live/collection_live/show.html.heex:79
|
#: lib/music_library_web/live/collection_live/show.html.heex:79
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:198
|
|
||||||
#: lib/music_library_web/live/wishlist_live/show.html.heex:79
|
#: lib/music_library_web/live/wishlist_live/show.html.heex:79
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Delete"
|
msgid "Delete"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:177
|
#: lib/music_library_web/components/record_components.ex:159
|
||||||
#: lib/music_library_web/live/collection_live/show.ex:146
|
#: lib/music_library_web/live/collection_live/show.ex:146
|
||||||
#: lib/music_library_web/live/collection_live/show.html.heex:32
|
#: lib/music_library_web/live/collection_live/show.html.heex:32
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:177
|
|
||||||
#: lib/music_library_web/live/wishlist_live/show.ex:129
|
#: lib/music_library_web/live/wishlist_live/show.ex:129
|
||||||
#: lib/music_library_web/live/wishlist_live/show.html.heex:32
|
#: lib/music_library_web/live/wishlist_live/show.html.heex:32
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Edit"
|
msgid "Edit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:123
|
#: lib/music_library_web/live/collection_live/index.ex:124
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:130
|
#: lib/music_library_web/live/collection_live/index.ex:131
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:120
|
#: lib/music_library_web/live/wishlist_live/index.ex:121
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:127
|
#: lib/music_library_web/live/wishlist_live/index.ex:128
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Error importing record"
|
msgid "Error importing record"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -174,8 +171,7 @@ msgstr ""
|
|||||||
msgid "No results"
|
msgid "No results"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:122
|
#: lib/music_library_web/components/record_components.ex:104
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:122
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Open options"
|
msgid "Open options"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -191,7 +187,7 @@ msgstr ""
|
|||||||
msgid "Previous"
|
msgid "Previous"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:187
|
#: lib/music_library_web/components/record_components.ex:170
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Purchase"
|
msgid "Purchase"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -206,14 +202,14 @@ msgstr ""
|
|||||||
msgid "Purchased on"
|
msgid "Purchased on"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:115
|
#: lib/music_library_web/live/collection_live/index.ex:116
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:112
|
#: lib/music_library_web/live/wishlist_live/index.ex:113
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Record imported successfully"
|
msgid "Record imported successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/record_live/form_component.ex:123
|
#: lib/music_library_web/live/record_live/form_component.ex:123
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:140
|
#: lib/music_library_web/live/wishlist_live/index.ex:141
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Record updated successfully"
|
msgid "Record updated successfully"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -249,9 +245,8 @@ msgstr ""
|
|||||||
msgid "Search for records"
|
msgid "Search for records"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:157
|
#: lib/music_library_web/components/record_components.ex:139
|
||||||
#: lib/music_library_web/live/collection_live/show.ex:145
|
#: lib/music_library_web/live/collection_live/show.ex:145
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:157
|
|
||||||
#: lib/music_library_web/live/wishlist_live/show.ex:128
|
#: lib/music_library_web/live/wishlist_live/show.ex:128
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Show"
|
msgid "Show"
|
||||||
@@ -279,9 +274,9 @@ msgstr ""
|
|||||||
msgid "Success!"
|
msgid "Success!"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:19
|
#: lib/music_library_web/live/collection_live/index.ex:20
|
||||||
#: lib/music_library_web/live/collection_live/show.ex:19
|
#: lib/music_library_web/live/collection_live/show.ex:19
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:19
|
#: lib/music_library_web/live/wishlist_live/index.ex:20
|
||||||
#: lib/music_library_web/live/wishlist_live/show.ex:19
|
#: lib/music_library_web/live/wishlist_live/show.ex:19
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "The application has been updated, please reload."
|
msgid "The application has been updated, please reload."
|
||||||
@@ -313,8 +308,7 @@ msgstr ""
|
|||||||
msgid "Updated at"
|
msgid "Updated at"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:167
|
#: lib/music_library_web/components/record_components.ex:149
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:167
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "View on MusicBrainz"
|
msgid "View on MusicBrainz"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -331,7 +325,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: lib/music_library_web/components/layouts/app.html.heex:20
|
#: lib/music_library_web/components/layouts/app.html.heex:20
|
||||||
#: lib/music_library_web/live/artist_live/show.html.heex:100
|
#: lib/music_library_web/live/artist_live/show.html.heex:100
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:75
|
#: lib/music_library_web/live/wishlist_live/index.ex:76
|
||||||
#: lib/music_library_web/live/wishlist_live/show.ex:122
|
#: lib/music_library_web/live/wishlist_live/show.ex:122
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Wishlist"
|
msgid "Wishlist"
|
||||||
@@ -381,10 +375,8 @@ msgstr ""
|
|||||||
msgid "Wishlisted"
|
msgid "Wishlisted"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:71
|
#: lib/music_library_web/components/record_components.ex:53
|
||||||
#: lib/music_library_web/live/collection_live/index.html.heex:97
|
#: lib/music_library_web/components/record_components.ex:79
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:71
|
|
||||||
#: lib/music_library_web/live/wishlist_live/index.html.heex:97
|
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Number of included records"
|
msgid "Number of included records"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -459,12 +451,12 @@ msgstr ""
|
|||||||
msgid "Details"
|
msgid "Details"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/collection_live/index.ex:42
|
#: lib/music_library_web/live/collection_live/index.ex:43
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Import from MusicBrainz · Collection"
|
msgid "Import from MusicBrainz · Collection"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: lib/music_library_web/live/wishlist_live/index.ex:42
|
#: lib/music_library_web/live/wishlist_live/index.ex:43
|
||||||
#, elixir-autogen, elixir-format
|
#, elixir-autogen, elixir-format
|
||||||
msgid "Import from MusicBrainz · Wishlist"
|
msgid "Import from MusicBrainz · Wishlist"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Reference in New Issue
Block a user