fixup! Time for React!

Stein Ivar Berghei 2023-10-27 11:35:04 +02:00
parent 8a019c0817
commit 05931eb8e9
4 changed files with 175 additions and 22 deletions

98
src/app/controls.tsx Normal file
View File

@ -0,0 +1,98 @@
import React, { useState } from "react";
import CSS from "csstype";
import { on } from "./events";
import ws from "./ws";
export default function Controls() {
const [channel, setChannel] = useState("");
const [title, setTitle] = useState("");
const [pos, setPos] = useState(0);
const [len, setLen] = useState(0);
const [song, setSong] = useState("");
const [pause, setPause] = useState(true);
const linkStyle: CSS.Properties = {
textDecoration: "none",
};
const Next = () => {
ws.send(
JSON.stringify({
event: "next",
})
);
};
const Prev = () => {
ws.send(
JSON.stringify({
event: "next",
})
);
};
const msToTime = (duration: number) => {
var milliseconds = (duration % 1000) / 100,
seconds = Math.floor((duration / 1000) % 60),
minutes = Math.floor((duration / (1000 * 60)) % 60),
minutes = minutes < 10 ? 0 + minutes : minutes;
seconds = seconds < 10 ? 0 + seconds : seconds;
return (
minutes.toString().padStart(2, "0") +
":" +
seconds.toString().padStart(2, "0")
);
};
on("dnd:song_info", (e: any) => {
setChannel(e.detail.channel);
setTitle(e.detail.current);
setPos(e.detail.position);
setLen(e.detail.len);
setSong("https://youtu.be/" + e.detail.song);
setPause(e.detail.pause);
});
on("dnd:song_position", (e: any) => {
setPos(e.detail.position);
setLen(e.detail.len);
});
return (
<section>
<div id="info">
<a
href={song}
id="link"
style={linkStyle}
className={pause ? "u-hidden" : ""}
>
<span id="channel">{channel}</span>
<span> - </span>
<span id="title">{title}</span>
</a>
<div className="controls">
<input
id="prev"
name="prev"
type="button"
value="prev"
onClick={Prev}
/>
<span id="time" className={pause ? "u-hidden" : ""}>
{msToTime(pos)} / {msToTime(len)}
</span>
<input
id="next"
name="next"
type="button"
value="next"
onClick={Next}
/>
</div>
</div>
</section>
);
}

View File

@ -9,7 +9,7 @@ function on(eventType: any, listener: { (event: any): void; (this: Document, ev:
function once(eventType: any, listener: (arg0: any) => void) { function once(eventType: any, listener: (arg0: any) => void) {
on(eventType, handleEventOnce); on(eventType, handleEventOnce);
function handleEventOnce(event) { function handleEventOnce(event: any) {
listener(event); listener(event);
off(eventType, handleEventOnce); off(eventType, handleEventOnce);
} }

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState, useCallback } from "react"; import React, { useEffect, useState } from "react";
import CSS from "csstype"; import CSS from "csstype";
import { on } from "./events"; import { on } from "./events";
import ws from "./ws"; import ws from "./ws";
@ -11,6 +11,9 @@ export default function Playlists() {
const [playlists, setPlaylists] = useState<Playlist[]>([]); const [playlists, setPlaylists] = useState<Playlist[]>([]);
const [active, setActive] = useState(""); const [active, setActive] = useState("");
const [title, setTitle] = useState("");
const [url, setUrl] = useState("");
const [output, setOutput] = useState("");
const activeStyle: CSS.Properties = { const activeStyle: CSS.Properties = {
backgroundColor: "burlywood", backgroundColor: "burlywood",
@ -31,7 +34,6 @@ export default function Playlists() {
}, []); }, []);
const Play = (e: any) => { const Play = (e: any) => {
console.log(e.target.dataset);
ws.send( ws.send(
JSON.stringify({ JSON.stringify({
event: "load_playlist", event: "load_playlist",
@ -48,30 +50,81 @@ export default function Playlists() {
); );
}; };
const AddPlaylist = () => {
if (title == "" || url == "") {
setOutput("Title or Url is empty!");
}
ws.send(
JSON.stringify({
event: "add_playlist",
payload: {
title: title,
url: url,
},
})
);
};
on("dnd:song_info", (e: any) => { on("dnd:song_info", (e: any) => {
setActive(e.detail.playlist); setActive(e.detail.playlist);
}); });
on("dnd:new_playlist", (e: any) => {
fetchPlaylists();
setOutput("New playlist was added: " + e.details.title);
});
return ( return (
<section> <>
<div id="items" className="item-container"> <section>
{playlists.map((playlist) => { <div id="items" className="item-container">
return ( {playlists.map((playlist) => {
<div return (
key={playlist.Id} <div
onClick={Play} key={playlist.Id}
className="item" onClick={Play}
data-id={playlist.Id} className="item"
style={playlist.Id == active ? activeStyle : {}} data-id={playlist.Id}
> style={playlist.Id == active ? activeStyle : {}}
{playlist.Title} >
</div> {playlist.Title}
); </div>
})} );
<div className="item locked stop" onClick={Stop} data-id="reset"> })}
Stop <div className="item locked stop" onClick={Stop} data-id="reset">
Stop
</div>
</div> </div>
</div> </section>
</section> <section>
<div id="inputplaylist" className="input-container">
<input
className="u-full-width"
name="title"
type="text"
placeholder="Enter name.."
onChange={(e) => setTitle(e.target.value)}
/>
<input
className="u-full-width"
name="url"
type="text"
placeholder="https://youtube.com/playlist?list=..."
onChange={(e) => setUrl(e.target.value)}
/>
<input
id="addplaylist"
name="submit"
value="Add"
type="submit"
onClick={AddPlaylist}
/>
</div>
</section>
<section>
<p>{output}</p>
</section>
</>
); );
} }

View File

@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
import { trigger, on } from "./events"; import { trigger, on } from "./events";
import Playlists from "./playlist"; import Playlists from "./playlist";
import ws from "./ws"; import ws from "./ws";
import Controls from "./controls";
function Content() { function Content() {
ws.onmessage = (e) => { ws.onmessage = (e) => {
@ -14,6 +15,7 @@ function Content() {
<> <>
<h2 className="bot">Playlists</h2> <h2 className="bot">Playlists</h2>
<Playlists /> <Playlists />
<Controls />
</> </>
); );
} }