fix: creates form for user media preferences

This commit is contained in:
2025-07-23 21:17:18 -05:00
parent 8ff9cddbb0
commit 93f5b716b2
10 changed files with 150 additions and 94 deletions

11
assets/bootstrap.js vendored
View File

@@ -1,11 +1,14 @@
import { startStimulusApp } from '@symfony/stimulus-bundle';
import Popover from '@stimulus-components/popover'
import Dialog from '@stimulus-components/dialog'
import Dropdown from '@stimulus-components/dropdown'
import 'animate.css'
import Popover from '@stimulus-components/popover';
import Dialog from '@stimulus-components/dialog';
import Dropdown from '@stimulus-components/dropdown';
import 'animate.css';
import Brock from './components/brock.js';
const app = startStimulusApp();
// register any custom, 3rd party controllers here
app.register('popover', Popover);
app.register('dialog', Dialog);
app.register('dropdown', Dropdown);
customElements.define('brock-app', Brock);

View File

@@ -0,0 +1,25 @@
export default class Brock extends HTMLElement {
constructor() {
super();
}
connectedCallback() {
this.render();
}
render() {
this.innerHTML = `
Yo, yo, yo! Waddup ${this.name}, doe, it's Brocky fresh!
`;
}
// attribute change
attributeChangedCallback(property, oldValue, newValue) {
if (oldValue === newValue) return;
this[ property ] = newValue;
this.render();
}
static get observedAttributes() {
return ['name'];
}
}

View File

@@ -65,7 +65,7 @@ dialog[data-dialog-target="dialog"][closing] {
}
.text-input {
@apply bg-gray-50 text-gray-50 p-1 bg-transparent border-b-2 border-orange-400
@apply bg-gray-50 text-gray-50 px-2 py-1 bg-transparent border-b-2 border-orange-400
}
.submit-button {

View File

@@ -102,7 +102,7 @@ class QualityList
public static function asSelectOptions(): array
{
$result = [];
$result = ['n/a' => null];
foreach (array_keys(static::$qualities) as $quality) {
$result[$quality] = $quality;
}

View File

@@ -34,14 +34,14 @@ class UserPreferencesFactory
private static function getNestedValue(UserInterface $user, string $preferenceId): ?string
{
$preference = $user->getUserPreference($preferenceId);
if (null === $preference) {
if (null === $preference || "" === $preference || null === $preference->getPreferenceValue() || "" === $preference->getPreferenceValue()) {
return null;
}
return $preference->getPreference()
->getPreferenceOptions()
->filter(fn (PreferenceOption $option) => (string) $option->getId() === $preference->getPreferenceValue())
->first()
->getValue()
->getId()
;
}
}

View File

@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\User\Framework\Controller\Web;
use App\Base\Service\Broadcaster;
use App\User\Action\Command\SaveUserMediaPreferencesCommand;
use App\User\Action\Handler\SaveUserDownloadPreferencesHandler;
use App\User\Action\Handler\SaveUserMediaPreferencesHandler;
use App\User\Action\Input\SaveUserDownloadPreferencesInput;
@@ -14,8 +15,10 @@ use App\User\Database\ProviderList;
use App\User\Database\QualityList;
use App\User\Dto\UserPreferencesFactory;
use App\User\Framework\Form\GettingStartedFilterForm;
use App\User\Framework\Form\UserMediaPreferencesForm;
use App\User\Framework\Repository\PreferencesRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
@@ -26,24 +29,35 @@ class PreferencesController extends AbstractController
private readonly Broadcaster $broadcaster,
) {}
#[Route('/user/preferences', 'app_user_preferences', methods: ['GET'])]
public function mediaPreferences(): Response
#[Route('/user/preferences', 'app_user_preferences', methods: ['GET', 'POST'])]
public function mediaPreferences(Request $request, SaveUserMediaPreferencesHandler $saveUserMediaPreferencesHandler): Response
{
$mediaPreferences = $this->getUser()->getMediaPreferences();
$downloadPreferences = $this->getUser()->getDownloadPreferences();
$languages = CountryLanguages::$languages;
sort($languages);
$formData = (array) UserPreferencesFactory::createFromUser($this->getUser());
$form = $this->createForm(UserMediaPreferencesForm::class, $formData);
if ($request->isMethod('POST')) {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$saveUserMediaPreferencesHandler->handle(new SaveUserMediaPreferencesCommand(
resolution: (string) $form->get('resolution')->getData(),
codec: (string) $form->get('codec')->getData(),
quality: (string) $form->get('quality')->getData(),
language: (string) $form->get('language')->getData(),
provider: (string) $form->get('provider')->getData(),
));
$this->broadcaster->alert(
title: 'Success',
message: 'Your media preferences have been saved.'
);
}
}
return $this->render(
'user/preferences.html.twig',
[
'preferences' => $this->preferencesRepository->findEnabled(),
'languages' => $languages,
'providers' => ProviderList::getProviders(),
'qualities' => QualityList::getBaseQualities(),
'mediaPreferences' => $mediaPreferences,
'downloadPreferences' => $downloadPreferences,
'filterForm' => $this->createForm(GettingStartedFilterForm::class, (array) UserPreferencesFactory::createFromUser($this->getUser())),
'preferences_form' => $form,
]
);
}

