[TWIG] Restructure user settings template, making it much easier to use and extend

This commit is contained in:
Hugo Sales 2021-12-08 17:53:29 +00:00
parent 6627006e61
commit d52a043705
Signed by: someonewithpc
GPG Key ID: 7D0C7EAFC9D835A0
9 changed files with 107 additions and 158 deletions

View File

@ -50,15 +50,16 @@ class Avatar extends Component
/** /**
* @throws \App\Util\Exception\ClientException * @throws \App\Util\Exception\ClientException
*/ */
public function onPopulateProfileSettingsTabs(Request $request, &$tabs): bool public function onPopulateSettingsTabs(Request $request, string $section, &$tabs): bool
{ {
// TODO avatar template shouldn't be on settings folder if ($section === 'profile') {
$tabs[] = [ $tabs[] = [
'title' => 'Avatar', 'title' => 'Avatar',
'desc' => 'Change your avatar.', 'desc' => 'Change your avatar.',
'controller' => C\Avatar::settings_avatar($request), 'id' => 'settings-avatar',
]; 'controller' => C\Avatar::settings_avatar($request),
];
}
return Event::next; return Event::next;
} }

View File

@ -127,6 +127,6 @@ class Avatar extends Controller
} }
} }
return ['_template' => 'settings/avatar.html.twig', 'avatar' => $form->createView()]; return ['_template' => 'avatar/settings.html.twig', 'avatar' => $form->createView()];
} }
} }

View File

@ -54,14 +54,16 @@ class Cover extends Plugin
return Event::next; return Event::next;
} }
public function onPopulateProfileSettingsTabs(Request $request, &$tabs) public function onPopulateSettingsTabs(Request $request, string $section, &$tabs)
{ {
$tabs[] = [ if ($section === 'profile') {
'title' => 'Cover', $tabs[] = [
'desc' => 'Change your cover.', 'title' => 'Cover',
'controller' => C\Cover::coverSettings($request), 'desc' => 'Change your cover.',
]; 'id' => 'settings-cover',
'controller' => C\Cover::coverSettings($request),
];
}
return Event::next; return Event::next;
} }

View File

@ -41,9 +41,9 @@ class EmailNotifications extends Plugin
{ {
public function onAddNotificationTransport(&$form_defs): bool public function onAddNotificationTransport(&$form_defs): bool
{ {
$form_defs['email'] = $form_defs['placeholder']; $form_defs['Email'] = $form_defs['placeholder'];
$form_defs['email'][] = $form_defs['placeholder']['save']('Email', 'save_email'); $form_defs['Email'][] = $form_defs['placeholder']['save']('Email', 'save_email');
unset($form_defs['email']['post_on_status_change']); unset($form_defs['Email']['post_on_status_change']);
return Event::next; return Event::next;
} }
} }

View File

