fix(episodes): add publication status + set publication date to null when none has been set

- replace $is_published attribute by $publication_status to better handle episode's publication
state
- update publication date datepicker to include a clear button

fixes #70
This commit is contained in:
Yassine Doghri 2020-12-09 17:52:30 +00:00
parent 5dc0f19656
commit d882981b3a
14 changed files with 141 additions and 99 deletions

View File

@ -117,6 +117,7 @@ class Episode extends BaseController
->with('errors', $this->validator->getErrors());
}
$publicationDate = $this->request->getPost('publication_date');
$newEpisode = new \App\Entities\Episode([
'podcast_id' => $this->podcast->id,
'title' => $this->request->getPost('title'),
@ -141,11 +142,13 @@ class Episode extends BaseController
'is_blocked' => $this->request->getPost('block') == 'yes',
'created_by' => user(),
'updated_by' => user(),
'published_at' => Time::createFromFormat(
'Y-m-d H:i',
$this->request->getPost('publication_date'),
$this->request->getPost('client_timezone')
)->setTimezone('UTC'),
'published_at' => $publicationDate
? Time::createFromFormat(
'Y-m-d H:i',
$publicationDate,
$this->request->getPost('client_timezone')
)->setTimezone('UTC')
: null,
]);
$episodeModel = new EpisodeModel();
@ -231,11 +234,16 @@ class Episode extends BaseController
: null;
$this->episode->type = $this->request->getPost('type');
$this->episode->is_blocked = $this->request->getPost('block') == 'yes';
$this->episode->published_at = Time::createFromFormat(
'Y-m-d H:i',
$this->request->getPost('publication_date'),
$this->request->getPost('client_timezone')
)->setTimezone('UTC');
$publicationDate = $this->request->getPost('publication_date');
$this->episode->published_at = $publicationDate
? Time::createFromFormat(
'Y-m-d H:i',
$publicationDate,
$this->request->getPost('client_timezone')
)->setTimezone('UTC')
: null;
$this->episode->updated_by = user();
$enclosure = $this->request->getFile('enclosure');

View File

@ -89,9 +89,9 @@ class Episode extends Entity
protected $description;
/**
* @var boolean
* @var string
*/
protected $is_published;
protected $publication_status;
protected $dates = [
'published_at',
@ -462,16 +462,21 @@ class Episode extends Entity
return $this;
}
public function getIsPublished()
public function getPublicationStatus()
{
if ($this->is_published) {
return $this->is_published;
if ($this->publication_status) {
return $this->publication_status;
}
if (!$this->published_at) {
return 'not_published';
}
helper('date');
if ($this->published_at->isBefore(Time::now())) {
return 'published';
}
$this->is_published = $this->published_at->isBefore(Time::now());
return $this->is_published;
return 'scheduled';
}
}

View File

