mirror of
https://github.com/tomru/advcal.git
synced 2026-03-03 06:27:17 +01:00
first working version
This commit is contained in:
42
components/calendar.js
Normal file
42
components/calendar.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import React, { useContext } from "react";
|
||||
|
||||
import AppContext from "../context/app";
|
||||
|
||||
const Calendar = () => {
|
||||
const { loading, songs, openDoor } = useContext(AppContext);
|
||||
|
||||
if (loading || !songs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<ul className="cal">
|
||||
{songs.map((song, index) => {
|
||||
const classes = ["calcard", song.locked && "calcard__locked"]
|
||||
.filter(Boolean)
|
||||
.join(" ");
|
||||
|
||||
return (
|
||||
<li
|
||||
className={classes}
|
||||
data-id={index}
|
||||
key={song.id}
|
||||
onClick={() => !song.locked && openDoor(index)}
|
||||
title={
|
||||
song.locked &&
|
||||
`Es ist noch nicht der ${new Date(
|
||||
song.lockedUntil
|
||||
).toLocaleDateString()}! Geduld, nur Gedult!` || index + 1
|
||||
}
|
||||
>
|
||||
<span className="cardnumber">{index + 1}</span>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
export default Calendar;
|
||||
36
components/controls.js
vendored
Normal file
36
components/controls.js
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import React, { useEffect, useState, Fragment } from "react";
|
||||
|
||||
export const ENDED = 0;
|
||||
export const PLAYING = 1;
|
||||
export const PAUSED = 2;
|
||||
|
||||
const Controls = ({ playerState, onPause, onPlay, onRestart }) => {
|
||||
return (
|
||||
<Fragment>
|
||||
<div className="player-controls">
|
||||
{playerState === PLAYING && (
|
||||
<a onClick={onPause} title="Pause">
|
||||
⏸
|
||||
</a>
|
||||
)}
|
||||
{playerState === PAUSED && (
|
||||
<a onClick={onPlay} title="Play">
|
||||
▶
|
||||
</a>
|
||||
)}
|
||||
{[PLAYING, PAUSED].includes(playerState) && (
|
||||
<a onClick={onRestart} title="Von Vorne">
|
||||
⏮
|
||||
</a>
|
||||
)}
|
||||
{playerState === ENDED && (
|
||||
<a onClick={onPlay} title="Play">
|
||||
▶
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default Controls;
|
||||
27
components/header.js
Normal file
27
components/header.js
Normal file
@@ -0,0 +1,27 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
|
||||
const Header = () => {
|
||||
const [gifUrl, setGifUrl] = useState(null);
|
||||
useEffect(() => {
|
||||
async function getGif() {
|
||||
const response = await fetch(
|
||||
"//api.giphy.com/v1/gifs/random?tag=christmas&api_key=3ziHSa4ptYJdv2dOuawgzpBhhiW09Ss1"
|
||||
);
|
||||
const { data } = await response.json();
|
||||
setGifUrl(data.image_mp4_url);
|
||||
}
|
||||
|
||||
getGif();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="header">
|
||||
<h1>Bello's Adventskalender 2020</h1>
|
||||
{gifUrl && (
|
||||
<video className="yay-gif-video" src={gifUrl} autoPlay loop muted />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
||||
28
components/nav.js
Normal file
28
components/nav.js
Normal file
@@ -0,0 +1,28 @@
|
||||
import React from "react";
|
||||
|
||||
const links = [
|
||||
{ href: "https://youtube.com", label: "Youtube" },
|
||||
{ href: "https://www.discogs.com/", label: "Discog" },
|
||||
{ href: "https://findmusicbylyrics.com/", label: "Find music by Lyrics" },
|
||||
{ href: "https://musicbrainz.org/", label: "MusicBrainz" }
|
||||
].map(link => {
|
||||
link.key = `nav-link-${link.href}-${link.label}`;
|
||||
return link;
|
||||
});
|
||||
|
||||
const Nav = () => (
|
||||
<nav className="ad-nav">
|
||||
<ul>
|
||||
<li>Für die Recherche:</li>
|
||||
{links.map(({ key, href, label }) => (
|
||||
<li key={key}>
|
||||
<a href={href} target="_blank">
|
||||
{label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
|
||||
export default Nav;
|
||||
64
components/player.js
Normal file
64
components/player.js
Normal file
@@ -0,0 +1,64 @@
|
||||
import React, { useEffect, useState, Fragment, useContext } from "react";
|
||||
import youtubePlayer from "youtube-player";
|
||||
|
||||
import AppContext from "../context/app";
|
||||
|
||||
import Controls, { ENDED, PLAYING, PAUSED } from "./controls";
|
||||
|
||||
let player;
|
||||
|
||||
const Player = ({ hidden }) => {
|
||||
const [playerState, setPlayerState] = useState(-1);
|
||||
|
||||
const { openSong } = useContext(AppContext);
|
||||
|
||||
const onPause = () => player && player.pauseVideo();
|
||||
const onPlay = () => player && player.playVideo();
|
||||
const onRestart = () => {
|
||||
player && player.seekTo(openSong.startSeconds || 0);
|
||||
player && [PAUSED, ENDED].includes(playerState) && player.playVideo();
|
||||
};
|
||||
|
||||
useEffect(onRestart, [!hidden]);
|
||||
|
||||
useEffect(() => {
|
||||
const setState = ({ data }) => setPlayerState(data);
|
||||
player = youtubePlayer("player", {
|
||||
height: "390",
|
||||
width: "640"
|
||||
});
|
||||
player.on("stateChange", setState);
|
||||
player.loadVideoById({
|
||||
videoId: openSong.id,
|
||||
startSeconds: openSong.startSeconds,
|
||||
endSeconds: openSong.endSeconds
|
||||
});
|
||||
|
||||
return () => {
|
||||
player = null;
|
||||
setPlayerState(null);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<div className={hidden ? "hidden" : undefined}>
|
||||
<div id="player" />
|
||||
</div>
|
||||
{hidden && playerState === -1 && <span>🎶 Titel laden...</span>}
|
||||
{hidden && playerState > -1 && (
|
||||
<Fragment>
|
||||
<span>🎶 Titel spielt</span>
|
||||
<Controls
|
||||
playerState={playerState}
|
||||
onPause={onPause}
|
||||
onPlay={onPlay}
|
||||
onRestart={onRestart}
|
||||
/>
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default Player;
|
||||
57
components/showdoordialog.js
Normal file
57
components/showdoordialog.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useState, useContext } from "react";
|
||||
|
||||
import AppContext from "../context/app";
|
||||
import Player from "./player";
|
||||
|
||||
const ShowDoorDialog = () => {
|
||||
const { loading, openDoor, openSong, openSongIndex } = useContext(AppContext);
|
||||
const [showSolution, setShowSolution] = useState(false);
|
||||
const [showHint, setShowHint] = useState(false);
|
||||
|
||||
const handleClose = () => {
|
||||
openDoor(null);
|
||||
setShowSolution(false);
|
||||
};
|
||||
|
||||
if (loading || !openSong) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { title, hint, id } = openSong;
|
||||
|
||||
return (
|
||||
<div className="door-mask" onClick={handleClose}>
|
||||
<div className="door" onClick={event => event.stopPropagation()}>
|
||||
<a className="door-close" onClick={handleClose}>
|
||||
❌
|
||||
</a>
|
||||
<h1>Türchen {openSongIndex + 1}</h1>
|
||||
<Player hidden={!showSolution} />
|
||||
{!showSolution && (
|
||||
<div>
|
||||
{hint && !showHint && (
|
||||
<p>
|
||||
<a className="link" onClick={() => setShowHint(true)}>
|
||||
Ich brauche einen Tip!
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
{hint && showHint && (
|
||||
<div className="hint">
|
||||
<h4>Tip:</h4>
|
||||
<p>{hint}</p>
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
<a className="solve" onClick={() => setShowSolution(true)}>
|
||||
Zeig mir die Lösung!
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ShowDoorDialog;
|
||||
Reference in New Issue
Block a user