[Enhancement] Improve flow for adding many sources at once (#306)

* Added mechamism for using existing sources as a reference for adding new sources

* Added test
This commit is contained in:
Kieran 2024-07-12 08:53:26 -07:00 committed by GitHub
parent 7f1daf90ca
commit 8f91c4e6a2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 53 additions and 9 deletions

View file

@ -41,13 +41,30 @@ defmodule PinchflatWeb.Sources.SourceController do
render(conn, :index, sources: Repo.all(source_query)) render(conn, :index, sources: Repo.all(source_query))
end end
def new(conn, _params) do def new(conn, params) do
changeset = Sources.change_source(%Source{}) # This lets me preload the settings from another source for more efficient creation
cs_struct =
case to_string(params["template_id"]) do
"" -> %Source{}
template_id -> Repo.get(Source, template_id) || %Source{}
end
render(conn, :new, render(conn, :new,
changeset: changeset,
media_profiles: media_profiles(), media_profiles: media_profiles(),
layout: get_onboarding_layout() layout: get_onboarding_layout(),
# Most of these don't actually _need_ to be nullified at this point,
# but if I don't do it now I know it'll bite me
changeset:
Sources.change_source(%Source{
cs_struct
| id: nil,
uuid: nil,
custom_name: nil,
collection_name: nil,
collection_id: nil,
collection_type: nil,
original_url: nil
})
) )
end end

View file

@ -9,6 +9,7 @@ defmodule PinchflatWeb.Sources.SourceHTML do
attr :changeset, Ecto.Changeset, required: true attr :changeset, Ecto.Changeset, required: true
attr :action, :string, required: true attr :action, :string, required: true
attr :media_profiles, :list, required: true attr :media_profiles, :list, required: true
attr :method, :string, required: true
def source_form(assigns) def source_form(assigns)

View file

@ -26,6 +26,11 @@
<span x-show="copied" x-transition.duration.150ms><.icon name="hero-check" class="ml-2 h-4 w-4" /></span> <span x-show="copied" x-transition.duration.150ms><.icon name="hero-check" class="ml-2 h-4 w-4" /></span>
</span> </span>
</:option> </:option>
<:option>
<.link href={~p"/sources/new?template_id=#{@source}"} method="get">
Use as Template
</.link>
</:option>
<:option> <:option>
<div class="h-px w-full bg-bodydark2"></div> <div class="h-px w-full bg-bodydark2"></div>
</:option> </:option>

View file

@ -10,7 +10,12 @@
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> <div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<div class="max-w-full"> <div class="max-w-full">
<div class="flex flex-col gap-10"> <div class="flex flex-col gap-10">
<.source_form changeset={@changeset} media_profiles={@media_profiles} action={~p"/sources/#{@source}"} /> <.source_form
changeset={@changeset}
media_profiles={@media_profiles}
action={~p"/sources/#{@source}"}
method="patch"
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -8,7 +8,7 @@
<div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1"> <div class="rounded-sm border border-stroke bg-white px-5 pb-2.5 pt-6 shadow-default dark:border-strokedark dark:bg-boxdark sm:px-7.5 xl:pb-1">
<div class="max-w-full"> <div class="max-w-full">
<div class="flex flex-col gap-10"> <div class="flex flex-col gap-10">
<.source_form changeset={@changeset} media_profiles={@media_profiles} action={~p"/sources"} /> <.source_form changeset={@changeset} media_profiles={@media_profiles} action={~p"/sources"} method="post" />
</div> </div>
</div> </div>
</div> </div>

View file

@ -2,6 +2,7 @@
:let={f} :let={f}
for={@changeset} for={@changeset}
action={@action} action={@action}
method={@method}
x-data="{ advancedMode: !!JSON.parse(localStorage.getItem('advancedMode')) }" x-data="{ advancedMode: !!JSON.parse(localStorage.getItem('advancedMode')) }"
x-init="$watch('advancedMode', value => localStorage.setItem('advancedMode', JSON.stringify(value)))" x-init="$watch('advancedMode', value => localStorage.setItem('advancedMode', JSON.stringify(value)))"
> >
@ -10,7 +11,7 @@
</.error> </.error>
<section x-data="{ mediaProfileId: null }"> <section x-data="{ mediaProfileId: null }">
<section class="flex justify-between items-center mt-8"> <section class="flex justify-between items-center mt-4">
<h3 class=" text-2xl text-black dark:text-white"> <h3 class=" text-2xl text-black dark:text-white">
General Options General Options
</h3> </h3>
@ -19,6 +20,14 @@
</span> </span>
</section> </section>
<.input
field={f[:original_url]}
type="text"
label="Source URL"
help="URL of a channel or playlist (required)"
x-init="$el.focus()"
/>
<.input <.input
field={f[:custom_name]} field={f[:custom_name]}
type="text" type="text"
@ -26,8 +35,6 @@
help="Does not impact indexing or downloading. Will be inferred from the source if left blank" help="Does not impact indexing or downloading. Will be inferred from the source if left blank"
/> />
<.input field={f[:original_url]} type="text" label="Source URL" help="URL of a channel or playlist (required)" />
<.input <.input
field={f[:media_profile_id]} field={f[:media_profile_id]}
options={Enum.map(@media_profiles, &{&1.name, &1.id})} options={Enum.map(@media_profiles, &{&1.name, &1.id})}

View file

@ -50,6 +50,15 @@ defmodule PinchflatWeb.SourceControllerTest do
refute html_response(conn, 200) =~ "MENU" refute html_response(conn, 200) =~ "MENU"
end end
test "preloads some attributes when using a template", %{conn: conn} do
source = source_fixture(custom_name: "My first source", download_cutoff_date: "2021-01-01")
conn = get(conn, ~p"/sources/new", %{"template_id" => source.id})
assert html_response(conn, 200) =~ "New Source"
assert html_response(conn, 200) =~ "2021-01-01"
refute html_response(conn, 200) =~ source.custom_name
end
end end
describe "create source" do describe "create source" do