11<script setup>
2- import { createDirectus , rest , realtime , readItems , readSingleton , authentication } from ' @directus/sdk' ;
2+ import { createDirectus , rest , readItems , readSingleton } from ' @directus/sdk' ;
33
44const {
55 public: { tvUrl , baseUrl },
66} = useRuntimeConfig ();
77
8- const directus = createDirectus (tvUrl).with (rest ()).with (realtime ()).with (authentication ());
9- const live = await directus .request (readSingleton (' live' ));
10- const globals = await directus .request (readSingleton (' globals' , { fields: [' realtime_public_user_token' ] }));
8+ const directus = createDirectus (tvUrl).with (rest ());
119
12- const shows = await directus .request (
13- readItems (' shows' , {
14- filter: { id: { _in: live .offline_featured } },
15- }),
16- );
10+ const { data: live } = await useAsyncData (' live' , () => directus .request (readSingleton (' live' )));
1711
18- const highlights = ref ([]);
19- const highlightsLoaded = ref (false );
12+ const { data: globals } = await useAsyncData (' globals' , () =>
13+ directus .request (readSingleton (' globals' , { fields: [' realtime_public_user_token' ] })),
14+ );
2015
21- onMounted (async () => {
22- await directus .connect ();
23- directus .sendMessage ({ type: ' auth' , access_token: globals .realtime_public_user_token });
16+ const { data: shows } = await useAsyncData (' shows' , () =>
17+ directus .request (
18+ readItems (' shows' , {
19+ filter: { id: { _in: live .value .offline_featured } },
20+ }),
21+ ),
22+ );
2423
25- directus .onWebSocket (' message' , async (message ) => {
26- if (message .type == ' auth' && message .status == ' ok' ) {
27- await subscribe ();
28- }
29- });
30-
31- async function subscribe () {
32- const { subscription } = await directus .subscribe (' live' , {
33- query: { fields: [' highlights' ] },
34- uid: ' highlights' ,
35- });
36-
37- for await (const item of subscription ) {
38- if (item .event == ' init' && item .uid == ' highlights' ) {
39- highlights .value = item .data [0 ].highlights .filter ((highlight ) => highlight .show ).reverse ();
40- highlightsLoaded .value = true ;
41- }
42-
43- if (item .event == ' update' && item .uid == ' highlights' ) {
44- highlights .value = item .data [0 ].highlights .filter ((highlight ) => highlight .show ).reverse ();
45- }
46- }
47- }
24+ const domain = computed (() => {
25+ return new URL (baseUrl).hostname ;
4826});
4927
5028definePageMeta ({
@@ -63,6 +41,8 @@ useSeoMeta({
6341 twitterCard: ' summary_large_image' ,
6442 ogUrl: ` ${ baseUrl} /tv` ,
6543});
44+
45+ const isChatOpen = ref (true );
6646 </script >
6747
6848<template >
@@ -84,30 +64,62 @@ useSeoMeta({
8464 </BaseContainer >
8565 <div v-else >
8666 <BaseContainer class =" player" >
87- <iframe
88- :src =" `https://vimeo.com/event/${live.vimeo_id}/embed${live.interaction ? '/interaction' : ''}`"
89- frameborder =" 0"
90- allow =" fullscreen; picture-in-picture"
91- allowfullscreen
92- ></iframe >
67+ <template v-if =" live .vimeo_id " >
68+ <iframe
69+ :src =" `https://vimeo.com/event/${live.vimeo_id}/embed${live.interaction ? '/interaction' : ''}`"
70+ frameborder =" 0"
71+ allow =" fullscreen; picture-in-picture"
72+ allowfullscreen
73+ ></iframe >
74+ </template >
75+ <template v-else-if =" live .youtube_id " >
76+ <div class =" player-container" >
77+ <iframe
78+ class =" stream"
79+ :src =" `https://www.youtube.com/embed/${live.youtube_id}`"
80+ allow =" autoplay"
81+ allowfullscreen
82+ frameborder =" 0"
83+ referrerpolicy =" strict-origin-when-cross-origin"
84+ ></iframe >
85+ <transition name =" chat-toggle" >
86+ <div v-show =" isChatOpen" class =" chat" >
87+ <iframe
88+ :src =" `https://www.youtube.com/live_chat?v=${live.youtube_id}&embed_domain=${domain}`"
89+ allow =" autoplay"
90+ allowfullscreen
91+ frameborder =" 0"
92+ ></iframe >
93+ </div >
94+ </transition >
95+ </div >
96+ </template >
9397 </BaseContainer >
9498
9599 <BaseContainer >
96- <div class =" details" >
97- <h1 >{{ live.title }}</h1 >
98- <div v-html =" live.description" />
100+ <div class =" nav" >
101+ <BaseButton
102+ label =" Back to Directus TV"
103+ class =" secondary"
104+ href =" /tv"
105+ icon-start =" arrow_back"
106+ color =" white"
107+ size =" small"
108+ outline
109+ />
110+ <!-- Vimeo already has chat baked in to events so only show for YouTube -->
111+ <BaseButton
112+ v-if =" live.youtube_id"
113+ color =" primary"
114+ :icon =" isChatOpen ? 'visibility_off' : 'visibility'"
115+ :label =" isChatOpen ? 'Hide Chat' : 'Show Chat'"
116+ @click =" isChatOpen = !isChatOpen"
117+ />
99118 </div >
100119
101- <div v-if =" highlights.length" class =" highlights" >
102- <h2 >Live Highlights</h2 >
103- <ol v-if =" highlights.length" >
104- <li v-for =" (highlight, index) in highlights" :key =" highlight.label" >
105- <a :href =" highlight.url" target =" _blank" >
106- <BaseBadge v-if =" index === 0" label =" Latest" color =" gray" />
107- <span >{{ highlight.label }}</span >
108- </a >
109- </li >
110- </ol >
120+ <div class =" details" >
121+ <BaseHeading :content =" live.title" size =" medium" />
122+ <BaseText :content =" live.description" color =" foreground" />
111123 </div >
112124 </BaseContainer >
113125 </div >
@@ -179,41 +191,81 @@ useSeoMeta({
179191 gap : 1rem ;
180192}
181193
182- .highlights {
183- margin-top : 2rem ;
184- ol {
185- padding-left : 0 ;
186- list-style-type : none ;
194+ .player-container {
195+ display : flex ;
196+ flex-direction : column ;
197+ gap : 1rem ;
198+
199+ .stream {
200+ flex-grow : 1 ;
201+ flex-shrink : 1 ;
202+ flex-basis : auto ;
203+ aspect-ratio : 16 / 9 ;
204+ transition : flex-grow 0.4s ease ;
205+ }
206+
207+ .chat {
208+ flex-basis : 300px ;
209+ flex-shrink : 0 ;
210+ height : 400px ;
211+ max-height : 65vh ;
212+ width : 100% ;
187213 display : flex ;
188214 flex-direction : column ;
189- gap : 1em ;
190- a {
191- display : block ;
192- background : rgba (255 , 255 , 255 , 0.12 );
215+ overflow : visible ;
216+
217+ iframe {
218+ width : 100% ;
219+ height : 100% ;
193220 border-radius : 8px ;
194- box-shadow : 0 4px 30px rgba (0 , 0 , 0 , 0.1 );
195- backdrop-filter : blur (1.8px );
196- -webkit-backdrop-filter : blur (1.8px );
197- border : 1px solid rgba (255 , 255 , 255 , 0.33 );
198- color : white ;
199- text-decoration : none ;
200- padding : 1rem ;
201- display : flex ;
202- gap : 1rem ;
203- .base-badge {
204- display : none ;
205- }
206- }
207- li :not (:first-child ) {
208- opacity : 0.5 ;
221+ min-height : 380px ;
209222 }
210223 }
224+
225+ .chat-toggle-enter-active ,
226+ .chat-toggle-leave-active {
227+ transition : all 0.4s ease ;
228+ overflow : hidden ;
229+ }
230+
231+ .chat-toggle-enter-from ,
232+ .chat-toggle-leave-to {
233+ opacity : 0 ;
234+ height : 0 ;
235+ margin-bottom : 0 ;
236+ }
237+
211238 @media (width > 60rem ) {
212- ol a {
213- font-size : 1.25rem ;
214- .base-badge {
215- display : block !important ;
216- }
239+ flex-direction : row ;
240+
241+ .chat {
242+ height : auto ;
243+ max-height : none ;
244+ min-height : 450px ;
245+ }
246+
247+ .chat-toggle-enter-from ,
248+ .chat-toggle-leave-to {
249+ width : 0 ;
250+ flex-basis : 0 ;
251+ margin-right : 0 ;
252+ }
253+ }
254+ }
255+ .nav {
256+ display : flex ;
257+ flex-wrap : wrap ;
258+ justify-content : space-between ;
259+ gap : var (--space-4 );
260+ align-items : center ;
261+ margin-top : 2rem ;
262+ a {
263+ --background-color : rgba (255 , 255 , 255 , 0.12 );
264+ color : white ;
265+ outline : 2px solid white ;
266+ & :hover {
267+ color : white !important ;
268+ --background-color : rgba (255 , 255 , 255 , 0.25 );
217269 }
218270 }
219271}
0 commit comments