This commit is contained in:
tree 2022-05-27 13:28:15 +02:00
rodič 7327e75e3e
revize b930ea7c34
10 změnil soubory, kde provedl 431 přidání a 49 odebrání

15
package-lock.json vygenerováno
Zobrazit soubor

@ -22,6 +22,7 @@
"fuse.js": "^6.5.3", "fuse.js": "^6.5.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"remove-markdown": "^0.3.0", "remove-markdown": "^0.3.0",
"svelte-scrolling": "^1.1.1",
"svelte-youtube": "^0.0.2" "svelte-youtube": "^0.0.2"
}, },
"devDependencies": { "devDependencies": {
@ -1953,6 +1954,14 @@
"svelte": "^3.0.0" "svelte": "^3.0.0"
} }
}, },
"node_modules/svelte-scrolling": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/svelte-scrolling/-/svelte-scrolling-1.1.1.tgz",
"integrity": "sha512-QnVZZnF+SPZVDCV9RVvgjSQm50gNuJil+XO/fmuR9gkSA7z5UdT+XW48MZAlV1zrUdWwChyDGUU4xzmJkdN9jg==",
"peerDependencies": {
"svelte": "^3.38.3"
}
},
"node_modules/svelte-youtube": { "node_modules/svelte-youtube": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz", "resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz",
@ -3352,6 +3361,12 @@
"marked": "^4.0.10" "marked": "^4.0.10"
} }
}, },
"svelte-scrolling": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/svelte-scrolling/-/svelte-scrolling-1.1.1.tgz",
"integrity": "sha512-QnVZZnF+SPZVDCV9RVvgjSQm50gNuJil+XO/fmuR9gkSA7z5UdT+XW48MZAlV1zrUdWwChyDGUU4xzmJkdN9jg==",
"requires": {}
},
"svelte-youtube": { "svelte-youtube": {
"version": "0.0.2", "version": "0.0.2",
"resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz", "resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz",

Zobrazit soubor

@ -40,6 +40,7 @@
"fuse.js": "^6.5.3", "fuse.js": "^6.5.3",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"remove-markdown": "^0.3.0", "remove-markdown": "^0.3.0",
"svelte-scrolling": "^1.1.1",
"svelte-youtube": "^0.0.2" "svelte-youtube": "^0.0.2"
} }
} }

Zobrazit soubor

