2020-10-02 15:38:16 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
2021-06-08 09:52:11 +00:00
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
2020-10-02 15:38:16 +00:00
|
|
|
|
/**
|
2022-02-19 16:06:11 +00:00
|
|
|
|
* @copyright 2020 Ad Aures
|
2020-10-02 15:38:16 +00:00
|
|
|
|
* @license https://www.gnu.org/licenses/agpl-3.0.en.html AGPL3
|
|
|
|
|
* @link https://castopod.org/
|
|
|
|
|
*/
|
2022-01-23 19:58:30 +00:00
|
|
|
|
|
|
|
|
|
use App\Entities\Category;
|
2023-08-28 13:53:04 +00:00
|
|
|
|
use App\Entities\Episode;
|
2021-05-12 14:00:25 +00:00
|
|
|
|
use App\Entities\Location;
|
2021-05-06 14:00:48 +00:00
|
|
|
|
use CodeIgniter\I18n\Time;
|
2021-05-19 16:35:13 +00:00
|
|
|
|
use CodeIgniter\View\Table;
|
2021-05-06 14:00:48 +00:00
|
|
|
|
|
2020-10-02 15:38:16 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('hint_tooltip')) {
|
2020-10-02 15:38:16 +00:00
|
|
|
|
/**
|
|
|
|
|
* Hint component
|
|
|
|
|
*
|
|
|
|
|
* Used to produce tooltip with a question mark icon for hint texts
|
|
|
|
|
*
|
|
|
|
|
* @param string $hintText The hint text
|
|
|
|
|
*/
|
|
|
|
|
function hint_tooltip(string $hintText = '', string $class = ''): string
|
|
|
|
|
{
|
|
|
|
|
$tooltip =
|
2021-11-05 14:36:34 +00:00
|
|
|
|
'<span data-tooltip="bottom" tabindex="0" title="' .
|
2024-02-19 11:08:00 +00:00
|
|
|
|
esc($hintText) .
|
2021-12-24 17:55:56 +00:00
|
|
|
|
'" class="inline-block align-middle opacity-75 focus:ring-accent';
|
2020-10-02 15:38:16 +00:00
|
|
|
|
|
|
|
|
|
if ($class !== '') {
|
|
|
|
|
$tooltip .= ' ' . $class;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $tooltip . '">' . icon('question') . '</span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2020-10-02 15:38:16 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('data_table')) {
|
2020-10-02 15:38:16 +00:00
|
|
|
|
/**
|
|
|
|
|
* Data table component
|
|
|
|
|
*
|
|
|
|
|
* Creates a stylized table.
|
|
|
|
|
*
|
2021-05-14 17:59:35 +00:00
|
|
|
|
* @param array<array<string, mixed>> $columns array of associate arrays with `header` and `cell` keys where `cell` is a function with a row of $data as parameter
|
|
|
|
|
* @param mixed[] $data data to loop through and display in rows
|
|
|
|
|
* @param mixed ...$rest Any other argument to pass to the `cell` function
|
2020-10-02 15:38:16 +00:00
|
|
|
|
*/
|
2022-09-28 14:00:05 +00:00
|
|
|
|
function data_table(array $columns, array $data = [], string $class = '', mixed ...$rest): string
|
2020-10-02 15:38:16 +00:00
|
|
|
|
{
|
2021-05-06 14:00:48 +00:00
|
|
|
|
$table = new Table();
|
2020-10-02 15:38:16 +00:00
|
|
|
|
|
|
|
|
|
$template = [
|
2022-07-22 16:45:11 +00:00
|
|
|
|
'table_open' => '<table class="w-full whitespace-nowrap">',
|
2020-10-02 15:38:16 +00:00
|
|
|
|
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'thead_open' => '<thead class="text-xs font-semibold text-left uppercase text-skin-muted">',
|
2020-10-02 15:38:16 +00:00
|
|
|
|
|
|
|
|
|
'heading_cell_start' => '<th class="px-4 py-2">',
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'cell_start' => '<td class="px-4 py-2">',
|
|
|
|
|
'cell_alt_start' => '<td class="px-4 py-2">',
|
2020-10-02 15:38:16 +00:00
|
|
|
|
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'row_start' => '<tr class="border-t border-subtle hover:bg-base">',
|
2021-11-05 14:36:34 +00:00
|
|
|
|
'row_alt_start' => '<tr class="border-t border-subtle hover:bg-base">',
|
2020-10-02 15:38:16 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$table->setTemplate($template);
|
|
|
|
|
|
|
|
|
|
$tableHeaders = [];
|
|
|
|
|
foreach ($columns as $column) {
|
2021-05-06 14:00:48 +00:00
|
|
|
|
$tableHeaders[] = $column['header'];
|
2020-10-02 15:38:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$table->setHeading($tableHeaders);
|
|
|
|
|
|
2021-05-06 14:00:48 +00:00
|
|
|
|
if (($dataCount = count($data)) !== 0) {
|
|
|
|
|
for ($i = 0; $i < $dataCount; ++$i) {
|
2020-10-02 15:38:16 +00:00
|
|
|
|
$row = $data[$i];
|
|
|
|
|
$rowData = [];
|
|
|
|
|
foreach ($columns as $column) {
|
2021-05-06 14:00:48 +00:00
|
|
|
|
$rowData[] = $column['cell']($row, ...$rest);
|
2020-10-02 15:38:16 +00:00
|
|
|
|
}
|
2022-03-04 14:33:48 +00:00
|
|
|
|
|
2020-10-02 15:38:16 +00:00
|
|
|
|
$table->addRow($rowData);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-02 14:11:05 +00:00
|
|
|
|
$table->addRow([
|
|
|
|
|
[
|
|
|
|
|
'colspan' => count($tableHeaders),
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'class' => 'px-4 py-2 italic font-semibold text-center',
|
|
|
|
|
'data' => lang('Common.no_data'),
|
2022-01-02 14:11:05 +00:00
|
|
|
|
],
|
|
|
|
|
]);
|
2020-10-02 15:38:16 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-05 14:36:34 +00:00
|
|
|
|
return '<div class="overflow-x-auto rounded-lg bg-elevated border-3 border-subtle ' . $class . '" >' .
|
2020-10-02 15:38:16 +00:00
|
|
|
|
$table->generate() .
|
|
|
|
|
'</div>';
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2020-10-02 15:38:16 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('publication_pill')) {
|
2020-10-22 17:41:59 +00:00
|
|
|
|
/**
|
2020-10-29 17:25:15 +00:00
|
|
|
|
* Publication pill component
|
2020-10-22 17:41:59 +00:00
|
|
|
|
*
|
2020-10-29 17:25:15 +00:00
|
|
|
|
* Shows the stylized publication datetime in regards to current datetime.
|
2020-10-22 17:41:59 +00:00
|
|
|
|
*/
|
2021-05-19 16:35:13 +00:00
|
|
|
|
function publication_pill(?Time $publicationDate, string $publicationStatus, string $customClass = ''): string
|
|
|
|
|
{
|
2021-08-09 10:28:16 +00:00
|
|
|
|
$class = match ($publicationStatus) {
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'published' => 'text-pine-500 border-pine-500 bg-pine-50',
|
|
|
|
|
'scheduled' => 'text-red-600 border-red-600 bg-red-50',
|
|
|
|
|
'with_podcast' => 'text-blue-600 border-blue-600 bg-blue-50',
|
2021-10-20 14:22:58 +00:00
|
|
|
|
'not_published' => 'text-gray-600 border-gray-600 bg-gray-50',
|
2023-06-12 14:47:38 +00:00
|
|
|
|
default => 'text-gray-600 border-gray-600 bg-gray-50',
|
2021-08-09 10:28:16 +00:00
|
|
|
|
};
|
2020-12-09 17:52:30 +00:00
|
|
|
|
|
2022-07-05 16:39:20 +00:00
|
|
|
|
$title = match ($publicationStatus) {
|
|
|
|
|
'published', 'scheduled' => (string) $publicationDate,
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'with_podcast' => lang('Episode.with_podcast_hint'),
|
2022-07-05 16:39:20 +00:00
|
|
|
|
'not_published' => '',
|
2023-06-12 14:47:38 +00:00
|
|
|
|
default => '',
|
2022-07-05 16:39:20 +00:00
|
|
|
|
};
|
|
|
|
|
|
2021-08-09 10:28:16 +00:00
|
|
|
|
$label = lang('Episode.publication_status.' . $publicationStatus);
|
2020-10-22 17:41:59 +00:00
|
|
|
|
|
2022-07-05 16:39:20 +00:00
|
|
|
|
return '<span ' . ($title === '' ? '' : 'title="' . $title . '"') . ' class="flex items-center px-1 font-semibold border rounded w-max ' .
|
2020-10-22 17:41:59 +00:00
|
|
|
|
$class .
|
|
|
|
|
' ' .
|
|
|
|
|
$customClass .
|
|
|
|
|
'">' .
|
|
|
|
|
$label .
|
2023-10-12 15:52:20 +00:00
|
|
|
|
($publicationStatus === 'with_podcast' ? '<Icon glyph="error-warning" class="flex-shrink-0 ml-1 text-lg" />' : '') .
|
2020-10-22 17:41:59 +00:00
|
|
|
|
'</span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2020-10-29 17:25:15 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-08-19 14:00:14 +00:00
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('publication_button')) {
|
2021-04-02 17:20:02 +00:00
|
|
|
|
/**
|
2022-07-05 16:39:20 +00:00
|
|
|
|
* Publication button component for episodes
|
2021-04-02 17:20:02 +00:00
|
|
|
|
*
|
2021-08-13 11:07:29 +00:00
|
|
|
|
* Displays the appropriate publication button depending on the publication post.
|
2021-04-02 17:20:02 +00:00
|
|
|
|
*/
|
2021-05-19 16:35:13 +00:00
|
|
|
|
function publication_button(int $podcastId, int $episodeId, string $publicationStatus): string
|
|
|
|
|
{
|
2021-04-02 17:20:02 +00:00
|
|
|
|
switch ($publicationStatus) {
|
|
|
|
|
case 'not_published':
|
|
|
|
|
$label = lang('Episode.publish');
|
|
|
|
|
$route = route_to('episode-publish', $podcastId, $episodeId);
|
|
|
|
|
$variant = 'primary';
|
|
|
|
|
$iconLeft = 'upload-cloud';
|
|
|
|
|
break;
|
2022-07-05 16:39:20 +00:00
|
|
|
|
case 'with_podcast':
|
2021-04-02 17:20:02 +00:00
|
|
|
|
case 'scheduled':
|
|
|
|
|
$label = lang('Episode.publish_edit');
|
2021-06-08 09:52:11 +00:00
|
|
|
|
$route = route_to('episode-publish_edit', $podcastId, $episodeId);
|
2021-12-20 17:12:12 +00:00
|
|
|
|
$variant = 'warning';
|
2021-04-02 17:20:02 +00:00
|
|
|
|
$iconLeft = 'upload-cloud';
|
|
|
|
|
break;
|
|
|
|
|
case 'published':
|
|
|
|
|
$label = lang('Episode.unpublish');
|
|
|
|
|
$route = route_to('episode-unpublish', $podcastId, $episodeId);
|
|
|
|
|
$variant = 'danger';
|
|
|
|
|
$iconLeft = 'cloud-off';
|
|
|
|
|
break;
|
2021-05-12 14:00:25 +00:00
|
|
|
|
default:
|
|
|
|
|
$label = '';
|
|
|
|
|
$route = '';
|
|
|
|
|
$variant = '';
|
|
|
|
|
$iconLeft = '';
|
|
|
|
|
break;
|
2021-04-02 17:20:02 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:29:15 +00:00
|
|
|
|
return <<<HTML
|
2021-09-20 15:45:38 +00:00
|
|
|
|
<Button variant="{$variant}" uri="{$route}" iconLeft="{$iconLeft}" >{$label}</Button>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
2021-04-02 17:20:02 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2021-04-02 17:20:02 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2022-07-05 16:39:20 +00:00
|
|
|
|
if (! function_exists('publication_status_banner')) {
|
|
|
|
|
/**
|
|
|
|
|
* Publication status banner component for podcasts
|
|
|
|
|
*
|
|
|
|
|
* Displays the appropriate banner depending on the podcast's publication status.
|
|
|
|
|
*/
|
|
|
|
|
function publication_status_banner(?Time $publicationDate, int $podcastId, string $publicationStatus): string
|
|
|
|
|
{
|
|
|
|
|
switch ($publicationStatus) {
|
|
|
|
|
case 'not_published':
|
|
|
|
|
$bannerDisclaimer = lang('Podcast.publication_status_banner.draft_mode');
|
|
|
|
|
$bannerText = lang('Podcast.publication_status_banner.not_published');
|
|
|
|
|
$linkRoute = route_to('podcast-publish', $podcastId);
|
|
|
|
|
$linkLabel = lang('Podcast.publish');
|
|
|
|
|
break;
|
|
|
|
|
case 'scheduled':
|
|
|
|
|
$bannerDisclaimer = lang('Podcast.publication_status_banner.draft_mode');
|
|
|
|
|
$bannerText = lang('Podcast.publication_status_banner.scheduled', [
|
2022-07-06 15:29:15 +00:00
|
|
|
|
'publication_date' => local_datetime($publicationDate),
|
2023-08-26 13:03:01 +00:00
|
|
|
|
]);
|
2022-07-05 16:39:20 +00:00
|
|
|
|
$linkRoute = route_to('podcast-publish_edit', $podcastId);
|
|
|
|
|
$linkLabel = lang('Podcast.publish_edit');
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
$bannerDisclaimer = '';
|
|
|
|
|
$bannerText = '';
|
|
|
|
|
$linkRoute = '';
|
|
|
|
|
$linkLabel = '';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-06 15:29:15 +00:00
|
|
|
|
return <<<HTML
|
2023-08-28 13:53:04 +00:00
|
|
|
|
<div class="flex flex-wrap items-baseline px-4 py-2 border-b md:px-12 bg-stripes-default border-subtle" role="alert">
|
|
|
|
|
<p class="flex items-baseline text-gray-900">
|
2022-07-05 16:39:20 +00:00
|
|
|
|
<span class="text-xs font-semibold tracking-wide uppercase">{$bannerDisclaimer}</span>
|
|
|
|
|
<span class="ml-3 text-sm">{$bannerText}</span>
|
|
|
|
|
</p>
|
|
|
|
|
<a href="{$linkRoute}" class="ml-1 text-sm font-semibold underline shadow-xs text-accent-base hover:text-accent-hover hover:no-underline">{$linkLabel}</a>
|
|
|
|
|
</div>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
2022-07-05 16:39:20 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
2023-08-28 13:53:04 +00:00
|
|
|
|
if (! function_exists('episode_publication_status_banner')) {
|
|
|
|
|
/**
|
|
|
|
|
* Publication status banner component for podcasts
|
|
|
|
|
*
|
|
|
|
|
* Displays the appropriate banner depending on the podcast's publication status.
|
|
|
|
|
*/
|
|
|
|
|
function episode_publication_status_banner(Episode $episode, string $class = ''): string
|
|
|
|
|
{
|
|
|
|
|
switch ($episode->publication_status) {
|
|
|
|
|
case 'not_published':
|
|
|
|
|
$linkRoute = route_to('episode-publish', $episode->podcast_id, $episode->id);
|
|
|
|
|
$publishLinkLabel = lang('Episode.publish');
|
|
|
|
|
break;
|
|
|
|
|
case 'scheduled':
|
|
|
|
|
case 'with_podcast':
|
|
|
|
|
$linkRoute = route_to('episode-publish_edit', $episode->podcast_id, $episode->id);
|
|
|
|
|
$publishLinkLabel = lang('Episode.publish_edit');
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
$bannerDisclaimer = '';
|
|
|
|
|
$linkRoute = '';
|
|
|
|
|
$publishLinkLabel = '';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$bannerDisclaimer = lang('Episode.publication_status_banner.draft_mode');
|
|
|
|
|
$bannerText = lang('Episode.publication_status_banner.text', [
|
|
|
|
|
'publication_status' => $episode->publication_status,
|
|
|
|
|
'publication_date' => $episode->published_at instanceof Time ? local_datetime(
|
|
|
|
|
$episode->published_at
|
|
|
|
|
) : null,
|
2023-08-29 15:51:44 +00:00
|
|
|
|
]);
|
2023-08-28 13:53:04 +00:00
|
|
|
|
$previewLinkLabel = lang('Episode.publication_status_banner.preview');
|
|
|
|
|
|
|
|
|
|
return <<<HTML
|
|
|
|
|
<div class="flex flex-wrap gap-4 items-baseline px-4 md:px-12 py-2 bg-stripes-default border-subtle {$class}" role="alert">
|
|
|
|
|
<p class="flex items-baseline text-gray-900">
|
|
|
|
|
<span class="text-xs font-semibold tracking-wide uppercase">{$bannerDisclaimer}</span>
|
|
|
|
|
<span class="ml-3 text-sm">{$bannerText}</span>
|
|
|
|
|
</p>
|
|
|
|
|
<div class="flex items-baseline">
|
|
|
|
|
<a href="{$episode->preview_link}" class="ml-1 text-sm font-semibold underline shadow-xs text-accent-base hover:text-accent-hover hover:no-underline">{$previewLinkLabel}</a>
|
|
|
|
|
<span class="mx-1">•</span>
|
|
|
|
|
<a href="{$linkRoute}" class="ml-1 text-sm font-semibold underline shadow-xs text-accent-base hover:text-accent-hover hover:no-underline">{$publishLinkLabel}</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
HTML;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('episode_numbering')) {
|
2020-10-29 17:25:15 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns relevant translated episode numbering.
|
|
|
|
|
*
|
2021-05-12 14:00:25 +00:00
|
|
|
|
* @param bool $isAbbr component will show abbreviated numbering if true
|
2020-10-29 17:25:15 +00:00
|
|
|
|
*/
|
|
|
|
|
function episode_numbering(
|
2021-05-06 14:00:48 +00:00
|
|
|
|
?int $episodeNumber = null,
|
|
|
|
|
?int $seasonNumber = null,
|
|
|
|
|
string $class = '',
|
2021-05-12 14:00:25 +00:00
|
|
|
|
bool $isAbbr = false
|
2020-10-29 17:25:15 +00:00
|
|
|
|
): string {
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! $episodeNumber && ! $seasonNumber) {
|
2020-10-29 17:25:15 +00:00
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$transKey = '';
|
|
|
|
|
$args = [];
|
2021-05-12 14:00:25 +00:00
|
|
|
|
if ($episodeNumber !== null) {
|
2021-10-13 15:43:40 +00:00
|
|
|
|
$args['episodeNumber'] = sprintf('%02d', $episodeNumber);
|
2021-05-12 14:00:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($seasonNumber !== null) {
|
2021-10-13 15:43:40 +00:00
|
|
|
|
$args['seasonNumber'] = sprintf('%02d', $seasonNumber);
|
2021-05-12 14:00:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($episodeNumber !== null && $seasonNumber !== null) {
|
2020-10-29 17:25:15 +00:00
|
|
|
|
$transKey = 'Episode.season_episode';
|
2021-05-12 14:00:25 +00:00
|
|
|
|
} elseif ($episodeNumber !== null && $seasonNumber === null) {
|
2020-10-29 17:25:15 +00:00
|
|
|
|
$transKey = 'Episode.number';
|
2021-05-12 14:00:25 +00:00
|
|
|
|
} elseif ($episodeNumber === null && $seasonNumber !== null) {
|
2020-10-29 17:25:15 +00:00
|
|
|
|
$transKey = 'Episode.season';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($isAbbr) {
|
2021-08-09 10:28:16 +00:00
|
|
|
|
return '<abbr class="tracking-wider ' .
|
2020-10-29 17:25:15 +00:00
|
|
|
|
$class .
|
|
|
|
|
'" title="' .
|
|
|
|
|
lang($transKey, $args) .
|
2021-11-05 14:36:34 +00:00
|
|
|
|
'" data-tooltip="bottom">' .
|
2020-10-29 17:25:15 +00:00
|
|
|
|
lang($transKey . '_abbr', $args) .
|
|
|
|
|
'</abbr>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return '<span class="' .
|
|
|
|
|
$class .
|
|
|
|
|
'">' .
|
|
|
|
|
lang($transKey, $args) .
|
|
|
|
|
'</span>';
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
2021-05-19 16:35:13 +00:00
|
|
|
|
if (! function_exists('location_link')) {
|
2020-12-23 14:11:38 +00:00
|
|
|
|
/**
|
|
|
|
|
* Returns link to display from location info
|
|
|
|
|
*/
|
2021-05-12 14:00:25 +00:00
|
|
|
|
function location_link(?Location $location, string $class = ''): string
|
|
|
|
|
{
|
2023-04-14 11:11:53 +00:00
|
|
|
|
if (! $location instanceof Location) {
|
2021-05-06 14:00:48 +00:00
|
|
|
|
return '';
|
2020-12-23 14:11:38 +00:00
|
|
|
|
}
|
2021-02-27 21:21:26 +00:00
|
|
|
|
|
2021-05-06 14:00:48 +00:00
|
|
|
|
return anchor(
|
2021-05-12 14:00:25 +00:00
|
|
|
|
$location->url,
|
2022-03-04 14:33:48 +00:00
|
|
|
|
icon('map-pin', 'mr-2 flex-shrink-0') . '<span class="truncate">' . esc($location->name) . '</span>',
|
2021-05-06 14:00:48 +00:00
|
|
|
|
[
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'class' => 'w-full overflow-hidden inline-flex items-baseline hover:underline focus:ring-accent' .
|
2021-05-18 17:16:36 +00:00
|
|
|
|
($class === '' ? '' : " {$class}"),
|
2021-05-06 14:00:48 +00:00
|
|
|
|
'target' => '_blank',
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'rel' => 'noreferrer noopener',
|
2021-05-06 14:00:48 +00:00
|
|
|
|
],
|
|
|
|
|
);
|
2020-12-23 14:11:38 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2020-10-22 17:41:59 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2021-08-09 10:28:16 +00:00
|
|
|
|
if (! function_exists('audio_player')) {
|
|
|
|
|
/**
|
|
|
|
|
* Returns audio player
|
|
|
|
|
*/
|
|
|
|
|
function audio_player(string $source, string $mediaType, string $class = ''): string
|
|
|
|
|
{
|
|
|
|
|
$language = service('request')
|
|
|
|
|
->getLocale();
|
|
|
|
|
|
2022-07-06 15:29:15 +00:00
|
|
|
|
return <<<HTML
|
2021-08-09 10:28:16 +00:00
|
|
|
|
<vm-player
|
|
|
|
|
id="castopod-vm-player"
|
|
|
|
|
theme="light"
|
2021-08-11 15:47:23 +00:00
|
|
|
|
language="{$language}"
|
2021-08-09 10:28:16 +00:00
|
|
|
|
icons="castopod-icons"
|
2021-11-05 14:36:34 +00:00
|
|
|
|
class="{$class} relative z-0"
|
|
|
|
|
style="--vm-player-box-shadow:0; --vm-player-theme: hsl(var(--color-accent-base)); --vm-control-focus-color: hsl(var(--color-accent-contrast)); --vm-control-spacing: 4px; --vm-menu-item-focus-bg: hsl(var(--color-background-highlight));"
|
2021-08-09 10:28:16 +00:00
|
|
|
|
>
|
|
|
|
|
<vm-audio preload="none">
|
2021-08-11 15:47:23 +00:00
|
|
|
|
<source src="{$source}" type="{$mediaType}" />
|
2021-08-09 10:28:16 +00:00
|
|
|
|
</vm-audio>
|
|
|
|
|
<vm-ui>
|
|
|
|
|
<vm-icon-library name="castopod-icons"></vm-icon-library>
|
|
|
|
|
<vm-controls full-width>
|
|
|
|
|
<vm-playback-control></vm-playback-control>
|
|
|
|
|
<vm-volume-control></vm-volume-control>
|
|
|
|
|
<vm-current-time></vm-current-time>
|
|
|
|
|
<vm-scrubber-control></vm-scrubber-control>
|
|
|
|
|
<vm-end-time></vm-end-time>
|
|
|
|
|
<vm-settings-control></vm-settings-control>
|
|
|
|
|
<vm-default-settings></vm-default-settings>
|
|
|
|
|
</vm-controls>
|
|
|
|
|
</vm-ui>
|
|
|
|
|
</vm-player>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
2021-08-09 10:28:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2021-08-09 15:56:59 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2021-09-20 15:45:38 +00:00
|
|
|
|
|
2021-08-09 15:56:59 +00:00
|
|
|
|
if (! function_exists('relative_time')) {
|
|
|
|
|
function relative_time(Time $time, string $class = ''): string
|
|
|
|
|
{
|
2021-10-20 14:22:58 +00:00
|
|
|
|
$formatter = new IntlDateFormatter(service(
|
|
|
|
|
'request'
|
|
|
|
|
)->getLocale(), IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE);
|
|
|
|
|
$translatedDate = $time->toLocalizedString($formatter->getPattern());
|
2023-04-14 11:11:53 +00:00
|
|
|
|
$datetime = $time->format(DateTime::ATOM);
|
2021-08-09 15:56:59 +00:00
|
|
|
|
|
2022-07-06 15:29:15 +00:00
|
|
|
|
return <<<HTML
|
2023-08-28 13:53:04 +00:00
|
|
|
|
<relative-time tense="auto" class="{$class}" datetime="{$datetime}">
|
2021-08-09 15:56:59 +00:00
|
|
|
|
<time
|
2021-08-11 15:47:23 +00:00
|
|
|
|
datetime="{$datetime}"
|
|
|
|
|
title="{$time}">{$translatedDate}</time>
|
2023-04-14 11:11:53 +00:00
|
|
|
|
</relative-time>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
2021-08-09 15:56:59 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-11 12:15:20 +00:00
|
|
|
|
|
2022-07-06 15:29:15 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
if (! function_exists('local_datetime')) {
|
|
|
|
|
function local_datetime(Time $time): string
|
|
|
|
|
{
|
|
|
|
|
$formatter = new IntlDateFormatter(service(
|
|
|
|
|
'request'
|
|
|
|
|
)->getLocale(), IntlDateFormatter::MEDIUM, IntlDateFormatter::LONG);
|
|
|
|
|
$translatedDate = $time->toLocalizedString($formatter->getPattern());
|
2023-08-28 13:53:04 +00:00
|
|
|
|
$datetime = $time->format(DateTime::ATOM);
|
2022-07-06 15:29:15 +00:00
|
|
|
|
|
|
|
|
|
return <<<HTML
|
2023-08-28 13:53:04 +00:00
|
|
|
|
<relative-time datetime="{$datetime}"
|
2023-04-14 11:11:53 +00:00
|
|
|
|
prefix=""
|
|
|
|
|
threshold="PT0S"
|
|
|
|
|
weekday="long"
|
2022-07-06 15:29:15 +00:00
|
|
|
|
day="numeric"
|
2023-04-14 11:11:53 +00:00
|
|
|
|
month="long"
|
2022-07-06 15:29:15 +00:00
|
|
|
|
year="numeric"
|
|
|
|
|
hour="numeric"
|
|
|
|
|
minute="numeric">
|
|
|
|
|
<time
|
|
|
|
|
datetime="{$datetime}"
|
|
|
|
|
title="{$time}">{$translatedDate}</time>
|
2023-04-14 11:11:53 +00:00
|
|
|
|
</relative-time>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
if (! function_exists('local_date')) {
|
|
|
|
|
function local_date(Time $time): string
|
|
|
|
|
{
|
|
|
|
|
$formatter = new IntlDateFormatter(service(
|
|
|
|
|
'request'
|
|
|
|
|
)->getLocale(), IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE);
|
|
|
|
|
$translatedDate = $time->toLocalizedString($formatter->getPattern());
|
|
|
|
|
|
|
|
|
|
return <<<HTML
|
|
|
|
|
<time title="{$time}">{$translatedDate}</time>
|
|
|
|
|
HTML;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-11 12:15:20 +00:00
|
|
|
|
// ------------------------------------------------------------------------
|
2022-01-21 19:26:31 +00:00
|
|
|
|
|
|
|
|
|
if (! function_exists('explicit_badge')) {
|
|
|
|
|
function explicit_badge(bool $isExplicit, string $class = ''): string
|
|
|
|
|
{
|
|
|
|
|
if (! $isExplicit) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$explicitLabel = lang('Common.explicit');
|
2022-07-06 15:29:15 +00:00
|
|
|
|
return <<<HTML
|
2022-01-21 19:26:31 +00:00
|
|
|
|
<span class="px-1 text-xs font-semibold leading-tight tracking-wider uppercase border md:border-white/50 {$class}">{$explicitLabel}</span>
|
2022-07-06 15:29:15 +00:00
|
|
|
|
HTML;
|
2022-01-21 19:26:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
2022-01-23 19:58:30 +00:00
|
|
|
|
|
|
|
|
|
if (! function_exists('category_label')) {
|
|
|
|
|
function category_label(Category $category): string
|
|
|
|
|
{
|
|
|
|
|
$categoryLabel = '';
|
|
|
|
|
if ($category->parent_id !== null) {
|
2023-08-26 13:03:01 +00:00
|
|
|
|
$categoryLabel .= lang('Podcast.category_options.' . $category->parent->code) . ' › ';
|
2022-01-23 19:58:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-26 13:03:01 +00:00
|
|
|
|
return $categoryLabel . lang('Podcast.category_options.' . $category->code);
|
2022-01-23 19:58:30 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------------
|
2023-02-28 16:47:13 +00:00
|
|
|
|
|
|
|
|
|
if (! function_exists('downloads_abbr')) {
|
|
|
|
|
function downloads_abbr(int $downloads): string
|
|
|
|
|
{
|
|
|
|
|
if ($downloads < 1000) {
|
|
|
|
|
return (string) $downloads;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$option = match (true) {
|
|
|
|
|
$downloads < 1_000_000 => [
|
|
|
|
|
'divider' => 1_000,
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'suffix' => 'K',
|
2023-02-28 16:47:13 +00:00
|
|
|
|
],
|
|
|
|
|
$downloads < 1_000_000_000 => [
|
|
|
|
|
'divider' => 1_000_000,
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'suffix' => 'M',
|
2023-02-28 16:47:13 +00:00
|
|
|
|
],
|
|
|
|
|
default => [
|
|
|
|
|
'divider' => 1_000_000_000,
|
2023-06-12 14:47:38 +00:00
|
|
|
|
'suffix' => 'B',
|
2023-02-28 16:47:13 +00:00
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
$formatter = new NumberFormatter(service('request')->getLocale(), NumberFormatter::DECIMAL);
|
|
|
|
|
|
|
|
|
|
$formatter->setPattern('#,##0.##');
|
|
|
|
|
|
|
|
|
|
$abbr = $formatter->format($downloads / $option['divider']) . $option['suffix'];
|
|
|
|
|
|
|
|
|
|
return <<<HTML
|
|
|
|
|
<abbr title="{$downloads}">{$abbr}</abbr>
|
|
|
|
|
HTML;
|
|
|
|
|
}
|
|
|
|
|
}
|