From 33d01b8d4fd6ebf24e9f011aa705c456c846956c Mon Sep 17 00:00:00 2001 From: Yassine Doghri <yassine@doghri.fr> Date: Tue, 22 Jun 2021 17:59:33 +0000 Subject: [PATCH] fix(ux): allow for empty message upon episode publication and warn user on submit - clarify distiction between the announcement post and the show notes - change "note" occurences in UI by "post" - show warning message explaining why the podcaster should fill the message area - the podcaster can choose to publish the episode with an empty message anyways - redirect user to episode dashboard with error message when episode publication pages are inaccessible instead of showing a 404 error - add a cancel publication button in publish-edit form when episode is scheduled closes #129 --- app/Config/Routes.php | 9 + app/Controllers/Admin/EpisodeController.php | 59 ++++++- app/Helpers/components_helper.php | 4 +- app/Language/en/Episode.php | 17 +- app/Language/en/Podcast.php | 4 +- app/Language/fr/Episode.php | 17 +- app/Language/fr/Podcast.php | 4 +- .../ActivityPub/Models/StatusModel.php | 2 +- app/Views/_assets/admin.ts | 2 + .../_assets/modules/PublishMessageWarning.ts | 51 ++++++ app/Views/admin/episode/publish.php | 157 +++++++++--------- app/Views/admin/episode/publish_edit.php | 114 ++++++++----- 12 files changed, 300 insertions(+), 140 deletions(-) create mode 100644 app/Views/_assets/modules/PublishMessageWarning.ts diff --git a/app/Config/Routes.php b/app/Config/Routes.php index f93778a8da..41f8be7538 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -310,6 +310,15 @@ $routes->group( 'permission:podcast-manage_publications', ], ); + $routes->get( + 'publish-cancel', + 'EpisodeController::publishCancel/$1/$2', + [ + 'as' => 'episode-publish-cancel', + 'filter' => + 'permission:podcast-manage_publications', + ], + ); $routes->get( 'unpublish', 'EpisodeController::unpublish/$1/$2', diff --git a/app/Controllers/Admin/EpisodeController.php b/app/Controllers/Admin/EpisodeController.php index 28ad1a8560..91c9a69954 100644 --- a/app/Controllers/Admin/EpisodeController.php +++ b/app/Controllers/Admin/EpisodeController.php @@ -388,7 +388,7 @@ class EpisodeController extends BaseController return redirect()->back(); } - public function publish(): string + public function publish(): string | RedirectResponse { if ($this->episode->publication_status === 'not_published') { helper(['form']); @@ -405,7 +405,10 @@ class EpisodeController extends BaseController return view('admin/episode/publish', $data); } - throw PageNotFoundException::forPageNotFound(); + return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with( + 'error', + lang('Episode.publish_error') + ); } public function attemptPublish(): RedirectResponse @@ -478,7 +481,7 @@ class EpisodeController extends BaseController return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); } - public function publishEdit(): string + public function publishEdit(): string | RedirectResponse { if ($this->episode->publication_status === 'scheduled') { helper(['form']); @@ -500,7 +503,11 @@ class EpisodeController extends BaseController ]); return view('admin/episode/publish_edit', $data); } - throw PageNotFoundException::forPageNotFound(); + + return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with( + 'error', + lang('Episode.publish_edit_error') + ); } public function attemptPublishEdit(): RedirectResponse @@ -572,7 +579,44 @@ class EpisodeController extends BaseController return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); } - public function unpublish(): string + public function publishCancel(): RedirectResponse + { + if ($this->episode->publication_status === 'scheduled') { + $db = db_connect(); + $db->transStart(); + + $statusModel = new StatusModel(); + $status = $statusModel + ->where([ + 'actor_id' => $this->podcast->actor_id, + 'episode_id' => $this->episode->id, + ]) + ->first(); + $statusModel->removeStatus($status); + + $this->episode->published_at = null; + + $episodeModel = new EpisodeModel(); + if (! $episodeModel->update($this->episode->id, $this->episode)) { + $db->transRollback(); + return redirect() + ->back() + ->withInput() + ->with('errors', $episodeModel->errors()); + } + + $db->transComplete(); + + return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); + } + + return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with( + 'error', + lang('Episode.publish_cancel_error') + ); + } + + public function unpublish(): string | RedirectResponse { if ($this->episode->publication_status === 'published') { helper(['form']); @@ -589,7 +633,10 @@ class EpisodeController extends BaseController return view('admin/episode/unpublish', $data); } - throw PageNotFoundException::forPageNotFound(); + return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id])->with( + 'error', + lang('Episode.unpublish_error') + ); } public function attemptUnpublish(): RedirectResponse diff --git a/app/Helpers/components_helper.php b/app/Helpers/components_helper.php index 53d1f524fa..90e33641f7 100644 --- a/app/Helpers/components_helper.php +++ b/app/Helpers/components_helper.php @@ -86,11 +86,11 @@ if (! function_exists('button')) { } if ($options['iconLeft']) { - $label = icon($options['iconLeft'], 'mr-2') . $label; + $label = icon((string) $options['iconLeft'], 'mr-2') . $label; } if ($options['iconRight']) { - $label .= icon($options['iconRight'], 'ml-2'); + $label .= icon((string) $options['iconRight'], 'ml-2'); } if ($uri !== '') { diff --git a/app/Language/en/Episode.php b/app/Language/en/Episode.php index 80fe2ec76e..4cd4568873 100644 --- a/app/Language/en/Episode.php +++ b/app/Language/en/Episode.php @@ -27,8 +27,8 @@ return [ other {# total shares} }', 'total_statuses' => '{numberOfTotalStatuses, plural, - one {# note} - other {# total notes} + one {# total post} + other {# total posts} }', 'all_podcast_episodes' => 'All podcast episodes', 'back_to_podcast' => 'Go back to podcast', @@ -36,6 +36,10 @@ return [ 'publish' => 'Publish', 'publish_edit' => 'Edit publication', 'unpublish' => 'Unpublish', + 'publish_error' => 'Episode is already published.', + 'publish_edit_error' => 'Episode is already published.', + 'publish_cancel_error' => 'Episode is already published.', + 'unpublish_error' => 'Episode is not published.', 'delete' => 'Delete', 'go_to_page' => 'Go to page', 'create' => 'Add an episode', @@ -112,9 +116,10 @@ return [ 'submit_edit' => 'Save episode', ], 'publish_form' => [ - 'status' => 'Your note', + 'back_to_episode_dashboard' => 'Back to episode dashboard', + 'status' => 'Your announcement post', 'status_hint' => - 'The message you write will be broadcasted to all your followers in the fediverse.', + "Write a message to announce the publication of your episode. The message will be broadcasted to all your followers in the fediverse and be featured in your podcast's homepage.", 'publication_date' => 'Publication date', 'publication_method' => [ 'now' => 'Now', @@ -126,6 +131,10 @@ return [ 'You can schedule the episode release by setting a future publication date. This field must be formatted as YYYY-MM-DD HH:mm', 'submit' => 'Publish', 'submit_edit' => 'Edit publication', + 'cancel_publication' => 'Cancel publication', + 'message_warning' => 'You did not write a message for your announcement post!', + 'message_warning_hint' => 'Having a message increases social engagement, resulting in a better visibility for your episode.', + 'message_warning_submit' => 'Publish anyways', ], 'unpublish_form' => [ 'disclaimer' => diff --git a/app/Language/en/Podcast.php b/app/Language/en/Podcast.php index fa9279f421..828ce4f233 100644 --- a/app/Language/en/Podcast.php +++ b/app/Language/en/Podcast.php @@ -224,8 +224,8 @@ return [ other {<span class="font-semibold">#</span> followers} }', 'statuses' => '{numberOfStatuses, plural, - one {<span class="font-semibold">#</span> note} - other {<span class="font-semibold">#</span> notes} + one {<span class="font-semibold">#</span> post} + other {<span class="font-semibold">#</span> posts} }', 'activity' => 'Activity', 'episodes' => 'Episodes', diff --git a/app/Language/fr/Episode.php b/app/Language/fr/Episode.php index 86233cfa4d..5f75b5061d 100644 --- a/app/Language/fr/Episode.php +++ b/app/Language/fr/Episode.php @@ -27,8 +27,8 @@ return [ other {# partages en tout} }', 'total_statuses' => '{numberOfTotalStatuses, plural, - one {# note} - other {# notes} + one {# message} + other {# messages} }', 'all_podcast_episodes' => 'Tous les épisodes du podcast', 'back_to_podcast' => 'Revenir au podcast', @@ -36,6 +36,10 @@ return [ 'publish' => 'Publier', 'publish_edit' => 'Modifier la publication', 'unpublish' => 'Dépublier', + 'publish_error' => 'L’épisode est déjà publié.', + 'publish_edit_error' => 'L’épisode est déjà publié.', + 'publish_cancel_error' => 'L’épisode est déjà publié.', + 'unpublish_error' => 'L’épisode n’est pas publié.', 'delete' => 'Supprimer', 'go_to_page' => 'Voir', 'create' => 'Ajouter un épisode', @@ -115,9 +119,10 @@ return [ 'submit_edit' => 'Enregistrer l’épisode', ], 'publish_form' => [ - 'status' => 'Votre note', + 'back_to_episode_dashboard' => 'Retour au tableau de bord de l’épisode', + 'status' => 'Votre message de publication', 'status_hint' => - 'Le message que vous écrirez sera diffusé à toutes les personnes qui vous suivent dans le fédiverse.', + 'Écrivez un message pour annoncer la publication de votre épisode. Le message sera diffusé à toutes les personnes qui vous suivent dans le fédiverse et mis en évidence sur la page d’accueil de votre podcast.', 'publication_date' => 'Date de publication', 'publication_date_clear' => 'Effacer la date de publication', 'publication_date_hint' => @@ -132,6 +137,10 @@ return [ 'Vous pouvez planifier la sortie de l’épisode en saisissant une date de publication future. Ce champ doit être au format YYYY-MM-DD HH:mm', 'submit' => 'Publier', 'submit_edit' => 'Modifier la publication', + 'cancel_publication' => 'Annuler la publication', + 'message_warning' => 'Vous n’avez pas saisi de message pour l’annonce de votre épisode !', + 'message_warning_hint' => 'Ajouter un message augmente l’engagement social, menant à une meilleure visibilité pour votre épisode.', + 'message_warning_submit' => 'Publish quand même', ], 'soundbites' => 'Extraits sonores', 'soundbites_form' => [ diff --git a/app/Language/fr/Podcast.php b/app/Language/fr/Podcast.php index c93d2893d9..b5ccb5b0eb 100644 --- a/app/Language/fr/Podcast.php +++ b/app/Language/fr/Podcast.php @@ -226,8 +226,8 @@ return [ other {<span class="font-semibold">#</span> abonné·e·s} }', 'notes' => '{numberOfStatuses, plural, - one {<span class="font-semibold">#</span> note} - other {<span class="font-semibold">#</span> notes} + one {<span class="font-semibold">#</span> message} + other {<span class="font-semibold">#</span> messages} }', 'activity' => 'Activité', 'episodes' => 'Épisodes', diff --git a/app/Libraries/ActivityPub/Models/StatusModel.php b/app/Libraries/ActivityPub/Models/StatusModel.php index 3f911babd0..941beb030d 100644 --- a/app/Libraries/ActivityPub/Models/StatusModel.php +++ b/app/Libraries/ActivityPub/Models/StatusModel.php @@ -81,7 +81,7 @@ class StatusModel extends UuidModel */ protected $validationRules = [ 'actor_id' => 'required', - 'message_html' => 'required_without[reblog_of_id]|max_length[500]', + 'message_html' => 'max_length[500]', ]; /** diff --git a/app/Views/_assets/admin.ts b/app/Views/_assets/admin.ts index be73152409..2580f6d5e7 100644 --- a/app/Views/_assets/admin.ts +++ b/app/Views/_assets/admin.ts @@ -4,6 +4,7 @@ import DateTimePicker from "./modules/DateTimePicker"; import Dropdown from "./modules/Dropdown"; import MarkdownEditor from "./modules/MarkdownEditor"; import MultiSelect from "./modules/MultiSelect"; +import PublishMessageWarning from "./modules/PublishMessageWarning"; import SidebarToggler from "./modules/SidebarToggler"; import Slugify from "./modules/Slugify"; import Soundbites from "./modules/Soundbites"; @@ -23,3 +24,4 @@ Time(); Soundbites(); Clipboard(); ThemePicker(); +PublishMessageWarning(); diff --git a/app/Views/_assets/modules/PublishMessageWarning.ts b/app/Views/_assets/modules/PublishMessageWarning.ts new file mode 100644 index 0000000000..fbde59f4ce --- /dev/null +++ b/app/Views/_assets/modules/PublishMessageWarning.ts @@ -0,0 +1,51 @@ +const PublishMessageWarning = (): void => { + const publishForm: HTMLFormElement | null = document.querySelector( + "form[data-submit='validate-message']" + ); + + if (publishForm) { + const messageTextArea: HTMLTextAreaElement | null = publishForm.querySelector( + "[name='message']" + ); + const submitButton: HTMLButtonElement | null = publishForm.querySelector( + "button[type='submit']" + ); + const publishMessageWarning: HTMLDivElement | null = publishForm.querySelector( + "[id='publish-warning']" + ); + + if ( + messageTextArea && + submitButton && + submitButton.dataset.btnTextWarning && + submitButton.dataset.btnText && + publishMessageWarning + ) { + publishForm.addEventListener("submit", (event) => { + if ( + messageTextArea.value === "" && + publishMessageWarning.classList.contains("hidden") + ) { + event.preventDefault(); + + publishMessageWarning.classList.remove("hidden"); + messageTextArea.focus(); + submitButton.innerText = submitButton.dataset + .btnTextWarning as string; + } + }); + + messageTextArea.addEventListener("input", () => { + if ( + submitButton.innerText !== submitButton.dataset.btnText && + messageTextArea.value !== "" + ) { + publishMessageWarning.classList.add("hidden"); + submitButton.innerText = submitButton.dataset.btnText as string; + } + }); + } + } +}; + +export default PublishMessageWarning; diff --git a/app/Views/admin/episode/publish.php b/app/Views/admin/episode/publish.php index d612d9037e..e1ed34bc51 100644 --- a/app/Views/admin/episode/publish.php +++ b/app/Views/admin/episode/publish.php @@ -11,26 +11,34 @@ <?= $this->section('content') ?> +<?= anchor( + route_to('episode-view', $podcast->id, $episode->id), + icon('arrow-left', 'mr-2 text-lg') . lang('Episode.publish_form.back_to_episode_dashboard'), + ['class' => 'inline-flex items-center font-semibold mr-4 text-sm'], +) ?> + <?= form_open(route_to('episode-publish', $podcast->id, $episode->id), [ 'method' => 'post', - 'class' => 'flex flex-col max-w-xl items-start', + 'class' => 'mx-auto flex flex-col max-w-xl items-start', + 'data-submit' => 'validate-message' ]) ?> <?= csrf_field() ?> <?= form_hidden('client_timezone', 'UTC') ?> <label for="message" class="text-lg font-semibold"><?= lang( - 'Episode.publish_form.status', -) . hint_tooltip(lang('Episode.publish_form.status_hint'), 'ml-1') ?></label> + 'Episode.publish_form.status', + ) ?></label> +<small class="max-w-md mb-2 text-gray-600"><?= lang('Episode.publish_form.status_hint') ?></small> <div class="mb-8 overflow-hidden bg-white shadow-md rounded-xl"> <div class="flex px-4 py-3"> <img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast - ->actor->display_name ?>" class="w-12 h-12 mr-4 rounded-full"/> + ->actor->display_name ?>" class="w-12 h-12 mr-4 rounded-full" /> <p class="flex items-baseline min-w-0"> <span class="mr-2 font-semibold truncate"><?= $podcast->actor - ->display_name ?></span> + ->display_name ?></span> <span class="text-sm text-gray-500 truncate">@<?= $podcast->actor - ->username ?></span> + ->username ?></span> </p> </div> <div class="px-4 mb-2"> @@ -39,17 +47,15 @@ 'id' => 'message', 'name' => 'message', 'class' => 'form-textarea min-w-0 w-full', - 'required' => 'required', 'placeholder' => 'Write your message...', + 'autofocus' => '' ], old('message', '', false), ['rows' => 2], ) ?> </div> <div class="flex"> - <img - src="<?= $episode->image->thumbnail_url ?>" - alt="<?= $episode->title ?>" class="w-24 h-24"/> + <img src="<?= $episode->image->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2 bg-gray-100"> <div class="flex items-baseline"> @@ -68,34 +74,32 @@ </div> </a> <audio controls preload="none" class="w-full mt-auto"> - <source - src="<?= $episode->audio_file_url ?>" - type="<?= $episode->audio_file_mimetype ?>"> + <source src="<?= $episode->audio_file_url ?>" type="<?= $episode->audio_file_mimetype ?>"> Your browser does not support the audio tag. </audio> </div> </div> <footer class="flex justify-around px-6 py-3"> <span class="inline-flex items-center"><?= icon( - 'chat', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'chat', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> <span class="inline-flex items-center"><?= icon( - 'repeat', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'repeat', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> <span class="inline-flex items-center"><?= icon( - 'heart', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'heart', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> </footer> </div> <?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?> - <legend class="text-lg font-semibold"><?= lang( - 'Episode.publish_form.publication_date', - ) ?></legend> - <label for="now" class="inline-flex items-center"> +<legend class="text-lg font-semibold"><?= lang( + 'Episode.publish_form.publication_date', + ) ?></legend> +<label for="now" class="inline-flex items-center"> <?= form_radio( [ 'id' => 'now', @@ -105,65 +109,70 @@ 'now', old('publication_method') ? old('publish') === 'now' : true, ) ?> - <span class="ml-2"><?= lang( - 'Episode.publish_form.publication_method.now', - ) ?></span> - </label> - <div class="inline-flex flex-wrap items-center mb-4 radio-toggler"> - <?= form_radio( - [ - 'id' => 'schedule', - 'name' => 'publication_method', - 'class' => 'text-pine-700', - ], - 'schedule', - old('publication_method') && - old('publication_method') === 'schedule', + <span class="ml-2"><?= lang( + 'Episode.publish_form.publication_method.now', + ) ?></span> +</label> +<div class="inline-flex flex-wrap items-center radio-toggler"> + <?= form_radio( + [ + 'id' => 'schedule', + 'name' => 'publication_method', + 'class' => 'text-pine-700', + ], + 'schedule', + old('publication_method') && + old('publication_method') === 'schedule', + ) ?> + <label for="schedule" class="ml-2"><?= lang( + 'Episode.publish_form.publication_method.schedule', + ) ?></label> + <div class="w-full mt-2 radio-toggler-element"> + <?= form_label( + lang('Episode.publish_form.scheduled_publication_date'), + 'scheduled_publication_date', + [], + lang('Episode.publish_form.scheduled_publication_date_hint'), ) ?> - <label for="schedule" class="ml-2"><?= lang( - 'Episode.publish_form.publication_method.schedule', - ) ?></label> - <div class="w-full mt-2 radio-toggler-element"> - <?= form_label( - lang('Episode.publish_form.scheduled_publication_date'), - 'scheduled_publication_date', - [], - lang('Episode.publish_form.scheduled_publication_date_hint'), - ) ?> - <div class="flex mb-4" data-picker="datetime"> - <?= form_input([ - 'id' => 'scheduled_publication_date', - 'name' => 'scheduled_publication_date', - 'class' => 'form-input rounded-r-none flex-1', - 'value' => old('scheduled_publication_date', ''), - 'data-input' => '', - ]) ?> - <button - class="p-3 border border-l-0 border-gray-500 bg-pine-100 focus:outline-none rounded-r-md hover:bg-pine-200 focus:ring" - type="button" - title="<?= lang( - 'Episode.publish_form.scheduled_publication_date_clear', - ) ?>" - data-clear=""><?= icon('close') ?></button> - </div> + <div class="flex" data-picker="datetime"> + <?= form_input([ + 'id' => 'scheduled_publication_date', + 'name' => 'scheduled_publication_date', + 'class' => 'form-input rounded-r-none flex-1', + 'value' => old('scheduled_publication_date', ''), + 'data-input' => '', + ]) ?> + <button class="p-3 border border-l-0 border-gray-500 bg-pine-100 focus:outline-none rounded-r-md hover:bg-pine-200 focus:ring" type="button" title="<?= lang( + 'Episode.publish_form.scheduled_publication_date_clear', + ) ?>" data-clear=""><?= icon('close') ?></button> </div> </div> +</div> <?= form_fieldset_close() ?> -<div class="self-end"> -<?= anchor( - route_to('episode-view', $podcast->id, $episode->id), - lang('Common.cancel'), - ['class' => 'font-semibold mr-4'], -) ?> +<div id="publish-warning" class="inline-flex flex-col hidden p-4 text-black bg-yellow-300 border-2 border-yellow-900 rounded-md" role="alert"> + <p class="flex items-baseline font-semibold"> + <?= icon('alert', 'mr-2 text-lg flex-shrink-0') . lang( + 'Episode.publish_form.message_warning', + ) ?></p> + <p> + <?= lang( + 'Episode.publish_form.message_warning_hint', + ) ?> + </p> +</div> <?= button( lang('Episode.publish_form.submit'), '', ['variant' => 'primary'], - ['type' => 'submit'], + [ + 'class' => 'self-end mt-4', + 'type' => 'submit', + 'data-btn-text-warning' => lang('Episode.publish_form.message_warning_submit'), + 'data-btn-text' => lang('Episode.publish_form.submit_edit') + ], ) ?> -</div> <?= form_close() ?> diff --git a/app/Views/admin/episode/publish_edit.php b/app/Views/admin/episode/publish_edit.php index 9c23551d54..52e23326f8 100644 --- a/app/Views/admin/episode/publish_edit.php +++ b/app/Views/admin/episode/publish_edit.php @@ -1,19 +1,26 @@ <?= $this->extend('admin/_layout') ?> <?= $this->section('title') ?> -<?= lang('Episode.publish') ?> +<?= lang('Episode.publish_edit') ?> <?= $this->endSection() ?> <?= $this->section('pageTitle') ?> -<?= lang('Episode.publish') ?> +<?= lang('Episode.publish_edit') ?> <?= $this->endSection() ?> <?= $this->section('content') ?> +<?= anchor( + route_to('episode-view', $podcast->id, $episode->id), + icon('arrow-left', 'mr-2 text-lg') . lang('Episode.publish_form.back_to_episode_dashboard'), + ['class' => 'inline-flex items-center font-semibold mr-4 text-sm'], +) ?> + <?= form_open(route_to('episode-publish_edit', $podcast->id, $episode->id), [ 'method' => 'post', - 'class' => 'flex flex-col max-w-xl items-start', + 'class' => 'mx-auto flex flex-col max-w-xl items-start', + 'data-submit' => 'validate-message' ]) ?> <?= csrf_field() ?> <?= form_hidden('client_timezone', 'UTC') ?> @@ -21,25 +28,26 @@ <label for="message" class="text-lg font-semibold"><?= lang( - 'Episode.publish_form.status', -) . hint_tooltip(lang('Episode.publish_form.status_hint'), 'ml-1') ?></label> + 'Episode.publish_form.status', + ) ?></label> +<small class="max-w-md mb-2 text-gray-600"><?= lang('Episode.publish_form.status_hint') ?></small> <div class="mb-8 overflow-hidden bg-white shadow-md rounded-xl"> <div class="flex px-4 py-3"> - <img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor - ->display_name ?>" class="w-12 h-12 mr-4 rounded-full"/> + <img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor + ->display_name ?>" class="w-12 h-12 mr-4 rounded-full" /> <div class="flex flex-col min-w-0"> <p class="flex items-baseline min-w-0"> <span class="mr-2 font-semibold truncate"><?= $podcast->actor - ->display_name ?></span> + ->display_name ?></span> <span class="text-sm text-gray-500 truncate">@<?= $podcast - ->actor->username ?></span> + ->actor->username ?></span> </p> <time class="text-xs text-gray-500" itemprop="published" datetime="<?= $status->published_at->format( - DateTime::ATOM, - ) ?>" title="<?= $status->published_at ?>"><?= lang( - 'Common.mediumDate', - [$status->published_at], -) ?></time> + DateTime::ATOM, + ) ?>" title="<?= $status->published_at ?>"><?= lang( + 'Common.mediumDate', + [$status->published_at], + ) ?></time> </div> </div> <div class="px-4 mb-2"> @@ -48,8 +56,8 @@ 'id' => 'message', 'name' => 'message', 'class' => 'form-textarea', - 'required' => 'required', 'placeholder' => 'Write your message...', + 'autofocus' => '' ], old('message', $status->message, false), ['rows' => 2], @@ -57,7 +65,7 @@ </div> <div class="flex"> <img src="<?= $episode->image - ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> + ->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" /> <div class="flex flex-col flex-1"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2 bg-gray-100"> <div class="flex items-baseline"> @@ -71,8 +79,8 @@ </div> <div class="text-xs text-gray-600"> <time itemprop="published" datetime="<?= $episode->published_at->format( - DateTime::ATOM, - ) ?>" title="<?= $episode->published_at ?>"> + DateTime::ATOM, + ) ?>" title="<?= $episode->published_at ?>"> <?= lang('Common.mediumDate', [ $episode->published_at, ]) ?> @@ -91,24 +99,24 @@ </div> <footer class="flex justify-around px-6 py-3"> <span class="inline-flex items-center"><?= icon( - 'chat', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'chat', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> <span class="inline-flex items-center"><?= icon( - 'repeat', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'repeat', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> <span class="inline-flex items-center"><?= icon( - 'heart', - 'text-xl mr-1 text-gray-400', - ) . '0' ?></span> + 'heart', + 'text-xl mr-1 text-gray-400', + ) . '0' ?></span> </footer> </div> <?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?> <legend class="text-lg font-semibold"><?= lang( - 'Episode.publish_form.publication_date', -) ?></legend> + 'Episode.publish_form.publication_date', + ) ?></legend> <label for="now" class="inline-flex items-center"> <?= form_radio( [ @@ -120,10 +128,10 @@ old('publication_method') && old('publish') === 'now', ) ?> <span class="ml-2"><?= lang( - 'Episode.publish_form.publication_method.now', - ) ?></span> + 'Episode.publish_form.publication_method.now', + ) ?></span> </label> -<div class="inline-flex flex-wrap items-center mb-4 radio-toggler"> +<div class="inline-flex flex-wrap items-center radio-toggler"> <?= form_radio( [ 'id' => 'schedule', @@ -136,8 +144,8 @@ : true, ) ?> <label for="schedule" class="ml-2"><?= lang( - 'Episode.publish_form.publication_method.schedule', - ) ?></label> + 'Episode.publish_form.publication_method.schedule', + ) ?></label> <div class="w-full mt-2 radio-toggler-element"> <?= form_label( lang('Episode.publish_form.scheduled_publication_date'), @@ -145,7 +153,7 @@ [], lang('Episode.publish_form.scheduled_publication_date_hint'), ) ?> - <div class="flex mb-4" data-picker="datetime"> + <div class="flex" data-picker="datetime"> <?= form_input([ 'id' => 'scheduled_publication_date', 'name' => 'scheduled_publication_date', @@ -157,30 +165,46 @@ 'data-input' => '', ]) ?> <button class="p-3 border border-l-0 border-gray-500 bg-pine-100 focus:outline-none rounded-r-md hover:bg-pine-200 focus:ring" type="button" aria-label="<?= lang( - 'Episode.publish_form.scheduled_publication_date_clear', - ) ?>" title="<?= lang( - 'Episode.publish_form.scheduled_publication_date_clear', -) ?>" data-clear=""><?= icon('close') ?></button> + 'Episode.publish_form.scheduled_publication_date_clear', + ) ?>" title="<?= lang( + 'Episode.publish_form.scheduled_publication_date_clear', + ) ?>" data-clear=""><?= icon('close') ?></button> </div> </div> </div> <?= form_fieldset_close() ?> -<div class="self-end"> +<div id="publish-warning" class="inline-flex flex-col hidden p-4 text-black bg-yellow-300 border-2 border-yellow-900 rounded-md" role="alert"> + <p class="flex items-baseline font-semibold"> + <?= icon('alert', 'mr-2 text-lg flex-shrink-0') . lang( + 'Episode.publish_form.message_warning', + ) ?></p> + <p> + <?= lang( + 'Episode.publish_form.message_warning_hint', + ) ?> + </p> +</div> + +<div class="flex items-center justify-between w-full mt-4"> <?= anchor( - route_to('episode-view', $podcast->id, $episode->id), - lang('Common.cancel'), - ['class' => 'font-semibold mr-4'], + route_to('episode-publish-cancel', $podcast->id, $episode->id), + lang('Episode.publish_form.cancel_publication'), + ['class' => 'py-2 px-3 rounded-full bg-red-100 text-red-900 font-semibold mr-4'], ) ?> <?= button( lang('Episode.publish_form.submit_edit'), '', ['variant' => 'primary'], - ['type' => 'submit'], + [ + 'type' => 'submit', + 'data-btn-text-warning' => lang('Episode.publish_form.message_warning_submit'), + 'data-btn-text' => lang('Episode.publish_form.submit_edit') + ], ) ?> </div> <?= form_close() ?> -<?= $this->endSection() ?> +<?= $this->endSection() ?> \ No newline at end of file -- GitLab