@ -271,27 +271,30 @@ if (!function_exists('publication_pill')) {
*/
function publication_pill(
$publicationDate,
$isPublished,
$publicationStatus,
$customClass = ''
): string {
$class = $isPublished
? 'text-green-500 border-green-500'
: 'text-orange-600 border-orange-600';
$class =
$publicationStatus === 'published'
? 'text-green-500 border-green-500'
: 'text-orange-600 border-orange-600';
$label = lang(
$isPublished ? 'Episode.published' : 'Episode.scheduled',
[
'<time
pubdate
datetime="' .
$transParam = [];
if ($publicationDate) {
$transParam = [
'<time pubdate datetime="' .
$publicationDate->format(DateTime::ATOM) .
'"
title="' .
'" title="' .
$publicationDate .
'">' .
lang('Common.mediumDate', [$publicationDate]) .
'</time>',
]
];
}
$label = lang(
'Episode.publication_status.' . $publicationStatus,
$transParam
);
return '<span class="px-1 border ' .

View File

@ -23,8 +23,11 @@ return [
'delete' => 'Delete',
'go_to_page' => 'Go to page',
'create' => 'Add an episode',
'published' => 'Published on {0}',
'scheduled' => 'Scheduled for {0}',
'publication_status' => [
'published' => 'Published on {0}',
'scheduled' => 'Scheduled for {0}',
'not_published' => 'Not published',
],
'form' => [
'enclosure' => 'Audio file',
'enclosure_hint' => 'Choose an .mp3 or .m4a audio file.',
@ -58,6 +61,7 @@ return [
'publication_section_title' => 'Publication info',
'publication_section_subtitle' => '',
'publication_date' => 'Publication date',
'publication_date_clear' => 'Clear publication date',
'publication_date_hint' =>
'You can schedule the episode release by setting a future publication date. This field must be formatted as YYYY-MM-DD HH:mm',
'parental_advisory' => [

View File

@ -23,8 +23,11 @@ return [
'delete' => 'Supprimer',
'go_to_page' => 'Voir',
'create' => 'Ajouter un épisode',
'published' => 'Publié le {0}',
'scheduled' => 'Planifié pour le {0}',
'publication_status' => [
'published' => 'Publié le {0}',
'scheduled' => 'Planifié pour le {0}',
'not_published' => 'Non publié',
],
'form' => [
'enclosure' => 'Fichier audio',
'enclosure_hint' => 'Sélectionnez un fichier audio .mp3 ou .m4a.',
@ -58,6 +61,7 @@ return [
'publication_section_title' => 'Information de publication',
'publication_section_subtitle' => '',
'publication_date' => 'Date de publication',
'publication_date_clear' => 'Effacer la date de publication',
'publication_date_hint' =>
'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',
'parental_advisory' => [

View File

@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g>
<path fill="none" d="M0 0h24v24H0z"/>
<path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 280 B

View File

@ -11,30 +11,36 @@ const isBrowserLocale24h = () =>
.match(/AM/);
const DateTimePicker = (): void => {
const dateTimeContainers: NodeListOf<HTMLInputElement> = document.querySelectorAll(
"input[data-picker='datetime']"
const dateTimeContainers: NodeListOf<HTMLDivElement> = document.querySelectorAll(
"div[data-picker='datetime']"
);
for (let i = 0; i < dateTimeContainers.length; i++) {
const dateTimeContainer = dateTimeContainers[i];
const flatpickrInstance = flatpickr(dateTimeContainer, {
enableTime: true,
time_24hr: isBrowserLocale24h(),
});
// convert container UTC date value to user timezone
const dateTime = new Date(dateTimeContainer.value);
const dateUTC = Date.UTC(
dateTime.getFullYear(),
dateTime.getMonth(),
dateTime.getDate(),
dateTime.getHours(),
dateTime.getMinutes()
const dateTimeInput: HTMLInputElement | null = dateTimeContainer.querySelector(
"input[data-input]"
);
// set converted date as field value
flatpickrInstance.setDate(new Date(dateUTC));
if (dateTimeInput) {
const flatpickrInstance = flatpickr(dateTimeContainer, {
enableTime: true,
time_24hr: isBrowserLocale24h(),
wrap: true,
});
// convert container UTC date value to user timezone
const dateTime = new Date(dateTimeInput.value);
const dateUTC = Date.UTC(
dateTime.getFullYear(),
dateTime.getMonth(),
dateTime.getDate(),
dateTime.getHours(),
dateTime.getMinutes()
);
// set converted date as field value
flatpickrInstance.setDate(new Date(dateUTC));
}
}
};

View File

@ -3,8 +3,6 @@ const Time = (): void => {
"time"
);
console.log(timeElements);
for (let i = 0; i < timeElements.length; i++) {
const timeElement = timeElements[i];

View File

@ -199,13 +199,20 @@
[],
lang('Episode.form.publication_date_hint')
) ?>
<?= form_input([
'id' => 'publication_date',
'name' => 'publication_date',
'class' => 'form-input mb-4',
'value' => old('publication_date', date('Y-m-d H:i')),
'data-picker' => 'datetime',
]) ?>
<div class="flex mb-4" data-picker="datetime">
<?= form_input([
'id' => 'publication_date',
'name' => 'publication_date',
'class' => 'form-input rounded-r-none flex-1',
'value' => old('publication_date', date('Y-m-d H:i')),
'data-input' => '',
]) ?>
<button
class="p-3 bg-green-100 border border-l-0 focus:outline-none rounded-r-md hover:bg-green-200 focus:shadow-outline"
type="button"
title="<?= lang('Episode.form.publication_date_clear') ?>"
data-clear=""><?= icon('close') ?></button>
</div>
<?= form_fieldset('', ['class' => 'flex mb-6 gap-1']) ?>
<legend>

View File

@ -202,18 +202,25 @@
[],
lang('Episode.form.publication_date_hint')
) ?>
<?= form_input([
'id' => 'publication_date',
'name' => 'publication_date',
'class' => 'form-input mb-4',
'value' => old(
'publication_date',
$episode->published_at
? $episode->published_at->format('Y-m-d H:i')
: ''
),
'data-picker' => 'datetime',
]) ?>
<div class="flex mb-4" data-picker="datetime">
<?= form_input([
'id' => 'publication_date',
'name' => 'publication_date',
'class' => 'form-input rounded-r-none flex-1',
'value' => old(
'publication_date',
$episode->published_at
? $episode->published_at->format('Y-m-d H:i')
: ''
),
'data-input' => '',
]) ?>
<button
class="p-3 bg-green-100 border border-l-0 focus:outline-none rounded-r-md hover:bg-green-200 focus:shadow-outline"
type="button"
title="<?= lang('Episode.form.publication_date_clear') ?>"
data-clear=""><?= icon('close') ?></button>
</div>
<?= form_fieldset('', ['class' => 'mb-6']) ?>
<legend>

View File

@ -65,9 +65,7 @@
'soundbites-edit',
$podcast->id,
$episode->id
) ?>"><?= lang(
'Episode.soundbites'
) ?></a>
) ?>"><?= lang('Episode.soundbites') ?></a>
<a class="px-4 py-1 hover:bg-gray-100" href="<?= route_to(
'episode',
$podcast->name,
@ -84,7 +82,7 @@
<div class="mb-2 text-xs">
<?= publication_pill(
$episode->published_at,
$episode->is_published
$episode->publication_status
) ?>
<span class="mx-1"></span>
<time datetime="PT<?= $episode->enclosure_duration ?>S">

View File

@ -8,7 +8,7 @@
<?= $episode->title .
publication_pill(
$episode->published_at,
$episode->is_published,
$episode->publication_status,
'text-sm ml-2 align-middle'
) ?>
<?= $this->endSection() ?>

View File

@ -33,17 +33,19 @@
'font-bold text-gray-600',
true
) ?>
<span class="mx-1"></span>
<time
pubdate
datetime="<?= $episode->published_at->format(
DateTime::ATOM
) ?>"
title="<?= $episode->published_at ?>">
<?= lang('Common.mediumDate', [
$episode->published_at,
]) ?>
</time>
<?php if ($episode->published_at): ?>
<span class="mx-1"></span>
<time
pubdate
datetime="<?= $episode->published_at->format(
DateTime::ATOM
) ?>"
title="<?= $episode->published_at ?>">
<?= lang('Common.mediumDate', [
$episode->published_at,
]) ?>
</time>
<?php endif; ?>
</div>
</div>
<div class="relative" data-toggle="dropdown">

12
package-lock.json generated
View File

@ -3944,9 +3944,9 @@
}
},
"caniuse-lite": {
"version": "1.0.30001077",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001077.tgz",
"integrity": "sha512-AEzsGvjBJL0lby/87W96PyEvwN0GsYvk5LHsglLg9tW37K4BqvAvoSCdWIE13OZQ8afupqZ73+oL/1LkedN8hA==",
"version": "1.0.30001165",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001165.tgz",
"integrity": "sha512-8cEsSMwXfx7lWSUMA2s08z9dIgsnR5NAqjXP23stdsU3AUWkCr/rr4s4OFtHXn5XXr6+7kam3QFVoYyXNPdJPA==",
"dev": true
},
"cardinal": {
@ -16679,12 +16679,6 @@
"fill-range": "^7.0.1"
}
},
"caniuse-lite": {
"version": "1.0.30001142",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001142.tgz",
"integrity": "sha512-pDPpn9ankEpBFZXyCv2I4lh1v/ju+bqb78QfKf+w9XgDAFWBwSYPswXqprRdrgQWK0wQnpIbfwRjNHO1HWqvoQ==",
"dev": true
},
"chalk": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",