diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 74d1a06acfda1728887a62e9066748831575b606..04b8ebfdc54db17dd3e2c5be0899e37cf93fb3a7 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -80,6 +80,9 @@ $routes->group('@(:podcastHandle)', function ($routes): void { ], ], ]); + $routes->get('about', 'PodcastController::about/$1', [ + 'as' => 'podcast-about', + ]); $routes->options('episodes', 'ActivityPubController::preflight'); $routes->get('episodes', 'PodcastController::episodes/$1', [ 'as' => 'podcast-episodes', diff --git a/app/Controllers/PodcastController.php b/app/Controllers/PodcastController.php index 47892deba2b902628c152384469bee8d85a12789..21ce0ed08127fcb2619565ee0fa2783a299bf6c7 100644 --- a/app/Controllers/PodcastController.php +++ b/app/Controllers/PodcastController.php @@ -105,6 +105,51 @@ class PodcastController extends BaseController return $cachedView; } + public function about(): string + { + // Prevent analytics hit when authenticated + if (! can_user_interact()) { + $this->registerPodcastWebpageHit($this->podcast->id); + } + + $cacheName = implode( + '_', + array_filter([ + 'page', + "podcast#{$this->podcast->id}", + 'about', + service('request') + ->getLocale(), + can_user_interact() ? '_authenticated' : null, + ]), + ); + + if (! ($cachedView = cache($cacheName))) { + $data = [ + 'podcast' => $this->podcast, + ]; + + // if user is logged in then send to the authenticated activity view + if (can_user_interact()) { + helper('form'); + return view('podcast/about_authenticated', $data); + } + + $secondsToNextUnpublishedEpisode = (new EpisodeModel())->getSecondsToNextUnpublishedEpisode( + $this->podcast->id, + ); + + return view('podcast/about', $data, [ + 'cache' => $secondsToNextUnpublishedEpisode + ? $secondsToNextUnpublishedEpisode + : DECADE, + 'cache_name' => $cacheName, + ]); + } + + return $cachedView; + } + public function episodes(): string { // Prevent analytics hit when authenticated diff --git a/app/Helpers/page_helper.php b/app/Helpers/page_helper.php index 002e6ae01aebe3098cbe13f30a36d455a834312b..7a24aaa1ce70df943597a582da363f37f93b1ce4 100644 --- a/app/Helpers/page_helper.php +++ b/app/Helpers/page_helper.php @@ -23,10 +23,10 @@ if (! function_exists('render_page_links')) { 'class' => 'px-2 py-1 underline hover:no-underline', ]); $links .= anchor(route_to('credits'), lang('Person.credits'), [ - 'class' => 'px-2 py-1 underline hover:no-underline', + 'class' => 'px-2 py-1 underline hover:no-underline', ]); $links .= anchor(route_to('map'), lang('Page.map'), [ - 'class' => 'px-2 underline hover:no-underline', + 'class' => 'px-2 py-1 underline hover:no-underline', ]); foreach ($pages as $page) { $links .= anchor($page->link, $page->title, [ diff --git a/app/Language/en/Podcast.php b/app/Language/en/Podcast.php index 0683cb2d658655d0fd7e8db1ab7a1f656d183384..0c311ed83257cb8ef4863c809343013eed23b95c 100644 --- a/app/Language/en/Podcast.php +++ b/app/Language/en/Podcast.php @@ -42,6 +42,7 @@ return [ }', 'activity' => 'Activity', 'episodes' => 'Episodes', + 'about' => 'About', 'sponsor_title' => 'Enjoying the show?', 'sponsor' => 'Sponsor', 'funding_links' => 'Funding links for {podcastTitle}', diff --git a/app/Language/fr/Podcast.php b/app/Language/fr/Podcast.php index 12e9bba317043b0b05988ee7f5561d8016ddc97b..d332a9336297b50ef01b08f0bf21f298f2f69644 100644 --- a/app/Language/fr/Podcast.php +++ b/app/Language/fr/Podcast.php @@ -42,6 +42,7 @@ return [ }', 'activity' => 'Activité', 'episodes' => 'Épisodes', + 'about' => 'About', 'sponsor_title' => 'Vous aimez le podcast ?', 'sponsor' => 'Soutenez-nous', 'funding_links' => 'Liens de financement pour {podcastTitle}', diff --git a/tailwind.config.js b/tailwind.config.js index cfe1014c18d5e001108809b20ca7e23b9c048295..ea55a83cccf00bd5c5ad5fe78510f622ff63a6a0 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,5 +1,6 @@ /* eslint-disable */ const defaultTheme = require("tailwindcss/defaultTheme"); +const colors = require("tailwindcss/colors"); module.exports = { mode: "jit", @@ -42,6 +43,7 @@ module.exports = { 800: "#b21a39", 900: "#8e162e", }, + orange: colors.orange, }, spacing: { 112: "28rem", diff --git a/themes/cp_app/_admin_navbar.php b/themes/cp_app/_admin_navbar.php new file mode 100644 index 0000000000000000000000000000000000000000..aedbab7bb1cb9966deea970125b01bef32af62ae --- /dev/null +++ b/themes/cp_app/_admin_navbar.php @@ -0,0 +1,46 @@ +<div class="sticky top-0 left-0 z-50 flex items-center justify-between w-full h-12 px-4 text-white border-b shadow bg-pine-800 border-pine-900"> + <?= anchor( + route_to('admin'), + 'castopod' . svg('castopod-logo-base', 'h-5 ml-1'), + [ + 'class' => + 'text-2xl inline-flex items-baseline font-bold font-display', + ], +) ?> + <?php if (user()->podcasts !== []): ?> + <button type="button" class="inline-flex items-center px-6 py-2 mt-auto font-semibold outline-none focus:ring" id="interact-as-dropdown" data-dropdown="button" data-dropdown-target="interact-as-dropdown-menu" aria-haspopup="true" aria-expanded="false"> + <img src="<?= interact_as_actor() + ->avatar_image_url ?>" class="w-8 h-8 mr-2 rounded-full" /> + <?= '@' . interact_as_actor()->username ?> + <?= icon('caret-down', 'ml-auto') ?> + </button> + <nav id="interact-as-dropdown-menu" class="absolute z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border rounded shadow" aria-labelledby="my-accountDropdown" data-dropdown="menu" data-dropdown-placement="bottom-end"> + <span class="px-4 text-xs tracking-wider text-gray-700 uppercase"><?= lang( + 'Admin.choose_interact', + ) ?></span> + <form action="<?= route_to( + 'interact-as-actor', + ) ?>" method="POST" class="flex flex-col"> + <?= csrf_field() ?> + <?php foreach (user()->podcasts as $userPodcast): ?> + <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="<?= "interact-as-actor-{$userPodcast->id}" ?>" name="actor_id" value="<?= $userPodcast->actor_id ?>"> + <span class="inline-flex items-center flex-1"> + <img src="<?= $userPodcast->image + ->thumbnail_url ?>" class="w-8 h-8 mr-2 rounded-full" /><?= $userPodcast->title ?> + <?php if ( + interact_as_actor() + ->id === + $userPodcast->actor_id + ): ?> + </span> + <?= icon( + 'check', + 'ml-4 bg-pine-800 text-white rounded-full', + ) ?> + <?php endif; ?> + </button> + <?php endforeach; ?> + </form> + </nav> + <?php endif; ?> + </div> \ No newline at end of file diff --git a/themes/cp_app/_layout.php b/themes/cp_app/_layout.php index c0c05b2c7e3ad581cc3b91f6159e0453e47d48d0..bc8377c60caec9f131607b210ac82f1ec0cc0376 100644 --- a/themes/cp_app/_layout.php +++ b/themes/cp_app/_layout.php @@ -17,7 +17,11 @@ </head> <body class="flex flex-col min-h-screen mx-auto bg-gray-100"> - <header class="py-8 text-white border-b bg-pine-900"> + <?php if (service('authentication')->check()): ?> + <?= $this->include('_admin_navbar') ?> + <?php endif; ?> + + <header class="py-8 text-white border-b bg-pine-800"> <div class="container flex flex-col px-2 py-4 mx-auto"> <a href="<?= route_to('home') ?>" class="inline-flex items-center mb-2"><?= icon( diff --git a/themes/cp_app/home.php b/themes/cp_app/home.php index cae58aadc05d1395f065adaf507a50cd72d5606c..e903879469ef173bb7b379946de5c8288b370998 100644 --- a/themes/cp_app/home.php +++ b/themes/cp_app/home.php @@ -14,6 +14,10 @@ </head> <body class="flex flex-col min-h-screen mx-auto bg-pine-50"> + <?php if (service('authentication')->check()): ?> + <?= $this->include('_admin_navbar') ?> + <?php endif; ?> + <header class="py-8 text-white border-b bg-pine-800"> <div class="container flex items-center justify-between px-2 py-4 mx-auto"> <a href="<?= route_to( diff --git a/app/Views/map.php b/themes/cp_app/map.php similarity index 90% rename from app/Views/map.php rename to themes/cp_app/map.php index ff9fac45eb0f5a1663439e599888aa23ba2fd4bc..b82e424b4f9c7355a20eaea1deef7a40e5a05edc 100644 --- a/app/Views/map.php +++ b/themes/cp_app/map.php @@ -16,7 +16,11 @@ </head> <body class="flex flex-col h-full min-h-screen mx-auto bg-gray-100"> - <header class="py-8 text-white border-b bg-pine-900"> + <?php if (service('authentication')->check()): ?> + <?= $this->include('_admin_navbar') ?> + <?php endif; ?> + + <header class="py-8 text-white border-b bg-pine-800"> <div class="container flex flex-col px-2 py-4 mx-auto"> <a href="<?= route_to('home') ?>" class="inline-flex items-center mb-2"><?= icon( diff --git a/themes/cp_app/page.php b/themes/cp_app/page.php index 60143b2e328cec733224aad85882dcbcede0f713..49cea85027a19d95859b3f1bf66c3342733793ef 100644 --- a/themes/cp_app/page.php +++ b/themes/cp_app/page.php @@ -13,6 +13,10 @@ </head> <body class="flex flex-col min-h-screen mx-auto"> + <?php if (service('authentication')->check()): ?> + <?= $this->include('_admin_navbar') ?> + <?php endif; ?> + <header class="py-8 text-white border-b bg-pine-800"> <div class="container flex flex-col px-2 py-4 mx-auto"> <a href="<?= route_to('home') ?>" diff --git a/themes/cp_app/podcast/_layout copy.php b/themes/cp_app/podcast/_layout copy.php new file mode 100644 index 0000000000000000000000000000000000000000..a37e77b4ac315fd2f0047a819f55c1a756d200af --- /dev/null +++ b/themes/cp_app/podcast/_layout copy.php @@ -0,0 +1,123 @@ +<?= helper('page') ?> + +<!DOCTYPE html> +<html lang="<?= service('request') + ->getLocale() ?>"> + +<head> + <meta charset="UTF-8"/> + <meta name="viewport" content="width=device-width, initial-scale=1.0"/> + <link rel="shortcut icon" type="image/png" href="/favicon.ico" /> + + <?= $this->renderSection('meta-tags') ?> + <?php if ($podcast->payment_pointer): ?> + <meta name="monetization" content="<?= $podcast->payment_pointer ?>" /> + <?php endif; ?> + + <?= service('vite') + ->asset('styles/index.css', 'css') ?> + <?= service('vite') + ->asset('js/podcast.ts', 'js') ?> + <?= service('vite') + ->asset('js/audio-player.ts', 'js') ?> +</head> + +<body class="flex w-full min-h-screen pb-20 overflow-x-hidden lg:mx-auto lg:container bg-pine-50 sm:pb-0"> + <?= $this->include('podcast/_partials/header') ?> + + <main class="flex-shrink-0 w-full min-w-0 sm:w-auto sm:flex-1 sm:flex-shrink"> + <nav class="sticky top-0 left-0 z-50 flex items-center w-full h-12 px-2 py-1 sm:hidden bg-pine-800"> + <button + data-toggle="main-header" + data-toggle-class="sticky -translate-x-full" + class="flex-shrink-0 mr-3 overflow-hidden rounded-full focus:ring-2 focus:outline-none focus:ring-pine-50"> + <img src="<?= $podcast->image + ->thumbnail_url ?>" alt="<?= $podcast->title ?>" class="h-10"/> + </button> + <p class="flex flex-col flex-1 min-w-0 mr-2 text-white"> + <span class="text-sm font-semibold truncate"><?= $podcast->title ?></span> + <span class="text-xs">@<?= $podcast->handle ?></span> + </p> + <?= anchor_popup( + route_to('follow', $podcast->handle), + icon( + 'social/castopod', + 'mr-2 text-xl text-pink-200 group-hover:text-pink-50', + ) . lang('Podcast.follow'), + [ + 'width' => 420, + 'height' => 620, + 'class' => + 'group inline-flex mr-2 items-center px-3 py-1 text-xs tracking-wider font-semibold text-white uppercase rounded-full shadow focus:outline-none focus:ring bg-rose-600', + ], + ) ?> + <button + data-toggle="main-sidebar" + data-toggle-class="translate-x-full" + data-toggle-body-class="-ml-64" + class="p-2 text-xl rounded-full focus:outline-none focus:ring-2 focus:ring-pine-600 text-pine-200 hover:text-pine-50"><?= icon( + 'menu', + ) ?><span class="sr-only"><?= lang('Podcast.toggle_podcast_sidebar') ?></span></button> + </nav> + <?= $this->renderSection('content') ?> + </main> + + <?= $this->include('podcast/_partials/sidebar') ?> + + <button + data-toggle="main-sidebar" + data-toggle-class="translate-x-full" + data-toggle-body-class="-ml-64" + class="fixed z-40 hidden p-4 text-xl rounded-full shadow-2xl sm:block lg:hidden bottom-4 left-4 bg-pine-800 focus:outline-none focus:ring-2 focus:ring-pine-600 text-pine-200 hover:text-pine-50"><?= icon( + 'menu', + ) ?><span class="sr-only"><?= lang( + 'Podcast.toggle_podcast_sidebar', + ) ?></span></button> + + <!-- Funding links modal --> + <div id="funding-links" class="fixed top-0 left-0 z-50 flex items-center justify-center hidden w-screen h-screen"> + <div + class="absolute w-full h-full bg-pine-800 bg-opacity-90" + role="button" + data-toggle="funding-links" + data-toggle-class="hidden" + aria-label="<?= lang('Common.close') ?>"></div> + <div class="z-10 w-full max-w-xl bg-white rounded-lg shadow-2xl"> + <div class="flex justify-between px-4 py-2 border-b"> + <h3 class="self-center text-lg"><?= lang( + 'Podcast.funding_links', + [ + 'podcastTitle' => $podcast->title, + ], + ) ?></h3> + <button + data-toggle="funding-links" + data-toggle-class="hidden" + aria-label="<?= lang('Common.close') ?>" + class="self-start p-1 text-2xl"><?= icon('close') ?></button> + </div> + <div class="flex flex-col items-start p-4 space-y-4"> + <?php foreach ( + $podcast->fundingPlatforms + as $fundingPlatform + ): ?> + <?php if ($fundingPlatform->is_visible): ?> + <a + href="<?= $fundingPlatform->link_url ?>" + title="<?= $fundingPlatform->link_content ?>" + target="_blank" + rel="noopener noreferrer" + class="inline-flex items-center font-semibold text-pine-900"> + <?= icon( + $fundingPlatform->type . + '/' . + $fundingPlatform->slug, + 'mr-2', + ) . $fundingPlatform->link_url ?> + </a> + <?php endif; ?> + <?php endforeach; ?> + </div> + </div> + </div> +</body> diff --git a/themes/cp_app/podcast/_layout.php b/themes/cp_app/podcast/_layout.php index a37e77b4ac315fd2f0047a819f55c1a756d200af..be66043a9e9cc4752df848c7f03a5310991b841e 100644 --- a/themes/cp_app/podcast/_layout.php +++ b/themes/cp_app/podcast/_layout.php @@ -22,102 +22,116 @@ ->asset('js/audio-player.ts', 'js') ?> </head> -<body class="flex w-full min-h-screen pb-20 overflow-x-hidden lg:mx-auto lg:container bg-pine-50 sm:pb-0"> - <?= $this->include('podcast/_partials/header') ?> +<body class="grid items-start w-1/2 grid-cols-9 mx-auto bg-pine-50 gap-y-8 gap-x-6"> + <header class="sticky z-50 flex flex-col bg-white shadow -top-96 rounded-b-xl col-span-full"> + <div style="background-image: url('<?= $podcast->actor->cover_image_url ?>'); background-size: auto 320px;" class="w-full bg-fixed bg-top bg-no-repeat bg-cover bg-pine-800 h-80"></div> + <div class="flex items-center justify-between py-4 ml-8 -mt-28"> + <div class="flex items-center gap-x-4"> + <img src="<?= $podcast->image->thumbnail_url ?>" alt="<?= $podcast->title ?>" loading="lazy" class="rounded-full h-36 ring-4 ring-white" /> + <div class="flex flex-col -mt-4 text-white"> + <h1 class="inline-flex items-center text-2xl font-bold leading-none font-display"><?= $podcast->title . ($podcast->parental_advisory === 'explicit' ? '<span class="px-1 ml-2 text-xs font-semibold leading-tight tracking-wider text-gray-600 uppercase border-2 border-gray-500">' . lang('Common.explicit') . '</span>' : '') ?></h1> + <a href="#" class="hover:underline"><?= lang('Podcast.followers', [ + 'numberOfFollowers' => $podcast->actor->followers_count, + ]) ?></a> + </div> + </div> - <main class="flex-shrink-0 w-full min-w-0 sm:w-auto sm:flex-1 sm:flex-shrink"> - <nav class="sticky top-0 left-0 z-50 flex items-center w-full h-12 px-2 py-1 sm:hidden bg-pine-800"> - <button - data-toggle="main-header" - data-toggle-class="sticky -translate-x-full" - class="flex-shrink-0 mr-3 overflow-hidden rounded-full focus:ring-2 focus:outline-none focus:ring-pine-50"> - <img src="<?= $podcast->image - ->thumbnail_url ?>" alt="<?= $podcast->title ?>" class="h-10"/> - </button> - <p class="flex flex-col flex-1 min-w-0 mr-2 text-white"> - <span class="text-sm font-semibold truncate"><?= $podcast->title ?></span> - <span class="text-xs">@<?= $podcast->handle ?></span> - </p> + <div class="inline-flex items-center mr-4 -mt-4 gap-x-2"> + <?php if (in_array(true, array_column($podcast->fundingPlatforms, 'is_visible'), true)): ?> + <IconButton glyph="heart" data-toggle="funding-links" data-toggle-class="hidden"><?= lang('Podcast.sponsor') . lang('Podcast.sponsor_title') ?></IconButton> + <?php endif; ?> <?= anchor_popup( - route_to('follow', $podcast->handle), - icon( - 'social/castopod', - 'mr-2 text-xl text-pink-200 group-hover:text-pink-50', - ) . lang('Podcast.follow'), - [ - 'width' => 420, - 'height' => 620, - 'class' => - 'group inline-flex mr-2 items-center px-3 py-1 text-xs tracking-wider font-semibold text-white uppercase rounded-full shadow focus:outline-none focus:ring bg-rose-600', - ], - ) ?> - <button - data-toggle="main-sidebar" - data-toggle-class="translate-x-full" - data-toggle-body-class="-ml-64" - class="p-2 text-xl rounded-full focus:outline-none focus:ring-2 focus:ring-pine-600 text-pine-200 hover:text-pine-50"><?= icon( - 'menu', - ) ?><span class="sr-only"><?= lang('Podcast.toggle_podcast_sidebar') ?></span></button> + route_to('follow', $podcast->handle), + icon( + 'social/castopod', + 'mr-2 text-xl text-pink-200 group-hover:text-pink-50', + ) . lang('Podcast.follow'), + [ + 'width' => 420, + 'height' => 620, + 'class' => + 'group inline-flex items-center px-4 py-2 text-xs tracking-wider font-semibold text-white uppercase rounded-full shadow focus:outline-none focus:ring bg-rose-600', + ], + ) ?> + </div> + </div> + <nav class="flex gap-4 px-8"> + <a href="<?= route_to('podcast-activity', $podcast->handle) ?>" class="px-4 py-1 font-semibold uppercase border-b-4 text-pine-500 border-pine-500"><?= lang('Podcast.activity') ?></a> + <a href="<?= route_to('podcast-episodes', $podcast->handle) ?>" class="px-4 py-1 font-semibold text-gray-500 uppercase hover:text-black"><?= lang('Podcast.episodes') ?></a> + <a href="<?= route_to('podcast-about', $podcast->handle) ?>" class="px-4 py-1 font-semibold text-gray-500 uppercase hover:text-black"><?= lang('Podcast.about') ?></a> </nav> + </header> + + <main class="col-span-6"> <?= $this->renderSection('content') ?> </main> - <?= $this->include('podcast/_partials/sidebar') ?> - - <button - data-toggle="main-sidebar" - data-toggle-class="translate-x-full" - data-toggle-body-class="-ml-64" - class="fixed z-40 hidden p-4 text-xl rounded-full shadow-2xl sm:block lg:hidden bottom-4 left-4 bg-pine-800 focus:outline-none focus:ring-2 focus:ring-pine-600 text-pine-200 hover:text-pine-50"><?= icon( - 'menu', - ) ?><span class="sr-only"><?= lang( - 'Podcast.toggle_podcast_sidebar', - ) ?></span></button> - - <!-- Funding links modal --> - <div id="funding-links" class="fixed top-0 left-0 z-50 flex items-center justify-center hidden w-screen h-screen"> - <div - class="absolute w-full h-full bg-pine-800 bg-opacity-90" - role="button" - data-toggle="funding-links" - data-toggle-class="hidden" - aria-label="<?= lang('Common.close') ?>"></div> - <div class="z-10 w-full max-w-xl bg-white rounded-lg shadow-2xl"> - <div class="flex justify-between px-4 py-2 border-b"> - <h3 class="self-center text-lg"><?= lang( - 'Podcast.funding_links', + <aside class="sticky col-span-3 top-12"> + <?php if ( + in_array(true, array_column($podcast->socialPlatforms, 'is_visible'), true) + ): ?> + <h2 class="font-semibold"> <?= lang('Podcast.find_on', [ + 'podcastTitle' => $podcast->title, + ]) ?></h2> + <div class="grid items-center justify-center grid-cols-6 gap-3 mt-2"> + <?php foreach ($podcast->socialPlatforms as $socialPlatform): ?> + <?php if ($socialPlatform->is_visible): ?> + <?= anchor( + $socialPlatform->link_url, + icon("{$socialPlatform->type}/{$socialPlatform->slug}"), [ - 'podcastTitle' => $podcast->title, + 'class' => 'text-2xl text-gray-500 hover:text-gray-700 w-8 h-8 items-center inline-flex justify-center', + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + 'data-toggle' => 'tooltip', + 'data-placement' => 'bottom', + 'title' => $socialPlatform->label, ], - ) ?></h3> - <button - data-toggle="funding-links" - data-toggle-class="hidden" - aria-label="<?= lang('Common.close') ?>" - class="self-start p-1 text-2xl"><?= icon('close') ?></button> - </div> - <div class="flex flex-col items-start p-4 space-y-4"> - <?php foreach ( - $podcast->fundingPlatforms - as $fundingPlatform - ): ?> - <?php if ($fundingPlatform->is_visible): ?> - <a - href="<?= $fundingPlatform->link_url ?>" - title="<?= $fundingPlatform->link_content ?>" - target="_blank" - rel="noopener noreferrer" - class="inline-flex items-center font-semibold text-pine-900"> - <?= icon( - $fundingPlatform->type . - '/' . - $fundingPlatform->slug, - 'mr-2', - ) . $fundingPlatform->link_url ?> - </a> - <?php endif; ?> - <?php endforeach; ?> - </div> + ) ?> + <?php endif; ?> + <?php endforeach; ?> + </div> + <?php endif; ?> + + <h2 class="mt-6 font-semibold"><?= lang('Podcast.listen_on') ?></h2> + <div class="grid items-center justify-center grid-cols-6 gap-3 mt-2"> + <?= anchor(route_to('podcast_feed', $podcast->handle), icon('rss'), [ + 'class' => + 'bg-orange-500 text-xl text-white hover:bg-orange-700 w-8 h-8 inline-flex items-center justify-center rounded-lg', + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + 'data-toggle' => 'tooltip', + 'data-placement' => 'bottom', + 'title' => lang('Podcast.feed'), + ]) ?> + <?php foreach ($podcast->podcastingPlatforms as $podcastingPlatform): ?> + <?php if ($podcastingPlatform->is_visible): ?> + <?= anchor( + $podcastingPlatform->link_url, + icon( + "{$podcastingPlatform->type}/{$podcastingPlatform->slug}", + ), + [ + 'class' => 'text-2xl text-gray-500 hover:text-gray-700 w-8 h-8 items-center inline-flex justify-center', + 'target' => '_blank', + 'rel' => 'noopener noreferrer', + 'data-toggle' => 'tooltip', + 'data-placement' => 'bottom', + 'title' => $podcastingPlatform->label, + ], + ) ?> + <?php endif; ?> + <?php endforeach; ?> </div> - </div> + <footer class="flex flex-col items-center py-2 mt-8 text-xs text-center text-gray-600 border-t"> + <?= render_page_links('inline-flex mb-2 flex-wrap gap-y-1') ?> + <div class="flex flex-col"> + <p><?= $podcast->copyright ?></p> + <p><?= lang('Common.powered_by', [ + 'castopod' => + '<a class="inline-flex font-semibold hover:underline" href="https://castopod.org" target="_blank" rel="noreferrer noopener">Castopod' . icon('social/castopod', 'ml-1 text-lg') . '</a>', + ]) ?></p> + </div> + </footer> + </aside> </body> diff --git a/themes/cp_app/podcast/_layout_authenticated.php b/themes/cp_app/podcast/_layout_authenticated.php index 57ec0bed0758df937d49dda4b7bd2209853c6048..eef37085a7e471e6d73122e14652299f25242a8d 100644 --- a/themes/cp_app/podcast/_layout_authenticated.php +++ b/themes/cp_app/podcast/_layout_authenticated.php @@ -22,53 +22,10 @@ ->asset('js/audio-player.ts', 'js') ?> </head> -<body class="flex w-full min-h-screen pt-12 pb-20 overflow-x-hidden bg-pine-50 lg:mx-auto lg:container sm:pb-0"> - <div class="fixed top-0 left-0 z-50 flex items-center justify-between w-full h-12 px-4 text-white shadow bg-pine-800"> - <?= anchor( - route_to('admin'), - 'castopod' . svg('castopod-logo-base', 'h-5 ml-1'), - [ - 'class' => - 'text-2xl inline-flex items-baseline font-bold font-display', - ], - ) ?> - <?php if (user()->podcasts !== []): ?> - <button type="button" class="inline-flex items-center px-6 py-2 mt-auto font-semibold outline-none focus:ring" id="interact-as-dropdown" data-dropdown="button" data-dropdown-target="interact-as-dropdown-menu" aria-haspopup="true" aria-expanded="false"> - <img src="<?= interact_as_actor() - ->avatar_image_url ?>" class="w-8 h-8 mr-2 rounded-full" /> - <?= '@' . interact_as_actor()->username ?> - <?= icon('caret-down', 'ml-auto') ?> - </button> - <nav id="interact-as-dropdown-menu" class="absolute z-50 flex flex-col py-2 text-black whitespace-no-wrap bg-white border rounded shadow" aria-labelledby="my-accountDropdown" data-dropdown="menu" data-dropdown-placement="bottom-end"> - <span class="px-4 text-xs tracking-wider text-gray-700 uppercase"><?= lang( - 'Admin.choose_interact', - ) ?></span> - <form action="<?= route_to( - 'interact-as-actor', - ) ?>" method="POST" class="flex flex-col"> - <?= csrf_field() ?> - <?php foreach (user()->podcasts as $userPodcast): ?> - <button class="inline-flex items-center w-full px-4 py-1 hover:bg-gray-100" id="<?= "interact-as-actor-{$userPodcast->id}" ?>" name="actor_id" value="<?= $userPodcast->actor_id ?>"> - <span class="inline-flex items-center flex-1"> - <img src="<?= $userPodcast->image - ->thumbnail_url ?>" class="w-8 h-8 mr-2 rounded-full" /><?= $userPodcast->title ?> - <?php if ( - interact_as_actor() - ->id === - $userPodcast->actor_id - ): ?> - </span> - <?= icon( - 'check', - 'ml-4 bg-pine-800 text-white rounded-full', - ) ?> - <?php endif; ?> - </button> - <?php endforeach; ?> - </form> - </nav> - <?php endif; ?> - </div> +<body class="w-full min-h-screen pb-20 overflow-x-hidden bg-pine-50 lg:mx-auto sm:pb-0"> + <?= $this->include('_admin_navbar') ?> + <div class="flex"> + <?= $this->include('podcast/_partials/header') ?> <main class="flex-shrink-0 w-full min-w-0 sm:w-auto sm:flex-1 sm:flex-shrink"> @@ -154,4 +111,5 @@ </div> </div> </div> +</div> </body> diff --git a/themes/cp_app/podcast/about.php b/themes/cp_app/podcast/about.php new file mode 100644 index 0000000000000000000000000000000000000000..436beeae4b46a18d7fb871808ca38643c0ae5f75 --- /dev/null +++ b/themes/cp_app/podcast/about.php @@ -0,0 +1,35 @@ +<?= $this->extend('podcast/_layout') ?> + +<?= $this->section('meta-tags') ?> +<!-- TODO: --> + +<link type="application/rss+xml" rel="alternate" title="<?= $podcast->title ?>" href="<?= $podcast->feed_url ?>" /> + +<title><?= $podcast->title ?></title> +<meta name="description" content="<?= htmlspecialchars( + $podcast->description, +) ?>" /> +<link rel="shortcut icon" type="image/png" href="/favicon.ico" /> +<link rel="canonical" href="<?= current_url() ?>" /> +<meta property="og:title" content="<?= $podcast->title ?>" /> +<meta property="og:description" content="<?= $podcast->description ?>" /> +<meta property="og:locale" content="<?= $podcast->language_code ?>" /> +<meta property="og:site_name" content="<?= $podcast->title ?>" /> +<meta property="og:url" content="<?= current_url() ?>" /> +<meta property="og:image" content="<?= $podcast->image->large_url ?>" /> +<meta property="og:image:width" content="<?= config('Images') + ->largeSize ?>" /> +<meta property="og:image:height" content="<?= config('Images') + ->largeSize ?>" /> +<meta name="twitter:card" content="summary_large_image" /> + +<?= service('vite') + ->asset('styles/index.css', 'css') ?> +<?= $this->endSection() ?> + +<?= $this->section('content') ?> + + + +<?= $this->endSection() +?> diff --git a/themes/cp_app/podcast/activity.php b/themes/cp_app/podcast/activity.php index 332605910fd2e99d1a77f589ee6476a8653c3341..1be8aa5abc20bbb45a3e47c1dc99e12c7ea66385 100644 --- a/themes/cp_app/podcast/activity.php +++ b/themes/cp_app/podcast/activity.php @@ -27,21 +27,7 @@ <?= $this->section('content') ?> -<nav class="sticky z-20 flex justify-center pt-2 text-lg sm:top-0 top-12 bg-pine-50"> -<a href="<?= route_to( - 'podcast-activity', - $podcast->handle, - ) ?>" class="px-4 py-1 mr-8 font-semibold border-b-4 text-pine-800 border-pine-500"><?= lang( - 'Podcast.activity', - ) ?></a> - <a href="<?= route_to( - 'podcast-episodes', - $podcast->handle, - ) ?>" class="px-4 py-1 rounded-full hover:bg-pine-100"><?= lang( - 'Podcast.episodes', - ) ?></a> -</nav> -<section class="max-w-2xl px-6 py-8 mx-auto space-y-8"> +<section class="max-w-2xl mx-auto space-y-8"> <?php foreach ($posts as $post): ?> <?php if ($post->reblog_of_id !== null): ?> <?= view('podcast/_partials/reblog', [ diff --git a/themes/cp_app/podcast/episodes.php b/themes/cp_app/podcast/episodes.php index e49ed562c30d01aa4b1d59fbdddfdf229d1ede44..ed4a9a523e897edebde1a50f1d3be2360ab76a6a 100644 --- a/themes/cp_app/podcast/episodes.php +++ b/themes/cp_app/podcast/episodes.php @@ -26,63 +26,50 @@ <?= $this->endSection() ?> <?= $this->section('content') ?> -<nav class="sticky z-20 flex items-center justify-center pt-2 text-lg top-12 sm:top-0 bg-pine-50"> - <a href="<?= route_to( - 'podcast-activity', - $podcast->handle, - ) ?>" class="px-4 py-1 mr-8 rounded-full hover:bg-pine-100"><?= lang( - 'Podcast.activity', - ) ?></a> - <a href="<?= route_to( - 'podcast-episodes', - $podcast->handle, - ) ?>" class="px-4 py-1 font-semibold border-b-4 text-pine-800 border-pine-500"><?= lang( - 'Podcast.episodes', - ) ?></a> - <?php if ($activeQuery): ?> - <button id="episode-lists-dropdown" type="button" class="inline-flex items-center px-2 py-1 text-sm font-semibold outline-none focus:ring" data-dropdown="button" data-dropdown-target="episode-lists-dropdown-menu" aria-label="<?= lang( - 'Common.more', - ) ?>" aria-haspopup="true" aria-expanded="false"> - <?= $activeQuery['label'] . - ' (' . - $activeQuery['number_of_episodes'] . - ')' . - icon('caret-down', 'ml-2 text-xl') ?> - </button> - <nav id="episode-lists-dropdown-menu" class="flex flex-col py-2 text-black bg-white border rounded shadow" aria-labelledby="episode-lists-dropdown" data-dropdown="menu" data-dropdown-placement="bottom-end"> - <?php foreach ($episodesNav as $link): ?> - <?= anchor( - $link['route'], - $link['label'] . ' (' . $link['number_of_episodes'] . ')', - [ - 'class' => - 'px-2 py-1 whitespace-nowrap ' . - ($link['is_active'] - ? 'font-semibold' - : 'text-gray-600 hover:text-gray-900'), - ], - ) ?> - <?php endforeach; ?> - </nav> - <?php endif; ?> -</nav> - -<section class="flex flex-col max-w-2xl px-6 py-8 mx-auto"> +<section class="flex flex-col max-w-2xl"> <?php if ($episodes): ?> - <h1 class="mb-4 text-xl font-semibold"> - <?php if ($activeQuery['type'] === 'year'): ?> - <?= lang('Podcast.list_of_episodes_year', [ - 'year' => $activeQuery['value'], - 'episodeCount' => count($episodes), - ]) ?> - <?php elseif ($activeQuery['type'] === 'season'): ?> - <?= lang('Podcast.list_of_episodes_season', [ - 'seasonNumber' => $activeQuery['value'], - 'episodeCount' => count($episodes), - ]) ?> + <div class="flex items-center justify-between mb-4"> + <h1 class="text-xl font-semibold"> + <?php if ($activeQuery['type'] === 'year'): ?> + <?= lang('Podcast.list_of_episodes_year', [ + 'year' => $activeQuery['value'], + 'episodeCount' => count($episodes), +]) ?> + <?php elseif ($activeQuery['type'] === 'season'): ?> + <?= lang('Podcast.list_of_episodes_season', [ + 'seasonNumber' => $activeQuery['value'], + 'episodeCount' => count($episodes), +]) ?> + <?php endif; ?> + </h1> + <?php if ($activeQuery): ?> + <button id="episode-lists-dropdown" type="button" class="inline-flex items-center px-2 py-1 text-sm font-semibold outline-none focus:ring" data-dropdown="button" data-dropdown-target="episode-lists-dropdown-menu" aria-label="<?= lang( + 'Common.more', +) ?>" aria-haspopup="true" aria-expanded="false"> + <?= $activeQuery['label'] . + ' (' . + $activeQuery['number_of_episodes'] . + ')' . + icon('caret-down', 'ml-2 text-xl') ?> + </button> + <nav id="episode-lists-dropdown-menu" class="flex flex-col py-2 text-black bg-white border rounded shadow" aria-labelledby="episode-lists-dropdown" data-dropdown="menu" data-dropdown-placement="bottom-end"> + <?php foreach ($episodesNav as $link): ?> + <?= anchor( + $link['route'], + $link['label'] . ' (' . $link['number_of_episodes'] . ')', + [ + 'class' => + 'px-2 py-1 whitespace-nowrap ' . + ($link['is_active'] + ? 'font-semibold' + : 'text-gray-600 hover:text-gray-900'), + ], + ) ?> + <?php endforeach; ?> + </nav> <?php endif; ?> - </h1> + </div> <?php foreach ($episodes as $episode): ?> <?= view('podcast/_partials/episode_card', [ 'episode' => $episode,