@ -28,17 +28,13 @@ use App\Core\Event;
use App\Core\Modules\Plugin; use App\Core\Modules\Plugin;
use App\Core\Router\RouteLoader; use App\Core\Router\RouteLoader;
use App\Core\Router\Router; use App\Core\Router\Router;
use App\Entity\Actor;
use App\Entity\LocalUser; use App\Entity\LocalUser;
use App\Util\Common; use App\Util\Common;
use App\Util\Exception\ClientException;
use App\Util\Exception\NotFoundException; use App\Util\Exception\NotFoundException;
use App\Util\Exception\RedirectException; use App\Util\Exception\RedirectException;
use App\Util\Exception\ServerException; use App\Util\Exception\ServerException;
use App\Util\Formatting;
use Plugin\Oomox\Controller as C; use Plugin\Oomox\Controller as C;
use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Request;
use function App\Core\I18n\_m;
/** /**
* Profile Colour plugin main class * Profile Colour plugin main class
@ -52,12 +48,8 @@ use function App\Core\I18n\_m;
*/ */
class Oomox extends Plugin class Oomox extends Plugin
{ {
/** /**
* Map URLs to actions * Map URLs to actions
*
* @param RouteLoader $r
* @return bool
*/ */
public function onAddRoute(RouteLoader $r): bool public function onAddRoute(RouteLoader $r): bool
{ {
@ -66,55 +58,49 @@ class Oomox extends Plugin
return Event::next; return Event::next;
} }
/** /**
* Populates an additional profile user panel section * Populates an additional profile user panel section
* Used in templates/settings/base.html.twig * Used in templates/settings/base.html.twig
* *
* @param Request $request * @throws \App\Util\Exception\NoLoggedInUser
* @param array $tabs
* @return bool
* @throws RedirectException * @throws RedirectException
* @throws ServerException * @throws ServerException
* @throws \App\Util\Exception\NoLoggedInUser
*/ */
public function onPopulateProfileSettingsTabs(Request $request, array &$tabs): bool public function onPopulateSettingsTabs(Request $request, string $section, array &$tabs): bool
{ {
$tabs[] = [ if ($section === 'colours') {
'title' => 'Light theme colours', $tabs[] = [
'desc' => 'Change the theme colours.', 'title' => 'Light theme colours',
'controller' => C\Oomox::oomoxSettingsLight($request), 'desc' => 'Change the theme colours.',
]; 'id' => 'settings-light-theme-colours-details',
'controller' => C\Oomox::oomoxSettingsLight($request),
$tabs[] = [ ];
'title' => 'Dark theme colours',
'desc' => 'Change the theme colours.',
'controller' => C\Oomox::oomoxSettingsDark($request),
];
$tabs[] = [
'title' => 'Dark theme colours',
'desc' => 'Change the theme colours.',
'id' => 'settings-dark-theme-colours-details',
'controller' => C\Oomox::oomoxSettingsDark($request),
];
}
return Event::next; return Event::next;
} }
/** /**
* Returns Oomox cache key for the given user. * Returns Oomox cache key for the given user.
*
* @param LocalUser $user
* @return string
*/ */
public static function cacheKey(LocalUser $user) :string { public static function cacheKey(LocalUser $user): string
{
return "oomox-css-{$user->getId()}"; return "oomox-css-{$user->getId()}";
} }
/** /**
* Returns Entity\Oomox if it already exists * Returns Entity\Oomox if it already exists
*
* @param LocalUser $user
* @return Entity\Oomox|null
*/ */
public static function getEntity(LocalUser $user): ?Entity\Oomox public static function getEntity(LocalUser $user): ?Entity\Oomox
{ {
try { try {
return Cache::get(self::cacheKey($user), fn() => DB::findOneBy('oomox', ['actor_id' => $user->getId()])); return Cache::get(self::cacheKey($user), fn () => DB::findOneBy('oomox', ['actor_id' => $user->getId()]));
} catch (NotFoundException $e) { } catch (NotFoundException $e) {
return null; return null;
} }
@ -123,14 +109,12 @@ class Oomox extends Plugin
/** /**
* Adds to array $styles the generated CSS according to user settings, if any are present. * Adds to array $styles the generated CSS according to user settings, if any are present.
* *
* @param array $styles
* @param string $route
* @return bool * @return bool
*/ */
public function onEndShowStyles(array &$styles, string $route) public function onEndShowStyles(array &$styles, string $route)
{ {
$user = Common::user(); $user = Common::user();
if (!is_null($user) && !is_null(Cache::get(self::cacheKey($user), fn () => self::getEntity($user)))) { if (!\is_null($user) && !\is_null(Cache::get(self::cacheKey($user), fn () => self::getEntity($user)))) {
$styles[] = Router::url('oomox_css'); $styles[] = Router::url('oomox_css');
} }
return Event::next; return Event::next;

View File

@ -61,15 +61,16 @@ class ProfileColor extends Plugin
* @throws RedirectException * @throws RedirectException
* @throws ServerException * @throws ServerException
*/ */
public function onPopulateProfileSettingsTabs(Request $request, array &$tabs): bool public function onPopulateSettingsTabs(Request $request, string $section, array &$tabs): bool
{ {
// TODO avatar template shouldn't be on settings folder if ($section === 'colours') {
$tabs[] = [ $tabs[] = [
'title' => 'Color', 'title' => 'Profile Colour',
'desc' => 'Change your profile color.', 'desc' => 'Change your profile colour.',
'controller' => C\ProfileColor::profileColorSettings($request), 'id' => 'settings-profile-colour-details',
]; 'controller' => C\ProfileColor::profileColorSettings($request),
];
}
return Event::next; return Event::next;
} }

View File

@ -41,8 +41,8 @@ class XMPPNotifications extends Plugin
{ {
public function onAddNotificationTransport(&$form_defs): bool public function onAddNotificationTransport(&$form_defs): bool
{ {
$form_defs['xmpp'] = $form_defs['placeholder']; $form_defs['XMPP'] = $form_defs['placeholder'];
$form_defs['xmpp'][] = $form_defs['placeholder']['save']('XMMP', 'save_xmpp'); $form_defs['XMPP'][] = $form_defs['placeholder']['save']('XMMP', 'save_xmpp');
return Event::next; return Event::next;
} }
} }

View File

@ -95,10 +95,10 @@ class UserPanel extends Controller
return [ return [
'_template' => 'settings/base.html.twig', '_template' => 'settings/base.html.twig',
'profile' => $personal_form->createView(), 'personal_info_form' => $personal_form->createView(),
'email' => $email_form->createView(), 'email_form' => $email_form->createView(),
'password' => $password_form->createView(), 'password_form' => $password_form->createView(),
'language' => $language_form->createView(), 'language_form' => $language_form->createView(),
'tabbed_forms_notify' => $notifications_form_array, 'tabbed_forms_notify' => $notifications_form_array,
'open_details_query' => $this->string('open'), 'open_details_query' => $this->string('open'),
]; ];
@ -327,8 +327,11 @@ class UserPanel extends Controller
$tabbed_forms = []; $tabbed_forms = [];
foreach ($form_defs as $transport_name => $f) { foreach ($form_defs as $transport_name => $f) {
unset($f['save']); unset($f['save']);
$form = Form::create($f); $form = Form::create($f);
$tabbed_forms[$transport_name] = $form; $tabbed_forms[$transport_name]['title'] = $transport_name;
$tabbed_forms[$transport_name]['desc'] = _m('{transport} notification settings', ['transport' => $transport_name]);
$tabbed_forms[$transport_name]['id'] = "settings-notifications-{$transport_name}";
$tabbed_forms[$transport_name]['form'] = $form->createView();
$form->handleRequest($request); $form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) { if ($form->isSubmitted() && $form->isValid()) {
@ -353,7 +356,6 @@ class UserPanel extends Controller
} }
} }
$tabbed_forms = F\map($tabbed_forms, fn ($f) => $f->createView());
return $tabbed_forms; return $tabbed_forms;
} }

View File

@ -8,8 +8,11 @@
<link rel="stylesheet" href="{{ asset('assets/default_theme/css/pages/settings.css') }}"> <link rel="stylesheet" href="{{ asset('assets/default_theme/css/pages/settings.css') }}">
{% endblock stylesheets %} {% endblock stylesheets %}
{% macro settings_details_element(title, summary, form_name, context) %} {% macro settings_details_element(title, summary, id, details_content, context, details_ids) %}
<details class="section-title-settings" {{ open_details(context.open_details_query, [ form_name ]) }} id={{ form_name ~ '-details' }}> {% if details_ids is null %}
{% set details_ids = [id] %}
{% endif %}
<details class="section-title-settings" {{ open_details(context.open_details_query, details_ids) }} id={{ id }}>
<summary> <summary>
<h3> <h3>
{{ title | trans }} {{ title | trans }}
@ -20,87 +23,37 @@
</em> </em>
</summary> </summary>
{% if details_content is instanceof('Twig\\Markup') %}
{{ form(attribute(context, form_name) | default) }} {{ details_content }}
{% else %}
{{ form(details_content) }}
{% endif %}
</details> </details>
{% endmacro settings_details_element %} {% endmacro settings_details_element %}
{% macro settings_details_container(title, summary, tabs, context) %} {% macro settings_details_container(title, summary, container_id, tabs, context) %}
<details class="section-title-settings" {{ open_details(context.open_details_query, tabs) }} id={{ tabs[0] ~ '-details' }}> {% set children_ids = [container_id] %}
<summary> {% set details_content %}
<h3> <nav class='section-settings-nav'>
{{ title | trans }} <ul>
{{ icon('arrow-down', 'icon icon-details-open') | raw }} {% for tab in tabs %}
</h3> <hr>
<em> <li>
{{ summary | trans }} {% set content %}
</em> {% if tab['controller'] is defined %}
</summary> {% include tab['controller']['_template'] with tab['controller'] only %}
{% else %}
{{ form(tab['form']) }}
{% if title == 'Profile' %} {% endif %}
<li> {% endset %}
<nav class='section-settings-nav'> {{ _self.settings_details_element(tab['title'], tab['desc'], tab['id'] ~ '-details', content, context) }}
<ul> {% set children_ids = children_ids|merge([tab['id'] ~ '-details']) %}
<li> </li>
{{ _self.settings_details_element('Personal Info', 'Nickname, Homepage, Bio, Self Tags and more.', 'profile', context) }} {% endfor %}
</li> </ul>
</nav>
<hr> {% endset %}
{{ _self.settings_details_element(title, summary, container_id, details_content, context, children_ids) }}
{% for tab in context.profile_tabs %}
<li>
<details class="section-title-settings"> {# TODO: add ID and open_details and to parent #}
<summary>
<h3>{{ tab['title'] }}{{ icon('arrow-down', 'icon icon-details-open') | raw }}</h3>
<em>{{ tab['desc'] }}</em>
</summary>
{% include tab['controller']['_template'] with tab['controller'] only %}
</details>
</li>
<hr>
{% endfor %}
</ul>
</nav>
</li>
{% elseif title == 'Email' %}
<li>
{{ form(context.email) }}
</li>
{% elseif title == 'Password' %}
<li>
{{ form(context.password) }}
</li>
{% elseif title == 'Language' %}
<li>
{{ form(context.language) }}
</li>
{% elseif title == 'Notifications' %}
<li>
<nav class='section-settings-nav'>
<ul>
{% for transport, form_transport in context.tabbed_forms_notify %}
<li>
<details class="section-title-settings">
<summary>
<h3>{{ transport }}{{ icon('arrow-down', 'icon icon-details-open') | raw }}</h3>
<em>{{ transport }}'s notification options.</em>
</summary>
<div id="form_{{ transport }}" class='form'>
{{ form(form_transport) }}
</div>
</details>
</li>
<hr>
{% endfor %}
</ul>
</nav>
</li>
{% endif %}
</details>
{% endmacro settings_details_container %} {% endmacro settings_details_container %}
{% block body %} {% block body %}
@ -108,22 +61,28 @@
<h2>Settings</h2> <h2>Settings</h2>
<ul> <ul>
<li> <li>
{% set profile_tabs = handle_event('PopulateProfileSettingsTabs', app.request) %} {% set profile_tabs = [{'title': 'Personal Info', 'desc': 'Nickname, Homepage, Bio, Self Tags and more.', 'id': 'settings-personal-info', 'form': personal_info_form}] %}
{{ _self.settings_details_container('Profile', 'Personal Information, Avatar and Profile', ['profile', 'personal-info'], _context) }} {% set profile_tabs = profile_tabs|merge(handle_event('PopulateSettingsTabs', app.request, 'profile')) %}
{{ _self.settings_details_container('Profile', 'Personal Information, Avatar and Profile', 'settings-profile-details', profile_tabs, _context) }}
</li> </li>
<hr> <hr>
<li> <li>
{{ _self.settings_details_container('Email', 'Set incoming and outgoing email settings', ['email'], _context) }} {{ _self.settings_details_element('Email', 'Set incoming and outgoing email settings', 'settings-email-details', email_form, _context) }}
</li> </li>
<li> <li>
{{ _self.settings_details_container('Password', 'Change current password', ['password'], _context) }} {{ _self.settings_details_element('Password', 'Change current password', 'settings-password-details', password_form, _context) }}
</li> </li>
<li> <li>
{{ _self.settings_details_container('Language', 'Select and set language preferences', ['language'], _context) }} {{ _self.settings_details_element('Language', 'Select and set language preferences', 'settings-language-details', language_form, _context) }}
</li> </li>
<hr> <hr>
<li> <li>
{{ _self.settings_details_container('Notifications', 'Enable/disable notifications (Email, XMPP, Replies...)', ['notifications'], _context) }} {% set colour_tabs = handle_event('PopulateSettingsTabs', app.request, 'colours') %}
{{ _self.settings_details_container('Colours', 'Customize your interface colours', 'settings-colour-details', colour_tabs, _context) }}
</li>
<hr>
<li>
{{ _self.settings_details_container('Notifications', 'Enable/disable notifications (Email, XMPP, Replies...)', 'notifications', tabbed_forms_notify, _context) }}
</li> </li>
</ul> </ul>
</nav> </nav>