@ -21,7 +21,7 @@
$: current = config[event.type]; $: current = config[event.type];
</script> </script>
<div class="flex {size === 'big' ? 'h-6 text-sm' : 'h-5 text-xs'}"> <div class="inline-block {size === 'big' ? 'h-6 text-sm' : 'h-5 text-xs'} font-normal">
<div <div
class="{size === 'big' class="{size === 'big'
? 'px-2 py-0.5' ? 'px-2 py-0.5'

145
src/lib/YouTube.svelte Normal file
Zobrazit soubor

@ -0,0 +1,145 @@
<script context="module">
/**
* Expose PlayerState constants for convenience. These constants can also be
* accessed through the global YT object after the YouTube IFrame API is instantiated.
* https://developers.google.com/youtube/iframe_api_reference#onStateChange
*/
export const PlayerState = {
UNSTARTED: -1,
ENDED: 0,
PLAYING: 1,
PAUSED: 2,
BUFFERING: 3,
CUED: 5,
};
</script>
<script>
import { onMount } from 'svelte';
import { createEventDispatcher } from 'svelte';
import YoutubePlayer from 'youtube-player';
export { className as class }; // HTML class names for container element (optional)
export let id = undefined; // HTML element ID for player (optional)
export let videoId; // Youtube video ID (required)
export let options = undefined; // YouTube player options (optional)
let className; // HTML class names for container element
let playerElem; // player DOM element reference
export let player; // player API instance
// Create and tear down player as component mounts or unmounts
onMount(() => createPlayer());
// Update videoId and load new video if URL changes
$: play(videoId);
function createPlayer() {
player = YoutubePlayer(playerElem, options);
// Register event handlers
player.on('ready', onPlayerReady);
player.on('error', onPlayerError);
player.on('stateChange', onPlayerStateChange);
player.on('playbackRateChange', onPlayerPlaybackRateChange);
player.on('playbackQualityChange', onPlayerPlaybackQualityChange);
// Tear down player when done
return () => player.destroy();
}
function play(videoId) {
// this is needed because the loadVideoById function always starts playing,
// even if you have set autoplay to 1 whereas the cueVideoById function
// never starts autoplaying
if (player && videoId) {
if (options && options.playerVars && options.playerVars.autoplay === 1) {
player.loadVideoById(videoId);
} else {
player.cueVideoById(videoId);
}
}
}
// -------------------------------------------
// Event handling
// -------------------------------------------
const dispatch = createEventDispatcher();
/**
* https://developers.google.com/youtube/iframe_api_reference#onReady
*
* @param {Object} event
* @param {Object} target - player object
*/
function onPlayerReady(event) {
dispatch('ready', event);
// Start playing
play(videoId);
}
/**
* https://developers.google.com/youtube/iframe_api_reference#onError
*
* @param {Object} event
* @param {Integer} data - error type
* @param {Object} target - player object
*/
function onPlayerError(event) {
dispatch('error', event);
}
/**
* https://developers.google.com/youtube/iframe_api_reference#onStateChange
*
* @param {Object} event
* @param {Integer} data - status change type
* @param {Object} target - actual YT player
*/
function onPlayerStateChange(event) {
dispatch('stateChange', event)
switch (event.data) {
case PlayerState.ENDED:
dispatch('end', event);
break;
case PlayerState.PLAYING:
dispatch('play', event);
break;
case PlayerState.PAUSED:
dispatch('pause', event);
break;
default:
}
}
/**
* https://developers.google.com/youtube/iframe_api_reference#onPlaybackRateChange
*
* @param {Object} event
* @param {Float} data - playback rate
* @param {Object} target - actual YT player
*/
function onPlayerPlaybackRateChange(event) {
dispatch('playbackRateChange', event);
}
/**
* https://developers.google.com/youtube/iframe_api_reference#onPlaybackQualityChange
*
* @param {Object} event
* @param {String} data - playback quality
* @param {Object} target - actual YT player
*/
function onPlayerPlaybackQualityChange(event) {
dispatch('playbackQualityChange', event);
}
</script>
<div class={className}>
<div id={id} bind:this={playerElem}></div>
</div>

Zobrazit soubor

@ -13,7 +13,7 @@
</script> </script>
<header <header
class="relative bg-center bg-cover bg-[url('/img/bg-header.jpg')] bg-no-repeat bg-blue-web" class="relative bg-center bg-cover {$page.url.pathname !== '/tv' ? "bg-[url('/img/bg-header.jpg')]" : ''} bg-no-repeat bg-blue-web-bg"
> >
<!-- <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li> --> <!-- <li class:active={$page.url.pathname === '/'}><a sveltekit:prefetch href="/">Home</a></li> -->
<div> <div>
@ -36,30 +36,54 @@
> >
</div> </div>
</div> </div>
{:else} {:else if $page.url.pathname === '/tv'}
<div class="lg:flex-1" /> <div
class="block justify-start lg:flex-1 my-auto text-center pb-3 lg:pb-0 pt-3 lg:pt-0"
>
<div class="w-40 lg:w-32 inline-block lg:block">
<a href="/tv"
><img
src="/img/utxo-tv.svg"
class="w-full"
alt="UTXO.TV"
/></a
>
</div>
</div>
{/if} {/if}
<div <div
class="flex lg:space-x-10 uppercase text-sm font-bold text-white flex-wrap gap-3" class="flex lg:space-x-10 uppercase text-sm font-bold text-white flex-wrap gap-3"
> >
{#if $page.url.pathname === '/tv'}
<a
sveltekit:prefetch
href="/"
class="m-auto hover:text-[#E16A61] "
class:text-blue-400={$page.url.pathname === "/"}>O konferenci</a
>
{:else}
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/" href="/"
class="m-auto hover:text-[#E16A61] " class="m-auto hover:text-[#E16A61] "
class:text-blue-400={$page.url.pathname === "/"}>Úvod</a class:text-blue-400={$page.url.pathname === "/"}>Úvod</a
> >
{/if}
{#if $page.url.pathname !== '/tv'}
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/tv" href="/tv"
class="m-auto hover:text-[#E16A61] text-custom-green" class="m-auto hover:text-[#E16A61] text-custom-green"
class:text-blue-400={$page.url.pathname === "/tv"}><i class="fa-solid fa-video mr-1.5"></i> Livestreamy</a class:text-blue-400={$page.url.pathname === "/tv"}><i class="fa-solid fa-video mr-1.5"></i> Livestreamy</a
> >
{/if}
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/program" href="/program"
class="m-auto hover:text-[#E16A61]" class="m-auto hover:text-[#E16A61]"
class:text-blue-400={$page.url.pathname === "/program"}>Program</a class:text-blue-400={$page.url.pathname === "/program"}>Program</a
> >
{#if $page.url.pathname !== '/tv'}
<a <a
sveltekit:prefetch sveltekit:prefetch
href="/mapa" href="/mapa"
@ -84,6 +108,7 @@
.tickets.length}){/if} .tickets.length}){/if}
</div></a </div></a
> >
{/if}
</div> </div>
{#if $page.url.pathname === '/'} {#if $page.url.pathname === '/'}
<div <div

17
src/lib/periods.js Normal file
Zobrazit soubor

@ -0,0 +1,17 @@
import { format, addDays } from "date-fns";
export function parsePeriod(bundle, str) {
const [dayNumber, times, name] = str.split("/");
const [start, end] = times.split("-");
const date = bundle.dates[dayNumber - 1];
const endDate = end > start ? date : format(addDays(new Date(date), 1), 'yyyy-MM-dd')
return {
date,
name,
period: {
start: new Date(`${date}T${start}`),
end: new Date(`${endDate}T${end}`),
},
};
}

Zobrazit soubor

@ -1,10 +1,13 @@
<script context="module"> <script context="module">
export const prerender = true; export const prerender = true;
import { bundle, userData } from "$lib/stores.js";
</script> </script>
<script> <script>
import { bundle, userData } from "$lib/stores.js";
import SvelteMarkdown from "svelte-markdown";
import Link from "$lib/Link.svelte";
const renderers = { link: Link };
</script> </script>
<svelte:head> <svelte:head>
@ -14,7 +17,16 @@
<section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web"> <section class="relative mx-auto py-6 sm:py-10 px-6 max-w-6xl text-blue-web">
<h1 class="uppercase text-2xl font-bold">Praktické informace</h1> <h1 class="uppercase text-2xl font-bold">Praktické informace</h1>
<div class="mt-6"> {#if bundle}
TBA {#each $bundle.spec['practical-info'] as item}
<div class="mt-8">
<div><a id={item.id} href="#{item.id}"><h2 class="text-xl uppercase font-bold">{item.name}</h2></div>
<div class="mt-4">
<SvelteMarkdown source={item.text} {renderers} />
</div> </div>
</div>
{/each}
{:else}
Načítám ..
{/if}
</section> </section>

Zobrazit soubor

@ -6,9 +6,10 @@
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import { goto } from "$app/navigation"; import { goto } from "$app/navigation";
import { page } from "$app/stores"; import { page } from "$app/stores";
import { format, compareAsc, compareDesc, addDays } from "date-fns"; import { format, compareAsc, compareDesc } from "date-fns";
import { bundle, userData, loadInfo, schedulePref } from "$lib/stores.js"; import { bundle, userData, loadInfo, schedulePref } from "$lib/stores.js";
import { cs } from "date-fns/locale/index.js"; import { cs } from "date-fns/locale/index.js";
import { parsePeriod } from '$lib/periods.js';
import SvelteMarkdown from "svelte-markdown"; import SvelteMarkdown from "svelte-markdown";
const renderers = { link: Link }; const renderers = { link: Link };
import Link from "$lib/Link.svelte"; import Link from "$lib/Link.svelte";
@ -181,22 +182,6 @@
return showSpeakers(bundle, ev); return showSpeakers(bundle, ev);
} }
function parsePeriod(bundle, str) {
const [dayNumber, times, name] = str.split("/");
const [start, end] = times.split("-");
const date = bundle.dates[dayNumber - 1];
const endDate = end > start ? date : format(addDays(new Date(date), 1), 'yyyy-MM-dd')
return {
date,
name,
period: {
start: new Date(`${date}T${start}`),
end: new Date(`${endDate}T${end}`),
},
};
}
function scheduleTimes(bundle, filter = false) { function scheduleTimes(bundle, filter = false) {
let arr = bundle.scheduleTimes.map((item, i) => { let arr = bundle.scheduleTimes.map((item, i) => {
const out = parsePeriod(bundle, item); const out = parsePeriod(bundle, item);

Zobrazit soubor

@ -6,17 +6,44 @@
import { onMount, onDestroy } from "svelte"; import { onMount, onDestroy } from "svelte";
import { bundle, userData } from "$lib/stores.js"; import { bundle, userData } from "$lib/stores.js";
import { format, formatDistanceToNow } from "date-fns"; import { format, formatDistanceToNow } from "date-fns";
import { parsePeriod } from '$lib/periods.js';
import EventTypeLabel from "$lib/EventTypeLabel.svelte"; import EventTypeLabel from "$lib/EventTypeLabel.svelte";
import Avatar from "$lib/Avatar.svelte"; import Avatar from "$lib/Avatar.svelte";
import YouTube from 'svelte-youtube'; import YouTube from '$lib/YouTube.svelte';
import SvelteMarkdown from "svelte-markdown"; import SvelteMarkdown from "svelte-markdown";
import Link from "$lib/Link.svelte"; import Link from "$lib/Link.svelte";
import { scrollTo, scrollRef, scrollTop } from 'svelte-scrolling';
const renderers = { link: Link }; const renderers = { link: Link };
const YToptions = {
playerVars: {
autoplay: 0
}
}
const stageStatus = {} const stageStatus = {}
const stagePlayers = {}
let events = [] let events = []
let cachedBundle = [] let cachedBundle = []
function typeColor (type) {
let color = null
switch (type) {
case 'talk':
color = 'bg-custom-green/70'
break
case 'panel':
color = 'bg-orange-400/70'
break
case 'lightning-series':
color = 'bg-yellow-400/70'
break
case 'other':
color = 'bg-rose-400/70'
break
}
return color
}
bundle.subscribe(_bundle => { bundle.subscribe(_bundle => {
events = _bundle.spec['schedule-candidates'][0].schedule events = _bundle.spec['schedule-candidates'][0].schedule
cachedBundle = _bundle cachedBundle = _bundle
@ -34,6 +61,23 @@
clearInterval(interval) clearInterval(interval)
}) })
function startStream (stageId) {
const player = stagePlayers[stageId]
if (!player) {
return null
}
player.playVideo()
}
function youtubePlayed (stageId) {
for (const pi of Object.keys(stagePlayers)) {
if (pi !== stageId) {
console.log(`stopping player: ${pi}`)
stagePlayers[pi].stopVideo()
}
}
}
function findSpeaker (sp, _bundle) { function findSpeaker (sp, _bundle) {
return _bundle.spec.speakers.find(s => s.id === sp) return _bundle.spec.speakers.find(s => s.id === sp)
} }
@ -46,7 +90,9 @@
} }
function genStatus(_bundle) { function genStatus(_bundle) {
const now = new Date(`2022-06-04T${format(new Date(), 'HH:mm')}`) const now = new Date(`2022-06-04T${format(new Date(), 'HH:mm:ss')}`)
//const now = new Date()
//const now = new Date(`2022-06-04T13:25`)
let globalNextEvents = events.filter(ev => { let globalNextEvents = events.filter(ev => {
return new Date(ev.period.end).getTime() > now.getTime() return new Date(ev.period.end).getTime() > now.getTime()
@ -56,13 +102,36 @@
for (const stage of stages.filter(s => s.livestream)) { for (const stage of stages.filter(s => s.livestream)) {
let nextEvents = [...globalNextEvents.filter(e => e.stage === stage.id)] let nextEvents = [...globalNextEvents.filter(e => e.stage === stage.id)]
let current = null let current = null
if (new Date(nextEvents[0].period.start).getTime() <= now.getTime()) { if (nextEvents.length > 0 && new Date(nextEvents[0].period.start).getTime() <= now.getTime()) {
current = nextEvents[0] current = nextEvents[0]
nextEvents = nextEvents.slice(1) nextEvents = nextEvents.slice(1)
} }
const allStreams = stage.streams.map(st => parsePeriod(_bundle, st))
const nextStreams = allStreams.filter(s => s.period.end.getTime() >= now.getTime())
if (nextStreams.length === 0) {
nextStreams.push(allStreams[allStreams.length-1])
}
let currentPercentage = null
if (current) {
let duration = (new Date(current.period.end).getTime() - new Date(current.period.start).getTime()) / 1000
let elapsed = Math.floor((now.getTime() - new Date(current.period.start).getTime()) / 1000)
currentPercentage = elapsed/(duration/100)
}
const day = format(new Date(nextStreams[0].period.start), 'yyyy-MM-dd')
let ctime = 0
if (day === '2022-06-05') {
ctime = 2
}
const scheduleLink = `/program?time=${ctime}&stage=${stage.id}&desc=true`
stageStatus[stage.id] = { stageStatus[stage.id] = {
current: current ? extendEvents([current], _bundle)[0] : null, current: current ? extendEvents([current], _bundle)[0] : null,
next: extendEvents(nextEvents.slice(0,2), _bundle) currentPercentage,
next: extendEvents(nextEvents.slice(0,2), _bundle),
stream: nextStreams[0],
scheduleLink
} }
} }
console.log(stageStatus) console.log(stageStatus)
@ -86,24 +155,55 @@
</svelte:head> </svelte:head>
<div class="w-full h-full bg-blue-web-bg/90"> <div class="w-full h-full bg-blue-web-bg/90">
<section class="relative mx-auto py-6 sm:py-10 px-6 text-white">
{#if $bundle} {#if $bundle}
<div class="px-16 py-4">
<div class="flex w-full justify-center lg:pt-6 text-white gap-4 flex-wrap lg:flex-nowrap">
{#each $bundle.spec.stages.filter(s => s.livestream) as stage, i} {#each $bundle.spec.stages.filter(s => s.livestream) as stage, i}
<div class="mb-8 bg-blue-web-bg/90 p-4 rounded-lg"> <div class="w-full md:w-1/3 lg:w-1/4 px-6 py-4 bg-blue-web-bg/70 hover:bg-blue-web-bg rounded-2xl text-center cursor-pointer transition-all shadow-xl" use:scrollTo={stage.id} on:click={() => startStream(stage.id)}>
<h1 class="uppercase text-2xl font-bold">#{i+1} | {stage.name}</h1> <div class="uppercase font-semibold text-white text-lg">#{i+1} {stage.name}</div>
<div class="mt-2 text-sm">
{#each [stageStatus[stage.id]] as ss}
{#if ss.current}
<div class="text-center mb-2"><span class="uppercase text-xs mr-2 text-white/70"></span> <EventTypeLabel event={ss.current._event} black={true} /></div>
<div class="w-full bg-gray-200 rounded-full h-2 dark:bg-gray-700 mt-4 mb-2 transition-all">
<div class="{typeColor(ss.current._event.type)} h-2 rounded-full" style="width: {ss.currentPercentage}%"></div>
</div>
<div><span class="text-white/70">{format(new Date(ss.current.period.start), 'HH:mm')}-{format(new Date(ss.current.period.end), 'HH:mm')}</span> {ss.current._event.name}</div>
{:else}
<span class="italic">☕ Přestávka {#if ss.next[0]}do {format(new Date(ss.next[0].period.start), 'HH:mm')}{/if}</span>
{/if}
{/each}
</div>
</div>
{/each}
</div>
</div>
<section class="relative mx-auto lg:py-6 px-6 text-white">
{#each $bundle.spec.stages.filter(s => s.livestream) as stage, i}
<div use:scrollRef={stage.id} id="{stage.id}" class="mb-8 bg-blue-web-bg/90 p-4 rounded-lg shadow-xl">
<div class="md:flex gap-4">
<h1 class="uppercase text-2xl font-bold"><a use:scrollTo={stage.id} on:click={() => startStream(stage.id) }>Stream #{i+1} - {stage.name}</a></h1>
<div class="my-auto mt-2 md:mt-0 text-sm flex-1 md:text-right"><a href={stageStatus[stage.id].scheduleLink} class="hover:underline" target="_blank">Program tohoto sálu ({stage.name})</a></div>
</div>
<div class="flex gap-6 mt-4 flex-wrap xl:flex-nowrap"> <div class="flex gap-6 mt-4 flex-wrap xl:flex-nowrap">
<div> <div>
<YouTube videoId="bqiZ2xih6Jk" class="bg-blue-web-bg" /> <YouTube videoId={stageStatus[stage.id].stream.name} class="bg-blue-web-bg/60" id="player-{stage.id}" options={Object.assign({}, YToptions)} bind:player={stagePlayers[stage.id]} on:play={() => youtubePlayed(stage.id)} />
</div> </div>
<div class="pr-2"> <div class="pr-2">
{#each [stageStatus[stage.id]] as ss} {#each [stageStatus[stage.id]] as ss}
<div> <div>
{#if ss.current} {#if ss.current}
<div class="uppercase text-xs mb-2 font-semibold flex gap-2"><div class="my-auto">Právě probíhá</div> <EventTypeLabel event={ss.current._event} black={true} /></div> <div class="uppercase text-xs mb-2 font-semibold flex flex-wrap gap-2">
<div class="text-xl"><span class="text-white/70">{format(new Date(ss.current.period.start), 'HH:mm')}-{format(new Date(ss.current.period.end), 'HH:mm')}</span> <a href="/udalosti?id={ss.current.event}" class="hover:underline">{ss.current._event.name}</a></div> <div class="my-auto whitespace-nowrap">Právě probíhá</div>
<div class="my-auto"><EventTypeLabel event={ss.current._event} black={true} /></div>
</div>
<div class="w-full bg-gray-200 rounded-full h-3 dark:bg-gray-700 mb-3 mt-3">
<div class="{typeColor(ss.current._event.type)} h-3 rounded-full transition-all" style="width: {ss.currentPercentage}%"></div>
</div>
<div class="text-xl"><span class="text-white/70">{format(new Date(ss.current.period.start), 'HH:mm')}-{format(new Date(ss.current.period.end), 'HH:mm')}</span> <a href="/udalosti?id={ss.current.event}" class="hover:underline" target="_blank">{ss.current._event.name}</a></div>
<div class="flex flex-wrap mt-2 gap-3"> <div class="flex flex-wrap mt-2 gap-3">
{#each ss.current._event.speakers.map(sp => findSpeaker(sp, $bundle)) as speaker} {#each ss.current._event.speakers.map(sp => findSpeaker(sp, $bundle)) as speaker}
<div class="flex gap-2"><Avatar speaker={speaker} size="extra-small" /><div><a href="/lide?id={speaker.id}" class="hover:underline">{speaker.name} {#if speaker.nickname}({speaker.nickname}){/if}</a></div></div> <div class="flex gap-2"><Avatar speaker={speaker} size="extra-small" /><div><a href="/lide?id={speaker.id}" target="_blank" class="hover:underline">{speaker.name} {#if speaker.nickname}({speaker.nickname}){/if}</a></div></div>
{/each} {/each}
</div> </div>
{#if ss.current._event.description} {#if ss.current._event.description}
@ -112,30 +212,32 @@
<SvelteMarkdown source={spoiler.md} {renderers} /> <SvelteMarkdown source={spoiler.md} {renderers} />
{#if spoiler.stripped} {#if spoiler.stripped}
<div class="text-sm text-white/30"> <div class="text-sm text-white/30">
(<a href="/udalosti?id={ss.current.event}">Zobrazit celý popis</a>) (<a href="/udalosti?id={ss.current.event}" target="_blank">Zobrazit celý popis</a>)
</div> </div>
{/if} {/if}
</div> </div>
{/each} {/each}
{/if} {/if}
<div class="text-sm mt-3 text-white/50">{@html ss.current._event.tags.map(t => `<a href="/seznam-udalosti?tag=${t}" class="hover:underline">#${t}</a>`).join(' ')}</div> <div class="text-sm mt-3 text-white/50">{@html ss.current._event.tags.map(t => `<a href="/seznam-udalosti?tag=${t}" target="_blank" class="hover:underline">#${t}</a>`).join(' ')}</div>
{:else} {:else}
<div class="text-xl">☕ Přestávka</div> <div class="text-xl italic">☕ Přestávka {#if ss.next[0]}do {format(new Date(ss.next[0].period.start), 'HH:mm')}{/if}</div>
{/if} {/if}
</div> </div>
{/each} {/each}
<div class="uppercase text-xs mb-2 font-semibold mt-6">Následuje</div> <div class="uppercase text-xs mb-2 font-semibold mt-6"><a href="{stageStatus[stage.id].scheduleLink}" target="_blank">Následuje</div>
<div class="text-sm"> <div class="text-sm 2xl:text-base">
{#each stageStatus[stage.id].next as ne} {#each stageStatus[stage.id].next as ne}
<div><span class="text-white/70">{format(new Date(ne.period.start), 'HH:mm')}-{format(new Date(ne.period.end), 'HH:mm')}</span> <a href="/udalosti?id={ne.event}" class="hover:underline">{ne._event.name}</a></div> <div><span class="text-white/70">{format(new Date(ne.period.start), 'HH:mm')}-{format(new Date(ne.period.end), 'HH:mm')}</span> <a href="/udalosti?id={ne.event}" target="_blank" class="hover:underline">{ne._event.name}</a></div>
{/each} {/each}
</div> </div>
<div class="mt-2 text-xs"><a href="{stageStatus[stage.id].scheduleLink}" class="hover:underline" target="_blank">Zobrazit následující program v tomto sále</a></div>
</div> </div>
</div> </div>
</div> </div>
{/each} {/each}
</section>
{:else} {:else}
Načítám ... Načítám ...
{/if} {/if}
</section>
</div> </div>

80
static/img/utxo-tv.svg Normal file

Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá

Za

Šířka:  |  Výška:  |  Velikost: 14 KiB