Skip to content

Commit

Permalink
New single surface live page (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
phazonoverload authored May 10, 2024
1 parent cd43733 commit 8637355
Show file tree
Hide file tree
Showing 6 changed files with 251 additions and 263 deletions.
1 change: 0 additions & 1 deletion components/Tv/TVNavigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<li><NuxtLink href="/">Website</NuxtLink></li>
<li><a href="https://docs.directus.io">Docs</a></li>
<li><NuxtLink href="/demo">Book a Demo</NuxtLink></li>
<li class="show-on-mobile"><NuxtLink href="/tv/live">Live</NuxtLink></li>
</ul>
<BaseButton
label="Subscribe"
Expand Down
3 changes: 0 additions & 3 deletions modules/prerender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,11 @@ export default defineNuxtModule({
readItems('episodes', { fields: ['slug', { season: ['*', { show: ['slug'] }] }], limit: -1 }),
);

const events = await directusTv.request(readItems('events', { fields: ['slug'], limit: -1 }));

permalinks.push(...pages.map((page) => page.permalink));
permalinks.push(...resources.map((resource) => `/${resource.type.slug}/${resource.slug}`));
permalinks.push(...team.map((member) => `/team/${member.slug}`));
permalinks.push(...shows.map((show) => `/tv/${show.slug}`));
permalinks.push(...episodes.map((ep) => `/tv/${ep.season.show.slug}/${ep.slug}`));
permalinks.push(...events.map((ev) => `/tv/live/${ev.slug}`));

// Add RSS feed to prerender
permalinks.push('/rss.xml');
Expand Down
31 changes: 30 additions & 1 deletion pages/tv/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const { data: categories } = await useAsyncData('tv-categories', () => {
);
});
const { data: live } = await useAsyncData('live-home', () => {
return $directusTv.request($readSingleton('live', { fields: ['title', 'live_link_on_home'] }));
});
const heroButtons = [
{
type: 'primary',
Expand Down Expand Up @@ -63,6 +67,11 @@ useSeoMeta({
:buttons="heroButtons"
/>
<BaseContainer class="main">
<NuxtLink v-if="live.live_link_on_home" to="/tv/live" class="live">
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"><circle cx="50" cy="50" r="50" /></svg>
<span>Now Live: {{ live.title }}</span>
<BaseIcon class="arrow-forward" name="arrow_forward" />
</NuxtLink>
<section class="categories">
<TVCategory v-for="category in categories" :key="category.id" :title="category.title" :shows="category.shows" />
</section>
Expand All @@ -79,7 +88,27 @@ useSeoMeta({
flex-direction: column;
gap: 2rem;
}
.live {
--red: #e35169;
background: var(--red);
border: 1px solid #922637;
color: white;
padding: 1rem;
margin-top: 4rem;
border-radius: var(--rounded-lg);
text-decoration: none;
display: flex;
gap: 0.5rem;
svg {
height: 1.25rem;
margin-top: 2px;
fill: white;
}
.arrow-forward {
--foreground: white;
margin-left: auto;
}
}
@media (width > 60rem) {
.categories {
gap: 4rem;
Expand Down
221 changes: 221 additions & 0 deletions pages/tv/live.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
<script setup>
import { createDirectus, rest, realtime, readItems, readSingleton, authentication } from '@directus/sdk';
const {
public: { tvUrl, baseUrl },
} = useRuntimeConfig();
const directus = createDirectus(tvUrl).with(rest()).with(realtime()).with(authentication());
const live = await directus.request(readSingleton('live'));
const globals = await directus.request(readSingleton('globals', { fields: ['realtime_public_user_token'] }));
const shows = await directus.request(
readItems('shows', {
filter: { id: { _in: live.offline_featured } },
sort: 'sort',
}),
);
const highlights = ref([]);
const highlightsLoaded = ref(false);
onMounted(async () => {
await directus.connect();
directus.sendMessage({ type: 'auth', access_token: globals.realtime_public_user_token });
directus.onWebSocket('message', async (message) => {
if (message.type == 'auth' && message.status == 'ok') {
await subscribe();
}
});
async function subscribe() {
const { subscription } = await directus.subscribe('live', {
query: { fields: ['highlights'] },
uid: 'highlights',
});
for await (const item of subscription) {
if (item.event == 'init' && item.uid == 'highlights') {
highlights.value = item.data[0].highlights.filter((highlight) => highlight.show).reverse();
highlightsLoaded.value = true;
}
if (item.event == 'update' && item.uid == 'highlights') {
highlights.value = item.data[0].highlights.filter((highlight) => highlight.show).reverse();
}
}
}
});
definePageMeta({
layout: 'tv',
});
const seoTitle = 'Directus TV Live';
const seoDesc = 'Live events from the team at Directus.';
useSeoMeta({
title: seoTitle,
ogTitle: seoTitle,
description: seoDesc,
ogDescription: seoDesc,
ogImage: `${tvUrl}/assets/${unref(globals).og}`,
twitterCard: 'summary_large_image',
ogUrl: `${baseUrl}/tv`,
});
</script>

<template>
<TVNavigation />
<div class="main">
<BaseContainer v-if="!live.show_player" class="offline">
<p class="notice">We're offline at the moment, but why not enjoy some on-demand shows here on Directus TV.</p>
<ul>
<li v-for="show in shows" :key="show.id">
<TVShow
:slug="show.slug"
:tile="show.tile"
:title="show.title"
:description="show.one_liner"
:overlay="show.card_text"
/>
</li>
</ul>
</BaseContainer>
<div v-else>
<BaseContainer class="player">
<iframe
:src="`https://vimeo.com/event/${live.vimeo_id}/embed${live.interaction ? '/interaction' : ''}`"
frameborder="0"
allow="fullscreen; picture-in-picture"
allowfullscreen
></iframe>
</BaseContainer>

<BaseContainer>
<div class="details">
<h1>{{ live.title }}</h1>
<div v-html="live.description" />

Check warning on line 99 in pages/tv/live.vue

View workflow job for this annotation

GitHub Actions / Lint

'v-html' directive can lead to XSS attack
</div>

<div v-if="highlights.length" class="highlights">
<h2>Live Highlights</h2>
<ol v-if="highlights.length">
<li v-for="(highlight, index) in highlights" :key="highlight.label">
<a :href="highlight.url" target="_blank">
<BaseBadge v-if="index === 0" label="Latest" color="gray" />
<span>{{ highlight.label }}</span>
</a>
</li>
</ol>
</div>
</BaseContainer>
</div>
</div>
</template>

<style lang="scss" scoped>
.main {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.offline {
p.notice {
text-align: center;
font-size: 1.5rem;
line-height: 1.5;
margin-top: 2rem;
margin-bottom: 2rem;
}
ul {
--show-gap: 20px;
margin-top: 2rem;
margin-bottom: 2rem;
padding-left: 0;
list-style-type: none;
display: grid;
grid-template-columns: 1fr;
gap: var(--show-gap);
}
li {
width: var(--show-size);
}
@media (width > 40rem) {
ul {
grid-template-columns: repeat(2, 1fr);
}
}
@media (width > 60rem) {
ul {
grid-template-columns: repeat(4, 1fr);
--show-gap: 20px;
}
}
}
.player {
grid-template-columns:
[full-start] minmax(0, 1fr)
[standard-start] 0
[narrow-start] minmax(1rem, 75rem)
[narrow-end] 0
[standard-end] minmax(0, 1fr)
[full-end];
iframe {
width: 100%;
aspect-ratio: 16/9;
background: black;
border-radius: 8px;
}
}
.details {
margin-top: 2rem;
margin-bottom: 4rem;
display: flex;
flex-direction: column;
gap: 1rem;
}
.highlights {
margin-top: 2rem;
ol {
padding-left: 0;
list-style-type: none;
display: flex;
flex-direction: column;
gap: 1em;
a {
display: block;
background: rgba(255, 255, 255, 0.12);
border-radius: 8px;
box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1);
backdrop-filter: blur(1.8px);
-webkit-backdrop-filter: blur(1.8px);
border: 1px solid rgba(255, 255, 255, 0.33);
color: white;
text-decoration: none;
padding: 1rem;
display: flex;
gap: 1rem;
.base-badge {
display: none;
}
}
li:not(:first-child) {
opacity: 0.5;
}
}
@media (width > 60rem) {
ol a {
font-size: 1.25rem;
.base-badge {
display: block !important;
}
}
}
}
</style>
Loading

0 comments on commit 8637355

Please sign in to comment.