View File

@@ -0,0 +1,60 @@
<?php
namespace App\User\Framework\Form;
use Aimeos\Map;
use App\User\Database\CountryLanguages;
use App\User\Database\ProviderList;
use App\User\Database\QualityList;
use App\User\Framework\Repository\PreferenceOptionRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserMediaPreferencesForm extends AbstractType
{
public function __construct(
private readonly PreferenceOptionRepository $preferenceOptionRepository,
) {}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$this->addChoiceField($builder, 'language', CountryLanguages::asSelectOptions());
$this->addChoiceField($builder, 'quality', QualityList::asSelectOptions());
$this->addChoiceField($builder, 'provider', ProviderList::asSelectOptions());
$this->addChoiceField($builder, 'resolution', $this->getPreferenceChoices('resolution'));
$this->addChoiceField($builder, 'codec', $this->getPreferenceChoices('codec'));
}
private function addChoiceField(FormBuilderInterface $builder, string $fieldName, array $choices): void
{
$question = [
'attr' => ['class' => 'w-64 text-input mb-4'],
'label_attr' => ['class' => 'w-64 text-white block font-semibold mb-2'],
'choices' => $this->addDefaultChoice($choices),
'required' => false,
];
$builder->add($fieldName, ChoiceType::class, $question);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([]);
}
private function getPreferenceChoices(string $preference): array
{
$options = $this->preferenceOptionRepository->findBy(['preference' => $preference]);
$result = [];
foreach ($options as $item) {
$result[$item->getName()] = $item->getId();
}
return $result;
}
private function addDefaultChoice(array $choices): iterable
{
return ['n/a' => ''] + $choices;
}
}

View File

@@ -10,6 +10,8 @@ module.exports = {
"flex-row",
"p-2",
"p-4",
"w-32",
"w-64",
"bg-blue-300",
"bg-orange-300",
"bg-fuchsia-300",

View File

@@ -0,0 +1,17 @@
<div{{ attributes }}>
<label class="text-gray-50" for="quality">{{ label }}</label>
<select class="p-1.5 rounded-md mb-2" name="quality" id="quality" value="{{ value }}">
{% if true == show_na %}
<option class="text-gray-800"
value=""
{{ value is null ? "selected" }}
>n/a</option>
{% endif %}
{% for option in options %}
<option class="text-gray-800"
value="{{ option.value }}"
{{ quality == option.value ? "selected" }}
>{{ option.label }}</option>
{% endfor %}
</select>
</div>

View File

@@ -5,84 +5,19 @@
{% block body %}
<div class="p-4 flex flex-col md:flex-row gap-2">
<twig:Card title="Media Preferences" class="w-full">
<p class="text-gray-50 mb-2">Define a filter to be pre-applied to your download options.</p>
<form id="media_preferences" class="flex flex-col max-w-64" name="media_preferences" method="post" action="{{ path('app_save_media_preferences') }}">
<label class="text-gray-50" for="quality">Quality</label>
<select class="p-1.5 rounded-md mb-2" name="quality" id="quality" value="{{ mediaPreferences['quality'].getPreferenceValue() }}">
<option class="text-gray-800"
value=""
{{ mediaPreferences['quality'].getPreferenceValue() is null ? "selected" }}
>n/a</option>
{% for quality in qualities %}
<option class="text-gray-800"
value="{{ quality }}"
{{ quality == mediaPreferences['quality'].getPreferenceValue() ? "selected" }}
>{{ quality }}</option>
{% endfor %}
</select>
<label class="text-gray-50" for="resolution">Resolution</label>
<select class="p-1.5 rounded-md mb-2" name="resolution" id="resolution" value="{{ mediaPreferences['resolution'].getPreferenceValue() }}">
<option class="text-gray-800"
value=""
{{ mediaPreferences['resolution'] is null ? "selected" }}
>n/a</option>
{% for pref in mediaPreferences['resolution'].getPreference().getPreferenceOptions() %}
<option class="text-gray-800"
value="{{ pref.id }}"
{{ pref.id == mediaPreferences['resolution'].getPreferenceValue() ? "selected" }}
>{{ pref.name }}</option>
{% endfor %}
</select>
<label class="text-gray-50" for="codec">Codec</label>
<select class="p-1.5 rounded-md mb-2" name="codec" id="codec" value="{{ mediaPreferences['codec'].getPreferenceValue() }}">
<option class="text-gray-800"
value=""
{{ mediaPreferences['codec'].getPreferenceValue() is null ? "selected" }}
>n/a</option>
{% for pref in mediaPreferences['codec'].getPreference().getPreferenceOptions() %}
<option class="text-gray-800"
value="{{ pref.id }}"
{{ pref.id == mediaPreferences['codec'].getPreferenceValue() ? "selected" }}
>{{ pref.name }}</option>
{% endfor %}
</select>
<label class="text-gray-50" for="provider">Provider</label>
<select class="p-1.5 rounded-md mb-2" name="provider" id="provider" value="{{ mediaPreferences['provider'].getPreferenceValue() }}">
<option class="text-gray-800"
value=""
{{ "" == mediaPreferences['provider'].getPreferenceValue() ? "selected" }}
>n/a</option>
{% for provider in providers %}
<option class="text-gray-800"
value="{{ provider }}"
{{ provider == mediaPreferences['provider'].getPreferenceValue() ? "selected" }}
>{{ provider }}</option>
{% endfor %}
</select>
<label class="text-gray-50" for="language">Language</label>
<select class="p-1.5 rounded-md mb-2" name="language" id="language" value="{{ mediaPreferences['language'].getPreferenceValue() }}">
<option class="text-gray-800"
value=""
{{ mediaPreferences['language'].getPreferenceValue() is null ? "selected" }}
>n/a</option>
{% for language in languages %}
<option class="text-gray-800"
value="{{ language }}"
{{ language == mediaPreferences['language'].getPreferenceValue() ? "selected" }}
>{{ language }}</option>
{% endfor %}
</select>
<button class="px-1.5 py-1 max-w-20 rounded-md bg-green-600 text-white" type="submit">Submit</button>
</form>
<p class="text-gray-50 mb-4">Define a filter to be pre-applied to your download options.</p>
{{ form_start(preferences_form) }}
{{ form_row(preferences_form.language) }}
{{ form_row(preferences_form.quality) }}
{{ form_row(preferences_form.provider) }}
{{ form_row(preferences_form.resolution) }}
{{ form_row(preferences_form.codec) }}
<button class="submit-button">Save</button>
{{ form_end(preferences_form) }}
</twig:Card>
<twig:Card title="Download Preferences" class="w-full">
<p class="text-gray-50 mb-2">Change how your downloads are stored.</p>
<p class="text-gray-50 mb-4">Change how your downloads are stored.</p>
<form id="download_preferences" class="flex flex-col" name="download_preferences" method="post" action="{{ path('app_save_download_preferences') }}">
<div class="flex flex-row gap-2 mb-2">
<input type="hidden" name="movie_folder" id="movie_folder_hidden" value="0" />