[WIP] Adding UI for getting RSS feed
This commit is contained in:
parent
24e6b2e868
commit
7879e7202f
6 changed files with 120 additions and 38 deletions
|
|
@ -14,7 +14,7 @@ defmodule PinchflatWeb.CoreComponents do
|
||||||
|
|
||||||
Icons are provided by [heroicons](https://heroicons.com). See `icon/1` for usage.
|
Icons are provided by [heroicons](https://heroicons.com). See `icon/1` for usage.
|
||||||
"""
|
"""
|
||||||
use Phoenix.Component
|
use Phoenix.Component, global_prefixes: ~w(x-)
|
||||||
|
|
||||||
import PinchflatWeb.Gettext
|
import PinchflatWeb.Gettext
|
||||||
|
|
||||||
|
|
@ -654,10 +654,11 @@ defmodule PinchflatWeb.CoreComponents do
|
||||||
"""
|
"""
|
||||||
attr :name, :string, required: true
|
attr :name, :string, required: true
|
||||||
attr :class, :string, default: nil
|
attr :class, :string, default: nil
|
||||||
|
attr :rest, :global
|
||||||
|
|
||||||
def icon(%{name: "hero-" <> _} = assigns) do
|
def icon(%{name: "hero-" <> _} = assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<span class={[@name, @class]} />
|
<span class={[@name, @class]} {@rest} />
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
defmodule PinchflatWeb.CustomComponents.ButtonComponents do
|
defmodule PinchflatWeb.CustomComponents.ButtonComponents do
|
||||||
@moduledoc false
|
@moduledoc false
|
||||||
use Phoenix.Component
|
use Phoenix.Component, global_prefixes: ~w(x-)
|
||||||
|
|
||||||
|
alias PinchflatWeb.CoreComponents
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Render a button
|
Render a button
|
||||||
|
|
@ -39,4 +41,52 @@ defmodule PinchflatWeb.CustomComponents.ButtonComponents do
|
||||||
</button>
|
</button>
|
||||||
"""
|
"""
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Render a dropdown based off a button
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
<.button_dropdown text="Actions">
|
||||||
|
<:option>TEST</:option>
|
||||||
|
</.button_dropdown>
|
||||||
|
"""
|
||||||
|
attr :text, :string, required: true
|
||||||
|
attr :class, :string, default: ""
|
||||||
|
|
||||||
|
slot :option, required: true
|
||||||
|
|
||||||
|
def button_dropdown(assigns) do
|
||||||
|
~H"""
|
||||||
|
<div x-data="{ dropdownOpen: false }" class={["relative flex", @class]}>
|
||||||
|
<span
|
||||||
|
x-on:click.prevent="dropdownOpen = !dropdownOpen"
|
||||||
|
class={[
|
||||||
|
"cursor-pointer inline-flex gap-2.5 rounded-md bg-primary px-5.5 py-3",
|
||||||
|
"font-medium text-white hover:bg-opacity-95"
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<%= @text %>
|
||||||
|
<CoreComponents.icon
|
||||||
|
name="hero-chevron-down"
|
||||||
|
class="fill-current duration-200 ease-linear"
|
||||||
|
x-bind:class="dropdownOpen && 'rotate-180'"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div
|
||||||
|
x-show="dropdownOpen"
|
||||||
|
x-on:click.outside="dropdownOpen = false"
|
||||||
|
class="absolute left-0 top-full z-40 mt-2 w-full rounded-md bg-black py-3 shadow-card"
|
||||||
|
>
|
||||||
|
<ul class="flex flex-col">
|
||||||
|
<li :for={option <- @option}>
|
||||||
|
<span class="flex px-5 py-2 font-medium text-bodydark2 hover:text-white cursor-pointer">
|
||||||
|
<%= render_slot(option) %>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
"""
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -9,24 +9,31 @@ defmodule PinchflatWeb.CustomComponents.TabComponents do
|
||||||
attr :title, :string, required: true
|
attr :title, :string, required: true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
slot :tab_append, required: false
|
||||||
|
|
||||||
def tabbed_layout(assigns) do
|
def tabbed_layout(assigns) do
|
||||||
~H"""
|
~H"""
|
||||||
<div
|
<div
|
||||||
x-data="{ openTab: 0, activeClasses: 'text-meta-5 border-meta-5', inactiveClasses: 'border-transparent' }"
|
x-data="{ openTab: 0, activeClasses: 'text-meta-5 border-meta-5', inactiveClasses: 'border-transparent' }"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
>
|
>
|
||||||
<div class="mb-6 flex flex-wrap gap-5 border-b border-strokedark sm:gap-10">
|
<header class="flex flex-col md:flex-row md:justify-between border-b border-strokedark">
|
||||||
|
<div class="flex flex-wrap gap-5 sm:gap-10">
|
||||||
<a
|
<a
|
||||||
:for={{tab, idx} <- Enum.with_index(@tab)}
|
:for={{tab, idx} <- Enum.with_index(@tab)}
|
||||||
href="#"
|
href="#"
|
||||||
@click.prevent={"openTab = #{idx}"}
|
@click.prevent={"openTab = #{idx}"}
|
||||||
x-bind:class={"openTab === #{idx} ? activeClasses : inactiveClasses"}
|
x-bind:class={"openTab === #{idx} ? activeClasses : inactiveClasses"}
|
||||||
class="border-b-2 py-4 text-sm font-medium hover:text-meta-5 md:text-base"
|
class="border-b-2 py-4 w-full sm:w-fit text-sm font-medium hover:text-meta-5 md:text-base"
|
||||||
>
|
>
|
||||||
<span class="text-xl"><%= tab.title %></span>
|
<span class="text-xl"><%= tab.title %></span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div class="mx-4 my-4 lg:my-0 flex gap-5 sm:gap-10 items-center">
|
||||||
|
<%= render_slot(@tab_append) %>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="mt-4">
|
||||||
<div :for={{tab, idx} <- Enum.with_index(@tab)} x-show={"openTab === #{idx}"} class="font-medium leading-relaxed">
|
<div :for={{tab, idx} <- Enum.with_index(@tab)} x-show={"openTab === #{idx}"} class="font-medium leading-relaxed">
|
||||||
<%= render_slot(tab) %>
|
<%= render_slot(tab) %>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -24,4 +24,11 @@ defmodule PinchflatWeb.Sources.SourceHTML do
|
||||||
{"Monthly", 30 * 24 * 60}
|
{"Monthly", 30 * 24 * 60}
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rss_feed_url(source) do
|
||||||
|
url_base = Application.get_env(:pinchflat, :url_base)
|
||||||
|
path = ~p"/sources/#{source.uuid}/feed"
|
||||||
|
|
||||||
|
Path.join(url_base, "#{path}.rss")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,45 @@
|
||||||
<div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
|
<div class="rounded-sm border border-stroke bg-white px-5 py-5 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5">
|
||||||
<div class="max-w-full overflow-x-auto">
|
<div class="max-w-full overflow-x-auto">
|
||||||
<.tabbed_layout>
|
<.tabbed_layout>
|
||||||
|
<:tab_append>
|
||||||
|
<.button_dropdown text="Actions" class="justify-center w-full sm:w-50">
|
||||||
|
<:option>
|
||||||
|
<span
|
||||||
|
x-data="{ copied: false }"
|
||||||
|
x-on:click={"
|
||||||
|
navigator.clipboard.writeText('#{rss_feed_url(@source)}');
|
||||||
|
copied = true;
|
||||||
|
setTimeout(() => copied = false, 4000);
|
||||||
|
"}
|
||||||
|
>
|
||||||
|
Copy RSS Feed <span x-show="copied"><.icon name="hero-check" class="ml-2 h-4 w-4" /></span>
|
||||||
|
</span>
|
||||||
|
</:option>
|
||||||
|
<:option>
|
||||||
|
<div class="h-px w-full bg-bodydark2"></div>
|
||||||
|
</:option>
|
||||||
|
<:option>
|
||||||
|
<.link
|
||||||
|
href={~p"/sources/#{@source}"}
|
||||||
|
method="delete"
|
||||||
|
data-confirm="Are you sure you want to delete this source (leaving files in place)? This cannot be undone."
|
||||||
|
>
|
||||||
|
Delete Source
|
||||||
|
</.link>
|
||||||
|
</:option>
|
||||||
|
<:option>
|
||||||
|
<.link
|
||||||
|
href={~p"/sources/#{@source}?delete_files=true"}
|
||||||
|
method="delete"
|
||||||
|
data-confirm="Are you sure you want to delete this source and it's files on disk? This cannot be undone."
|
||||||
|
class="mt-5 md:mt-0"
|
||||||
|
>
|
||||||
|
Delete Source + Files
|
||||||
|
</.link>
|
||||||
|
</:option>
|
||||||
|
</.button_dropdown>
|
||||||
|
</:tab_append>
|
||||||
|
|
||||||
<:tab title="Attributes">
|
<:tab title="Attributes">
|
||||||
<div class="flex flex-col gap-10 text-white">
|
<div class="flex flex-col gap-10 text-white">
|
||||||
<h3 class="font-bold text-lg">Attributes</h3>
|
<h3 class="font-bold text-lg">Attributes</h3>
|
||||||
|
|
@ -31,28 +70,6 @@
|
||||||
|
|
||||||
<.list_items_from_map map={Map.from_struct(@source)} />
|
<.list_items_from_map map={Map.from_struct(@source)} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="flex flex-col md:flex-row items-center md:justify-around mt-10">
|
|
||||||
<.link
|
|
||||||
href={~p"/sources/#{@source}"}
|
|
||||||
method="delete"
|
|
||||||
data-confirm="Are you sure you want to delete this source (leaving files in place)? This cannot be undone."
|
|
||||||
>
|
|
||||||
<.button color="bg-meta-1" rounding="rounded-lg">
|
|
||||||
Delete Source
|
|
||||||
</.button>
|
|
||||||
</.link>
|
|
||||||
<.link
|
|
||||||
href={~p"/sources/#{@source}?delete_files=true"}
|
|
||||||
method="delete"
|
|
||||||
data-confirm="Are you sure you want to delete this source and it's files on disk? This cannot be undone."
|
|
||||||
class="mt-5 md:mt-0"
|
|
||||||
>
|
|
||||||
<.button color="bg-meta-1" rounding="rounded-lg">
|
|
||||||
Delete Source and Files
|
|
||||||
</.button>
|
|
||||||
</.link>
|
|
||||||
</section>
|
|
||||||
</:tab>
|
</:tab>
|
||||||
<:tab title="Pending Media">
|
<:tab title="Pending Media">
|
||||||
<%= if match?([_|_], @pending_media) do %>
|
<%= if match?([_|_], @pending_media) do %>
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ defmodule PinchflatWeb.Router do
|
||||||
# TODO: consider putting the basic auth here behind a config flag
|
# TODO: consider putting the basic auth here behind a config flag
|
||||||
# so people that want RSS feeds to work can enable it.
|
# so people that want RSS feeds to work can enable it.
|
||||||
scope "/", PinchflatWeb do
|
scope "/", PinchflatWeb do
|
||||||
get "/sources/:uuid/rss_feed", Podcasts.PodcastController, :rss_feed
|
get "/sources/:uuid/feed", Podcasts.PodcastController, :rss_feed
|
||||||
get "/sources/:uuid/feed_image", Podcasts.PodcastController, :feed_image
|
get "/sources/:uuid/feed_image", Podcasts.PodcastController, :feed_image
|
||||||
|
|
||||||
get "/media/:uuid/stream", MediaItems.MediaItemController, :stream
|
get "/media/:uuid/stream", MediaItems.MediaItemController, :stream
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue