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
This commit is contained in:
Yassine Doghri 2021-06-22 17:59:33 +00:00
parent 8f3e9d90c1
commit 33d01b8d4f
No known key found for this signature in database
GPG Key ID: 3E7F89498B960C9F
12 changed files with 300 additions and 140 deletions

View File

@ -310,6 +310,15 @@ $routes->group(
'permission:podcast-manage_publications', 'permission:podcast-manage_publications',
], ],
); );
$routes->get(
'publish-cancel',
'EpisodeController::publishCancel/$1/$2',
[
'as' => 'episode-publish-cancel',
'filter' =>
'permission:podcast-manage_publications',
],
);
$routes->get( $routes->get(
'unpublish', 'unpublish',
'EpisodeController::unpublish/$1/$2', 'EpisodeController::unpublish/$1/$2',

View File

@ -388,7 +388,7 @@ class EpisodeController extends BaseController
return redirect()->back(); return redirect()->back();
} }
public function publish(): string public function publish(): string | RedirectResponse
{ {
if ($this->episode->publication_status === 'not_published') { if ($this->episode->publication_status === 'not_published') {
helper(['form']); helper(['form']);
@ -405,7 +405,10 @@ class EpisodeController extends BaseController
return view('admin/episode/publish', $data); 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 public function attemptPublish(): RedirectResponse
@ -478,7 +481,7 @@ class EpisodeController extends BaseController
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); 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') { if ($this->episode->publication_status === 'scheduled') {
helper(['form']); helper(['form']);
@ -500,7 +503,11 @@ class EpisodeController extends BaseController
]); ]);
return view('admin/episode/publish_edit', $data); 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 public function attemptPublishEdit(): RedirectResponse
@ -572,7 +579,44 @@ class EpisodeController extends BaseController
return redirect()->route('episode-view', [$this->podcast->id, $this->episode->id]); 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') { if ($this->episode->publication_status === 'published') {
helper(['form']); helper(['form']);
@ -589,7 +633,10 @@ class EpisodeController extends BaseController
return view('admin/episode/unpublish', $data); 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 public function attemptUnpublish(): RedirectResponse

View File

