UTXO.TV
This commit is contained in:
rodič
7327e75e3e
revize
b930ea7c34
|
@ -22,6 +22,7 @@
|
|||
"fuse.js": "^6.5.3",
|
||||
"lodash": "^4.17.21",
|
||||
"remove-markdown": "^0.3.0",
|
||||
"svelte-scrolling": "^1.1.1",
|
||||
"svelte-youtube": "^0.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1953,6 +1954,14 @@
|
|||
"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": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz",
|
||||
|
@ -3352,6 +3361,12 @@
|
|||
"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": {
|
||||
"version": "0.0.2",
|
||||
"resolved": "https://registry.npmjs.org/svelte-youtube/-/svelte-youtube-0.0.2.tgz",
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
"fuse.js": "^6.5.3",
|
||||
"lodash": "^4.17.21",
|
||||
"remove-markdown": "^0.3.0",
|
||||
"svelte-scrolling": "^1.1.1",
|
||||
"svelte-youtube": "^0.0.2"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
$: current = config[event.type];
|
||||
</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
|
||||
class="{size === 'big'
|
||||
? 'px-2 py-0.5'
|
||||
|
|
|
@ -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>
|
|
@ -13,7 +13,7 @@
|
|||
</script>
|
||||
|
||||
<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> -->
|
||||
<div>
|
||||
|
@ -22,7 +22,7 @@
|
|||
>
|
||||
<div class="">
|
||||
<div class="lg:flex lg:flex-wrap lg:space-x-10">
|
||||
{#if !["/", "/tv"].includes($page.url.pathname)}
|
||||
{#if !["/","/tv"].includes($page.url.pathname)}
|
||||
<div
|
||||
class="block justify-start lg:flex-1 my-auto text-center pb-3 lg:pb-0 pt-3 lg:pt-0"
|
||||
>
|
||||
|
@ -36,30 +36,54 @@
|
|||
>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="lg:flex-1" />
|
||||
{:else if $page.url.pathname === '/tv'}
|
||||
<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}
|
||||
<div
|
||||
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
|
||||
sveltekit:prefetch
|
||||
href="/"
|
||||
class="m-auto hover:text-[#E16A61] "
|
||||
class:text-blue-400={$page.url.pathname === "/"}>Úvod</a
|
||||
>
|
||||
{/if}
|
||||
{#if $page.url.pathname !== '/tv'}
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
href="/tv"
|
||||
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
|
||||
>
|
||||
{/if}
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
href="/program"
|
||||
class="m-auto hover:text-[#E16A61]"
|
||||
class:text-blue-400={$page.url.pathname === "/program"}>Program</a
|
||||
>
|
||||
{#if $page.url.pathname !== '/tv'}
|
||||
<a
|
||||
sveltekit:prefetch
|
||||
href="/mapa"
|
||||
|
@ -84,6 +108,7 @@
|
|||
.tickets.length}){/if}
|
||||
</div></a
|
||||
>
|
||||
{/if}
|
||||
</div>
|
||||
{#if $page.url.pathname === '/'}
|
||||
<div
|
||||
|
|
|
@ -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}`),
|
||||
},
|
||||
};
|
||||
}
|
|
@ -1,10 +1,13 @@
|
|||
<script context="module">
|
||||
export const prerender = true;
|
||||
|
||||
import { bundle, userData } from "$lib/stores.js";
|
||||
</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>
|
||||
|
||||
<svelte:head>
|
||||
|
@ -14,7 +17,16 @@
|
|||
<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>
|
||||
|
||||
<div class="mt-6">
|
||||
TBA
|
||||
</div>
|
||||
{#if bundle}
|
||||
{#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>
|
||||
{/each}
|
||||
{:else}
|
||||
Načítám ..
|
||||
{/if}
|
||||
</section>
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
import { onMount, onDestroy } from "svelte";
|
||||
import { goto } from "$app/navigation";
|
||||
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 { cs } from "date-fns/locale/index.js";
|
||||
import { parsePeriod } from '$lib/periods.js';
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
const renderers = { link: Link };
|
||||
import Link from "$lib/Link.svelte";
|
||||
|
@ -181,22 +182,6 @@
|
|||
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) {
|
||||
let arr = bundle.scheduleTimes.map((item, i) => {
|
||||
const out = parsePeriod(bundle, item);
|
||||
|
|
|
@ -6,17 +6,44 @@
|
|||
import { onMount, onDestroy } from "svelte";
|
||||
import { bundle, userData } from "$lib/stores.js";
|
||||
import { format, formatDistanceToNow } from "date-fns";
|
||||
import { parsePeriod } from '$lib/periods.js';
|
||||
import EventTypeLabel from "$lib/EventTypeLabel.svelte";
|
||||
import Avatar from "$lib/Avatar.svelte";
|
||||
import YouTube from 'svelte-youtube';
|
||||
import YouTube from '$lib/YouTube.svelte';
|
||||
import SvelteMarkdown from "svelte-markdown";
|
||||
import Link from "$lib/Link.svelte";
|
||||
import { scrollTo, scrollRef, scrollTop } from 'svelte-scrolling';
|
||||
const renderers = { link: Link };
|
||||
|
||||
const YToptions = {
|
||||
playerVars: {
|
||||
autoplay: 0
|
||||
}
|
||||
}
|
||||
const stageStatus = {}
|
||||
const stagePlayers = {}
|
||||
let events = []
|
||||
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 => {
|
||||
events = _bundle.spec['schedule-candidates'][0].schedule
|
||||
cachedBundle = _bundle
|
||||
|
@ -34,6 +61,23 @@
|
|||
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) {
|
||||
return _bundle.spec.speakers.find(s => s.id === sp)
|
||||
}
|
||||
|
@ -46,7 +90,9 @@
|
|||
}
|
||||
|
||||
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 => {
|
||||
return new Date(ev.period.end).getTime() > now.getTime()
|
||||
|
@ -56,13 +102,36 @@
|
|||
for (const stage of stages.filter(s => s.livestream)) {
|
||||
let nextEvents = [...globalNextEvents.filter(e => e.stage === stage.id)]
|
||||
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]
|
||||
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] = {
|
||||
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)
|
||||
|
@ -86,24 +155,55 @@
|
|||
</svelte:head>
|
||||
|
||||
<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}
|
||||
<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)}>
|
||||
<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 class="mb-8 bg-blue-web-bg/90 p-4 rounded-lg">
|
||||
<h1 class="uppercase text-2xl font-bold">#{i+1} | {stage.name}</h1>
|
||||
<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>
|
||||
<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 class="pr-2">
|
||||
{#each [stageStatus[stage.id]] as ss}
|
||||
<div>
|
||||
{#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="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="uppercase text-xs mb-2 font-semibold flex flex-wrap gap-2">
|
||||
<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">
|
||||
{#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}
|
||||
</div>
|
||||
{#if ss.current._event.description}
|
||||
|
@ -112,30 +212,32 @@
|
|||
<SvelteMarkdown source={spoiler.md} {renderers} />
|
||||
{#if spoiler.stripped}
|
||||
<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>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
{/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}
|
||||
<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}
|
||||
</div>
|
||||
{/each}
|
||||
<div class="uppercase text-xs mb-2 font-semibold mt-6">Následuje</div>
|
||||
<div class="text-sm">
|
||||
<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 2xl:text-base">
|
||||
{#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}
|
||||
</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>
|
||||
{/each}
|
||||
{:else}
|
||||
Načítám ...
|
||||
{/if}
|
||||
</section>
|
||||
</section>
|
||||
{:else}
|
||||
Načítám ...
|
||||
{/if}
|
||||
</div>
|
||||
|
|
Rozdílový obsah nebyl zobrazen, protože některé řádky jsou příliš dlouhá
Za Šířka: | Výška: | Velikost: 14 KiB |
Načítá se…
Odkázat v novém úkolu