@ -86,11 +86,11 @@ if (! function_exists('button')) {
} }
if ($options['iconLeft']) { if ($options['iconLeft']) {
$label = icon($options['iconLeft'], 'mr-2') . $label; $label = icon((string) $options['iconLeft'], 'mr-2') . $label;
} }
if ($options['iconRight']) { if ($options['iconRight']) {
$label .= icon($options['iconRight'], 'ml-2'); $label .= icon((string) $options['iconRight'], 'ml-2');
} }
if ($uri !== '') { if ($uri !== '') {

View File

@ -27,8 +27,8 @@ return [
other {# total shares} other {# total shares}
}', }',
'total_statuses' => '{numberOfTotalStatuses, plural, 'total_statuses' => '{numberOfTotalStatuses, plural,
one {# note} one {# total post}
other {# total notes} other {# total posts}
}', }',
'all_podcast_episodes' => 'All podcast episodes', 'all_podcast_episodes' => 'All podcast episodes',
'back_to_podcast' => 'Go back to podcast', 'back_to_podcast' => 'Go back to podcast',
@ -36,6 +36,10 @@ return [
'publish' => 'Publish', 'publish' => 'Publish',
'publish_edit' => 'Edit publication', 'publish_edit' => 'Edit publication',
'unpublish' => 'Unpublish', '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', 'delete' => 'Delete',
'go_to_page' => 'Go to page', 'go_to_page' => 'Go to page',
'create' => 'Add an episode', 'create' => 'Add an episode',
@ -112,9 +116,10 @@ return [
'submit_edit' => 'Save episode', 'submit_edit' => 'Save episode',
], ],
'publish_form' => [ 'publish_form' => [
'status' => 'Your note', 'back_to_episode_dashboard' => 'Back to episode dashboard',
'status' => 'Your announcement post',
'status_hint' => '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_date' => 'Publication date',
'publication_method' => [ 'publication_method' => [
'now' => 'Now', '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', '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' => 'Publish',
'submit_edit' => 'Edit publication', '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' => [ 'unpublish_form' => [
'disclaimer' => 'disclaimer' =>

View File

@ -224,8 +224,8 @@ return [
other {<span class="font-semibold">#</span> followers} other {<span class="font-semibold">#</span> followers}
}', }',
'statuses' => '{numberOfStatuses, plural, 'statuses' => '{numberOfStatuses, plural,
one {<span class="font-semibold">#</span> note} one {<span class="font-semibold">#</span> post}
other {<span class="font-semibold">#</span> notes} other {<span class="font-semibold">#</span> posts}
}', }',
'activity' => 'Activity', 'activity' => 'Activity',
'episodes' => 'Episodes', 'episodes' => 'Episodes',

View File

@ -27,8 +27,8 @@ return [
other {# partages en tout} other {# partages en tout}
}', }',
'total_statuses' => '{numberOfTotalStatuses, plural, 'total_statuses' => '{numberOfTotalStatuses, plural,
one {# note} one {# message}
other {# notes} other {# messages}
}', }',
'all_podcast_episodes' => 'Tous les épisodes du podcast', 'all_podcast_episodes' => 'Tous les épisodes du podcast',
'back_to_podcast' => 'Revenir au podcast', 'back_to_podcast' => 'Revenir au podcast',
@ -36,6 +36,10 @@ return [
'publish' => 'Publier', 'publish' => 'Publier',
'publish_edit' => 'Modifier la publication', 'publish_edit' => 'Modifier la publication',
'unpublish' => 'Dépublier', '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 nest pas publié.',
'delete' => 'Supprimer', 'delete' => 'Supprimer',
'go_to_page' => 'Voir', 'go_to_page' => 'Voir',
'create' => 'Ajouter un épisode', 'create' => 'Ajouter un épisode',
@ -115,9 +119,10 @@ return [
'submit_edit' => 'Enregistrer lépisode', 'submit_edit' => 'Enregistrer lépisode',
], ],
'publish_form' => [ 'publish_form' => [
'status' => 'Votre note', 'back_to_episode_dashboard' => 'Retour au tableau de bord de lépisode',
'status' => 'Votre message de publication',
'status_hint' => '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 daccueil de votre podcast.',
'publication_date' => 'Date de publication', 'publication_date' => 'Date de publication',
'publication_date_clear' => 'Effacer la date de publication', 'publication_date_clear' => 'Effacer la date de publication',
'publication_date_hint' => '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', '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' => 'Publier',
'submit_edit' => 'Modifier la publication', 'submit_edit' => 'Modifier la publication',
'cancel_publication' => 'Annuler la publication',
'message_warning' => 'Vous navez pas saisi de message pour lannonce de votre épisode !',
'message_warning_hint' => 'Ajouter un message augmente lengagement social, menant à une meilleure visibilité pour votre épisode.',
'message_warning_submit' => 'Publish quand même',
], ],
'soundbites' => 'Extraits sonores', 'soundbites' => 'Extraits sonores',
'soundbites_form' => [ 'soundbites_form' => [

View File

@ -226,8 +226,8 @@ return [
other {<span class="font-semibold">#</span> abonné·e·s} other {<span class="font-semibold">#</span> abonné·e·s}
}', }',
'notes' => '{numberOfStatuses, plural, 'notes' => '{numberOfStatuses, plural,
one {<span class="font-semibold">#</span> note} one {<span class="font-semibold">#</span> message}
other {<span class="font-semibold">#</span> notes} other {<span class="font-semibold">#</span> messages}
}', }',
'activity' => 'Activité', 'activity' => 'Activité',
'episodes' => 'Épisodes', 'episodes' => 'Épisodes',

View File

@ -81,7 +81,7 @@ class StatusModel extends UuidModel
*/ */
protected $validationRules = [ protected $validationRules = [
'actor_id' => 'required', 'actor_id' => 'required',
'message_html' => 'required_without[reblog_of_id]|max_length[500]', 'message_html' => 'max_length[500]',
]; ];
/** /**

View File

@ -4,6 +4,7 @@ import DateTimePicker from "./modules/DateTimePicker";
import Dropdown from "./modules/Dropdown"; import Dropdown from "./modules/Dropdown";
import MarkdownEditor from "./modules/MarkdownEditor"; import MarkdownEditor from "./modules/MarkdownEditor";
import MultiSelect from "./modules/MultiSelect"; import MultiSelect from "./modules/MultiSelect";
import PublishMessageWarning from "./modules/PublishMessageWarning";
import SidebarToggler from "./modules/SidebarToggler"; import SidebarToggler from "./modules/SidebarToggler";
import Slugify from "./modules/Slugify"; import Slugify from "./modules/Slugify";
import Soundbites from "./modules/Soundbites"; import Soundbites from "./modules/Soundbites";
@ -23,3 +24,4 @@ Time();
Soundbites(); Soundbites();
Clipboard(); Clipboard();
ThemePicker(); ThemePicker();
PublishMessageWarning();

View File

@ -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;

View File

@ -11,9 +11,16 @@
<?= $this->section('content') ?> <?= $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), [ <?= form_open(route_to('episode-publish', $podcast->id, $episode->id), [
'method' => 'post', '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() ?> <?= csrf_field() ?>
<?= form_hidden('client_timezone', 'UTC') ?> <?= form_hidden('client_timezone', 'UTC') ?>
@ -21,11 +28,12 @@
<label for="message" class="text-lg font-semibold"><?= lang( <label for="message" class="text-lg font-semibold"><?= lang(
'Episode.publish_form.status', 'Episode.publish_form.status',
) . hint_tooltip(lang('Episode.publish_form.status_hint'), 'ml-1') ?></label> ) ?></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="mb-8 overflow-hidden bg-white shadow-md rounded-xl">
<div class="flex px-4 py-3"> <div class="flex px-4 py-3">
<img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast <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"> <p class="flex items-baseline min-w-0">
<span class="mr-2 font-semibold truncate"><?= $podcast->actor <span class="mr-2 font-semibold truncate"><?= $podcast->actor
->display_name ?></span> ->display_name ?></span>
@ -39,17 +47,15 @@
'id' => 'message', 'id' => 'message',
'name' => 'message', 'name' => 'message',
'class' => 'form-textarea min-w-0 w-full', 'class' => 'form-textarea min-w-0 w-full',
'required' => 'required',
'placeholder' => 'Write your message...', 'placeholder' => 'Write your message...',
'autofocus' => ''
], ],
old('message', '', false), old('message', '', false),
['rows' => 2], ['rows' => 2],
) ?> ) ?>
</div> </div>
<div class="flex"> <div class="flex">
<img <img src="<?= $episode->image->thumbnail_url ?>" alt="<?= $episode->title ?>" class="w-24 h-24" />
src="<?= $episode->image->thumbnail_url ?>"
alt="<?= $episode->title ?>" class="w-24 h-24"/>
<div class="flex flex-col flex-1"> <div class="flex flex-col flex-1">
<a href="<?= $episode->link ?>" class="flex-1 px-4 py-2 bg-gray-100"> <a href="<?= $episode->link ?>" class="flex-1 px-4 py-2 bg-gray-100">
<div class="flex items-baseline"> <div class="flex items-baseline">
@ -68,9 +74,7 @@
</div> </div>
</a> </a>
<audio controls preload="none" class="w-full mt-auto"> <audio controls preload="none" class="w-full mt-auto">
<source <source src="<?= $episode->audio_file_url ?>" type="<?= $episode->audio_file_mimetype ?>">
src="<?= $episode->audio_file_url ?>"
type="<?= $episode->audio_file_mimetype ?>">
Your browser does not support the audio tag. Your browser does not support the audio tag.
</audio> </audio>
</div> </div>
@ -92,10 +96,10 @@
</div> </div>
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?> <?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
<legend class="text-lg font-semibold"><?= lang( <legend class="text-lg font-semibold"><?= lang(
'Episode.publish_form.publication_date', 'Episode.publish_form.publication_date',
) ?></legend> ) ?></legend>
<label for="now" class="inline-flex items-center"> <label for="now" class="inline-flex items-center">
<?= form_radio( <?= form_radio(
[ [
'id' => 'now', 'id' => 'now',
@ -108,8 +112,8 @@
<span class="ml-2"><?= lang( <span class="ml-2"><?= lang(
'Episode.publish_form.publication_method.now', 'Episode.publish_form.publication_method.now',
) ?></span> ) ?></span>
</label> </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( <?= form_radio(
[ [
'id' => 'schedule', 'id' => 'schedule',
@ -130,7 +134,7 @@
[], [],
lang('Episode.publish_form.scheduled_publication_date_hint'), lang('Episode.publish_form.scheduled_publication_date_hint'),
) ?> ) ?>
<div class="flex mb-4" data-picker="datetime"> <div class="flex" data-picker="datetime">
<?= form_input([ <?= form_input([
'id' => 'scheduled_publication_date', 'id' => 'scheduled_publication_date',
'name' => 'scheduled_publication_date', 'name' => 'scheduled_publication_date',
@ -138,32 +142,37 @@
'value' => old('scheduled_publication_date', ''), 'value' => old('scheduled_publication_date', ''),
'data-input' => '', 'data-input' => '',
]) ?> ]) ?>
<button <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(
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', 'Episode.publish_form.scheduled_publication_date_clear',
) ?>" ) ?>" data-clear=""><?= icon('close') ?></button>
data-clear=""><?= icon('close') ?></button>
</div>
</div> </div>
</div> </div>
</div>
<?= form_fieldset_close() ?> <?= 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">
<?= anchor( <p class="flex items-baseline font-semibold">
route_to('episode-view', $podcast->id, $episode->id), <?= icon('alert', 'mr-2 text-lg flex-shrink-0') . lang(
lang('Common.cancel'), 'Episode.publish_form.message_warning',
['class' => 'font-semibold mr-4'], ) ?></p>
) ?> <p>
<?= lang(
'Episode.publish_form.message_warning_hint',
) ?>
</p>
</div>
<?= button( <?= button(
lang('Episode.publish_form.submit'), lang('Episode.publish_form.submit'),
'', '',
['variant' => 'primary'], ['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() ?> <?= form_close() ?>

View File

@ -1,19 +1,26 @@
<?= $this->extend('admin/_layout') ?> <?= $this->extend('admin/_layout') ?>
<?= $this->section('title') ?> <?= $this->section('title') ?>
<?= lang('Episode.publish') ?> <?= lang('Episode.publish_edit') ?>
<?= $this->endSection() ?> <?= $this->endSection() ?>
<?= $this->section('pageTitle') ?> <?= $this->section('pageTitle') ?>
<?= lang('Episode.publish') ?> <?= lang('Episode.publish_edit') ?>
<?= $this->endSection() ?> <?= $this->endSection() ?>
<?= $this->section('content') ?> <?= $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), [ <?= form_open(route_to('episode-publish_edit', $podcast->id, $episode->id), [
'method' => 'post', '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() ?> <?= csrf_field() ?>
<?= form_hidden('client_timezone', 'UTC') ?> <?= form_hidden('client_timezone', 'UTC') ?>
@ -22,11 +29,12 @@
<label for="message" class="text-lg font-semibold"><?= lang( <label for="message" class="text-lg font-semibold"><?= lang(
'Episode.publish_form.status', 'Episode.publish_form.status',
) . hint_tooltip(lang('Episode.publish_form.status_hint'), 'ml-1') ?></label> ) ?></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="mb-8 overflow-hidden bg-white shadow-md rounded-xl">
<div class="flex px-4 py-3"> <div class="flex px-4 py-3">
<img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor <img src="<?= $podcast->actor->avatar_image_url ?>" alt="<?= $podcast->actor
->display_name ?>" class="w-12 h-12 mr-4 rounded-full"/> ->display_name ?>" class="w-12 h-12 mr-4 rounded-full" />
<div class="flex flex-col min-w-0"> <div class="flex flex-col min-w-0">
<p class="flex items-baseline min-w-0"> <p class="flex items-baseline min-w-0">
<span class="mr-2 font-semibold truncate"><?= $podcast->actor <span class="mr-2 font-semibold truncate"><?= $podcast->actor
@ -39,7 +47,7 @@
) ?>" title="<?= $status->published_at ?>"><?= lang( ) ?>" title="<?= $status->published_at ?>"><?= lang(
'Common.mediumDate', 'Common.mediumDate',
[$status->published_at], [$status->published_at],
) ?></time> ) ?></time>
</div> </div>
</div> </div>
<div class="px-4 mb-2"> <div class="px-4 mb-2">
@ -48,8 +56,8 @@
'id' => 'message', 'id' => 'message',
'name' => 'message', 'name' => 'message',
'class' => 'form-textarea', 'class' => 'form-textarea',
'required' => 'required',
'placeholder' => 'Write your message...', 'placeholder' => 'Write your message...',
'autofocus' => ''
], ],
old('message', $status->message, false), old('message', $status->message, false),
['rows' => 2], ['rows' => 2],
@ -108,7 +116,7 @@
<?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?> <?= form_fieldset('', ['class' => 'flex flex-col mb-4']) ?>
<legend class="text-lg font-semibold"><?= lang( <legend class="text-lg font-semibold"><?= lang(
'Episode.publish_form.publication_date', 'Episode.publish_form.publication_date',
) ?></legend> ) ?></legend>
<label for="now" class="inline-flex items-center"> <label for="now" class="inline-flex items-center">
<?= form_radio( <?= form_radio(
[ [
@ -123,7 +131,7 @@
'Episode.publish_form.publication_method.now', 'Episode.publish_form.publication_method.now',
) ?></span> ) ?></span>
</label> </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( <?= form_radio(
[ [
'id' => 'schedule', 'id' => 'schedule',
@ -145,7 +153,7 @@
[], [],
lang('Episode.publish_form.scheduled_publication_date_hint'), lang('Episode.publish_form.scheduled_publication_date_hint'),
) ?> ) ?>
<div class="flex mb-4" data-picker="datetime"> <div class="flex" data-picker="datetime">
<?= form_input([ <?= form_input([
'id' => 'scheduled_publication_date', 'id' => 'scheduled_publication_date',
'name' => 'scheduled_publication_date', 'name' => 'scheduled_publication_date',
@ -160,24 +168,40 @@
'Episode.publish_form.scheduled_publication_date_clear', 'Episode.publish_form.scheduled_publication_date_clear',
) ?>" title="<?= lang( ) ?>" title="<?= lang(
'Episode.publish_form.scheduled_publication_date_clear', 'Episode.publish_form.scheduled_publication_date_clear',
) ?>" data-clear=""><?= icon('close') ?></button> ) ?>" data-clear=""><?= icon('close') ?></button>
</div> </div>
</div> </div>
</div> </div>
<?= form_fieldset_close() ?> <?= 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( <?= anchor(
route_to('episode-view', $podcast->id, $episode->id), route_to('episode-publish-cancel', $podcast->id, $episode->id),
lang('Common.cancel'), lang('Episode.publish_form.cancel_publication'),
['class' => 'font-semibold mr-4'], ['class' => 'py-2 px-3 rounded-full bg-red-100 text-red-900 font-semibold mr-4'],
) ?> ) ?>
<?= button( <?= button(
lang('Episode.publish_form.submit_edit'), lang('Episode.publish_form.submit_edit'),
'', '',
['variant' => 'primary'], ['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> </div>