Compare commits
2 Commits
605bd7f29e
...
2b01cd239a
Author | SHA1 | Date |
---|---|---|
steino | 2b01cd239a | |
Stein Ivar Berghei | 5b1c693bcd |
27
routes.go
27
routes.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
|
@ -50,6 +51,8 @@ func init() {
|
|||
app.router = httprouter.New()
|
||||
|
||||
app.router.GET("/", auth(app.Index))
|
||||
app.router.GET("/playlists", auth(app.Web_Playlists))
|
||||
app.router.GET("/ambiance", auth(app.Web_Ambiance))
|
||||
app.router.GET("/play/:playlist", auth(app.Play))
|
||||
app.router.GET("/reset", auth(app.Reset))
|
||||
app.router.GET("/public/*js", auth(app.ServeFiles))
|
||||
|
@ -232,6 +235,30 @@ func (app App) Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params
|
|||
}
|
||||
}
|
||||
|
||||
func (app App) Web_Playlists(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
playlists, err := app.GetPlaylists()
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(playlists)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (app App) Web_Ambiance(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
ambiance, err := GetAmbiances()
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to get ambiance. "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
err = json.NewEncoder(w).Encode(ambiance)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to get playlists. "+err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
|
||||
func (app *App) Play(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
plname := p.ByName("playlist")
|
||||
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import CSS from "csstype";
|
||||
import { on } from "./events";
|
||||
import ws from "./ws";
|
||||
import SortableList, { SortableItem } from "react-easy-sort";
|
||||
import { arrayMoveImmutable } from "array-move";
|
||||
|
||||
export default function Ambiance() {
|
||||
interface Ambiance {
|
||||
Id: string;
|
||||
Title: string;
|
||||
}
|
||||
|
||||
const [ambiance, setAmbiance] = useState<Ambiance[]>([]);
|
||||
const [active, setActive] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [url, setUrl] = useState("");
|
||||
const [percent, setPercent] = useState(0);
|
||||
const [running, setRunning] = useState(false);
|
||||
|
||||
const onSortEnd = (oldIndex: number, newIndex: number) => {
|
||||
setAmbiance((array) => arrayMoveImmutable(array, oldIndex, newIndex));
|
||||
};
|
||||
|
||||
const activeStyle: CSS.Properties = {
|
||||
backgroundColor: "burlywood",
|
||||
};
|
||||
|
||||
const getOrder = (name: string) => {
|
||||
var order = localStorage.getItem("dndmusicbot-" + name);
|
||||
return order ? order.split("|") : [];
|
||||
};
|
||||
|
||||
const setOrder = (name: string) => {
|
||||
var order = ambiance.map((a) => a.Id);
|
||||
localStorage.setItem("dndmusicbot-" + name, order.join("|"));
|
||||
};
|
||||
|
||||
const fetchAmbiance = () => {
|
||||
const order = getOrder("ambiance");
|
||||
|
||||
fetch("/ambiance")
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
data.sort(
|
||||
(a: Ambiance, b: Ambiance) =>
|
||||
order.indexOf(a.Id) - order.indexOf(b.Id)
|
||||
);
|
||||
setAmbiance(data);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchAmbiance();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOrder("ambiance");
|
||||
}, [ambiance]);
|
||||
|
||||
const Play = (e: any) => {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "ambiance_play",
|
||||
payload: e.target.dataset.id,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const Stop = () => {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "ambiance_stop",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const AddAmbiance = () => {
|
||||
if (title == "" || url == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "ambiance_add",
|
||||
payload: {
|
||||
title: title,
|
||||
url: url,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
on("dnd:ambiance_add", () => fetchAmbiance());
|
||||
on("dnd:ambiance_add_start", () => setRunning(true));
|
||||
on("dnd:ambiance_add_finish", () => {
|
||||
setRunning(false);
|
||||
setPercent(0);
|
||||
setTitle("");
|
||||
setUrl("");
|
||||
});
|
||||
on("dnd:ambiance_play", (e: any) => setActive(e.detail.id));
|
||||
on("dnd:ambiance_stop", () => setActive(""));
|
||||
on("dnd:ambiance_encode_finish", () => setPercent(0));
|
||||
on("dnd:ambiance_encode_progress", (e: any) => {
|
||||
setRunning(true);
|
||||
|
||||
const p = parseInt(e.detail.percent, 10);
|
||||
if (!Number.isNaN(p)) {
|
||||
setPercent(p);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="bot">Ambiance</h2>
|
||||
<section>
|
||||
<SortableList
|
||||
onSortEnd={onSortEnd}
|
||||
className="item-container"
|
||||
draggedItemClassName="dragged"
|
||||
>
|
||||
{ambiance.map((item) => {
|
||||
return (
|
||||
<SortableItem key={item.Id}>
|
||||
<div
|
||||
className="item drag"
|
||||
data-id={item.Id}
|
||||
style={item.Id == active ? activeStyle : {}}
|
||||
onClick={Play}
|
||||
>
|
||||
{item.Title}
|
||||
</div>
|
||||
</SortableItem>
|
||||
);
|
||||
})}
|
||||
<div className="item locked stop" data-id="reset" onClick={Stop}>
|
||||
Stop
|
||||
</div>
|
||||
</SortableList>
|
||||
</section>
|
||||
<section>
|
||||
<div id="progressambiance" className="input-container">
|
||||
<progress
|
||||
max="100"
|
||||
value={percent}
|
||||
style={{ width: "100%" }}
|
||||
className={running ? "" : "u-hidden"}
|
||||
>
|
||||
{percent}%
|
||||
</progress>
|
||||
</div>
|
||||
</section>
|
||||
<section className={running ? "u-hidden" : ""}>
|
||||
<div id="inputambiance" className="input-container">
|
||||
<input
|
||||
className="u-full-width"
|
||||
name="title"
|
||||
type="text"
|
||||
value={title}
|
||||
placeholder="Enter name.."
|
||||
onChange={(e) => setTitle(e.target.value)}
|
||||
disabled={running}
|
||||
/>
|
||||
<input
|
||||
className="u-full-width"
|
||||
name="url"
|
||||
type="text"
|
||||
value={url}
|
||||
placeholder="Enter url..."
|
||||
onChange={(e) => setUrl(e.target.value)}
|
||||
disabled={running}
|
||||
/>
|
||||
<input
|
||||
id="addambiance"
|
||||
name="submit"
|
||||
value="Add"
|
||||
type="submit"
|
||||
onClick={AddAmbiance}
|
||||
disabled={running}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -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={pause ? "controls u-hidden" : "controls"}>
|
||||
<input
|
||||
id="prev"
|
||||
name="prev"
|
||||
type="button"
|
||||
value="prev"
|
||||
onClick={Prev}
|
||||
/>
|
||||
<span id="time">
|
||||
{msToTime(pos)} / {msToTime(len)}
|
||||
</span>
|
||||
<input
|
||||
id="next"
|
||||
name="next"
|
||||
type="button"
|
||||
value="next"
|
||||
onClick={Next}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
function on(eventType: any, listener: { (event: any): void; (this: Document, ev: any): any; }) {
|
||||
document.addEventListener(eventType, listener);
|
||||
}
|
||||
|
||||
function off(eventType: any, listener: { (event: any): void; (this: Document, ev: any): any; }) {
|
||||
document.removeEventListener(eventType, listener);
|
||||
}
|
||||
|
||||
function once(eventType: any, listener: (arg0: any) => void) {
|
||||
on(eventType, handleEventOnce);
|
||||
|
||||
function handleEventOnce(event: any) {
|
||||
listener(event);
|
||||
off(eventType, handleEventOnce);
|
||||
}
|
||||
}
|
||||
|
||||
function trigger(eventType: string, data: any) {
|
||||
const event = new CustomEvent(eventType, { detail: data });
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
|
||||
export { on, once, off, trigger };
|
|
@ -0,0 +1,17 @@
|
|||
import { useState, useEffect } from "react";
|
||||
|
||||
export const useDebounce = (value: any, milliSeconds: number) => {
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, milliSeconds);
|
||||
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
}, [value, milliSeconds]);
|
||||
|
||||
return debouncedValue;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
import React, { useState } from "react";
|
||||
import ws from "./ws";
|
||||
|
||||
export default function Modal() {
|
||||
const [waiting, setWaiting] = useState(true);
|
||||
|
||||
ws.onopen = () => {
|
||||
console.log("false");
|
||||
setWaiting(false);
|
||||
};
|
||||
|
||||
ws.onclose = () => {
|
||||
console.log("true");
|
||||
setWaiting(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div id="waiting" className={waiting ? "modal" : "modal u-hidden"}>
|
||||
<div className="modal-content">
|
||||
<p>Waiting for bot..</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import CSS from "csstype";
|
||||
import { on } from "./events";
|
||||
import ws from "./ws";
|
||||
import SortableList, { SortableItem } from "react-easy-sort";
|
||||
import { arrayMoveImmutable } from "array-move";
|
||||
|
||||
export default function Playlists() {
|
||||
interface Playlist {
|
||||
Id: string;
|
||||
Title: string;
|
||||
}
|
||||
|
||||
const [playlists, setPlaylists] = useState<Playlist[]>([]);
|
||||
const [active, setActive] = useState("");
|
||||
const [title, setTitle] = useState("");
|
||||
const [url, setUrl] = useState("");
|
||||
const [output, setOutput] = useState("");
|
||||
|
||||
const onSortEnd = (oldIndex: number, newIndex: number) => {
|
||||
setPlaylists((array) => arrayMoveImmutable(array, oldIndex, newIndex));
|
||||
};
|
||||
|
||||
const activeStyle: CSS.Properties = {
|
||||
backgroundColor: "burlywood",
|
||||
};
|
||||
|
||||
const getOrder = (name: string) => {
|
||||
var order = localStorage.getItem("dndmusicbot-" + name);
|
||||
return order ? order.split("|") : [];
|
||||
};
|
||||
|
||||
const setOrder = (name: string) => {
|
||||
var order = playlists.map((a) => a.Id);
|
||||
localStorage.setItem("dndmusicbot-" + name, order.join("|"));
|
||||
};
|
||||
|
||||
const fetchPlaylists = () => {
|
||||
const order = getOrder("playlist");
|
||||
|
||||
fetch("/playlists")
|
||||
.then((response) => {
|
||||
return response.json();
|
||||
})
|
||||
.then((data) => {
|
||||
data.sort(
|
||||
(a: Playlist, b: Playlist) =>
|
||||
order.indexOf(a.Id) - order.indexOf(b.Id)
|
||||
);
|
||||
setPlaylists(data);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchPlaylists();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setOrder("playlist");
|
||||
}, [playlists]);
|
||||
|
||||
const Play = (e: any) => {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "load_playlist",
|
||||
payload: e.target.dataset.id,
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const Stop = () => {
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "stop",
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const AddPlaylist = () => {
|
||||
if (title == "" || url == "") {
|
||||
setOutput("Title or Url is empty!");
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "add_playlist",
|
||||
payload: {
|
||||
title: title,
|
||||
url: url,
|
||||
},
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
on("dnd:song_info", (e: any) => {
|
||||
setActive(e.detail.playlist);
|
||||
});
|
||||
|
||||
on("dnd:new_playlist", (e: any) => {
|
||||
fetchPlaylists();
|
||||
setOutput("New playlist was added: " + e.details.title);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<h2 className="bot">Playlists</h2>
|
||||
<section>
|
||||
<SortableList
|
||||
onSortEnd={onSortEnd}
|
||||
className="item-container"
|
||||
draggedItemClassName="dragged"
|
||||
>
|
||||
{playlists.map((playlist) => {
|
||||
return (
|
||||
<SortableItem key={playlist.Id}>
|
||||
<div
|
||||
onClick={Play}
|
||||
className="item"
|
||||
data-id={playlist.Id}
|
||||
style={playlist.Id == active ? activeStyle : {}}
|
||||
>
|
||||
{playlist.Title}
|
||||
</div>
|
||||
</SortableItem>
|
||||
);
|
||||
})}
|
||||
<div className="item locked stop" onClick={Stop} data-id="reset">
|
||||
Stop
|
||||
</div>
|
||||
</SortableList>
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import React, { useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { trigger, on } from "./events";
|
||||
import Playlists from "./playlist";
|
||||
import ws from "./ws";
|
||||
import Controls from "./controls";
|
||||
import Ambiance from "./ambiance";
|
||||
import Volumes from "./volume";
|
||||
import Modal from "./modal";
|
||||
|
||||
function Content() {
|
||||
ws.onmessage = (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
trigger("dnd:" + data.event, data.payload);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Playlists />
|
||||
<Controls />
|
||||
<Ambiance />
|
||||
<Volumes />
|
||||
<Modal />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const domNode = document.getElementById("content");
|
||||
const root = createRoot(domNode!);
|
||||
root.render(<Content />);
|
|
@ -0,0 +1,111 @@
|
|||
import React, { useState } from "react";
|
||||
import { on } from "./events";
|
||||
import ws from "./ws";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useDebounce } from "./hooks/useDebounce";
|
||||
|
||||
export default function Volumes() {
|
||||
const [playlist, setPlaylist] = useState(0);
|
||||
const [ambiance, setAmbiance] = useState(0);
|
||||
|
||||
const plfirstUpdate = useRef(true);
|
||||
const ambfirstUpdate = useRef(true);
|
||||
const plsend = useDebounce(playlist, 100);
|
||||
const ambsend = useDebounce(ambiance, 100);
|
||||
|
||||
on("dnd:volume", (e: any) => {
|
||||
setPlaylist(e.detail.playlist);
|
||||
setAmbiance(e.detail.ambiance);
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (plfirstUpdate.current) {
|
||||
plfirstUpdate.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "vol_set",
|
||||
payload: {
|
||||
type: "playlist",
|
||||
vol: plsend.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [plsend]);
|
||||
|
||||
useEffect(() => {
|
||||
if (ambfirstUpdate.current) {
|
||||
ambfirstUpdate.current = false;
|
||||
return;
|
||||
}
|
||||
ws.send(
|
||||
JSON.stringify({
|
||||
event: "vol_set",
|
||||
payload: {
|
||||
type: "ambiance",
|
||||
vol: ambsend.toString(),
|
||||
},
|
||||
})
|
||||
);
|
||||
}, [ambsend]);
|
||||
|
||||
const PlaylistVol = (e: any) => {
|
||||
setPlaylist(parseFloat(e.target.value));
|
||||
};
|
||||
const AmbianceVol = (e: any) => {
|
||||
setAmbiance(parseFloat(e.target.value));
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<div id="volume_playlist" className="input-container">
|
||||
<label htmlFor="playlist-volume">Playlist</label>
|
||||
<input
|
||||
type="range"
|
||||
id="playlist-volume"
|
||||
min="-10"
|
||||
max="4"
|
||||
step="0.1"
|
||||
value={playlist}
|
||||
onChange={PlaylistVol}
|
||||
/>
|
||||
<input
|
||||
id="playlist-volume-number"
|
||||
type="number"
|
||||
min="-10"
|
||||
max="4"
|
||||
step="0.1"
|
||||
style={{ width: "50px" }}
|
||||
value={playlist}
|
||||
onChange={PlaylistVol}
|
||||
/>
|
||||
</div>
|
||||
<div id="volume_ambiance" className="input-container">
|
||||
<label htmlFor="ambiance-volume">Ambiance</label>
|
||||
<input
|
||||
type="range"
|
||||
id="ambiance-volume"
|
||||
min="-10"
|
||||
max="4"
|
||||
step="0.1"
|
||||
value={ambiance}
|
||||
onChange={AmbianceVol}
|
||||
/>
|
||||
<input
|
||||
id="ambiance-volume-number"
|
||||
type="number"
|
||||
min="-10"
|
||||
max="4"
|
||||
step="0.1"
|
||||
style={{ width: "50px" }}
|
||||
value={ambiance}
|
||||
onChange={AmbianceVol}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import ReconnectingWebSocket from "reconnecting-websocket";
|
||||
|
||||
const ws = new ReconnectingWebSocket(
|
||||
(window.location.protocol === "https:" ? "wss://" : "ws://") +
|
||||
window.location.host +
|
||||
"/ws"
|
||||
);
|
||||
|
||||
export default ws;
|
|
@ -1,9 +1,20 @@
|
|||
{
|
||||
"scripts": {
|
||||
"build": "esbuild script.js --bundle --outdir=/public/"
|
||||
"build": "esbuild app/root.tsx --bundle --outfile=../public_test/script.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/react": "^18.2.33",
|
||||
"@types/react-dom": "^18.2.14",
|
||||
"array-move": "^4.0.0",
|
||||
"esbuild": "^0.15.14",
|
||||
"react": "^18.2.0",
|
||||
"react-beautiful-dnd": "^13.1.1",
|
||||
"react-debounce-input": "^3.3.0",
|
||||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-easy-sort": "^1.6.0",
|
||||
"react-sortable-hoc": "^2.0.0",
|
||||
"reconnecting-websocket": "^4.4.0",
|
||||
"sortablejs": "^1.15.0"
|
||||
}
|
||||
|
|
284
src/yarn.lock
284
src/yarn.lock
|
@ -2,6 +2,13 @@
|
|||
# yarn lockfile v1
|
||||
|
||||
|
||||
"@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.9.2":
|
||||
version "7.23.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.2.tgz#062b0ac103261d68a966c4c7baf2ae3e62ec3885"
|
||||
integrity sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.14.0"
|
||||
|
||||
"@esbuild/android-arm@0.15.14":
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.14.tgz#5d0027f920eeeac313c01fd6ecb8af50c306a466"
|
||||
|
@ -12,6 +19,96 @@
|
|||
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.14.tgz#1221684955c44385f8af34f7240088b7dc08d19d"
|
||||
integrity sha512-eQi9rosGNVQFJyJWV0HCA5WZae/qWIQME7s8/j8DMvnylfBv62Pbu+zJ2eUDqNf2O4u3WB+OEXyfkpBoe194sg==
|
||||
|
||||
"@react-dnd/asap@^5.0.1":
|
||||
version "5.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-5.0.2.tgz#1f81f124c1cd6f39511c11a881cfb0f715343488"
|
||||
integrity sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==
|
||||
|
||||
"@react-dnd/invariant@^4.0.1":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-4.0.2.tgz#b92edffca10a26466643349fac7cdfb8799769df"
|
||||
integrity sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==
|
||||
|
||||
"@react-dnd/shallowequal@^4.0.1":
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz#d1b4befa423f692fa4abf1c79209702e7d8ae4b4"
|
||||
integrity sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0":
|
||||
version "3.3.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.4.tgz#cc477ce0283bb9d19ea0cbfa2941fe2c8493a1be"
|
||||
integrity sha512-ZchYkbieA+7tnxwX/SCBySx9WwvWR8TaP5tb2jRAzwvLb/rWchGw3v0w3pqUbUvj0GCwW2Xz/AVPSk6kUGctXQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
|
||||
"@types/prop-types@*":
|
||||
version "15.7.9"
|
||||
resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.9.tgz#b6f785caa7ea1fe4414d9df42ee0ab67f23d8a6d"
|
||||
integrity sha512-n1yyPsugYNSmHgxDFjicaI2+gCNjsBck8UX9kuofAKlc0h1bL+20oSF72KeNaW2DUlesbEVCFgyV2dPGTiY42g==
|
||||
|
||||
"@types/react-dom@^18.2.14":
|
||||
version "18.2.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.14.tgz#c01ba40e5bb57fc1dc41569bb3ccdb19eab1c539"
|
||||
integrity sha512-V835xgdSVmyQmI1KLV2BEIUgqEuinxp9O4G6g3FqO/SqLac049E53aysv0oEFD2kHfejeKU+ZqL2bcFWj9gLAQ==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.20":
|
||||
version "7.1.28"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.28.tgz#30a44303c7daceb6ede9cfb4aaf72e64f1dde4de"
|
||||
integrity sha512-EQr7cChVzVUuqbA+J8ArWK1H0hLAHKOs21SIMrskKZ3nHNeE+LFYA+IsoZGhVOT8Ktjn3M20v4rnZKN3fLbypw==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.0"
|
||||
"@types/react" "*"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
redux "^4.0.0"
|
||||
|
||||
"@types/react@*", "@types/react@^18.2.33":
|
||||
version "18.2.33"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.33.tgz#055356243dc4350a9ee6c6a2c07c5cae12e38877"
|
||||
integrity sha512-v+I7S+hu3PIBoVkKGpSYYpiBT1ijqEzWpzQD62/jm4K74hPpSP7FF9BnKG6+fg2+62weJYkkBWDJlZt5JO/9hg==
|
||||
dependencies:
|
||||
"@types/prop-types" "*"
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.5.tgz#4751153abbf8d6199babb345a52e1eb4167d64af"
|
||||
integrity sha512-s/FPdYRmZR8SjLWGMCuax7r3qCWQw9QKHzXVukAuuIJkXkDRwp+Pu5LMIVFi0Fxbav35WURicYr8u1QsoybnQw==
|
||||
|
||||
array-move@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/array-move/-/array-move-3.0.1.tgz#179645cc0987b65953a4fc06b6df9045e4ba9618"
|
||||
integrity sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==
|
||||
|
||||
array-move@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/array-move/-/array-move-4.0.0.tgz#2c3730f056cc926f62a59769a5a8cda2fb6d8c55"
|
||||
integrity sha512-+RY54S8OuVvg94THpneQvFRmqWdAHeqtMzgMW6JNurHxe8rsS07cHQdfGkXnTUXiBcyZ0j3SiDIxxj0RPiqCkQ==
|
||||
|
||||
css-box-model@^1.2.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.1.tgz#59951d3b81fd6b2074a62d49444415b0d2b4d7c1"
|
||||
integrity sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==
|
||||
dependencies:
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
csstype@^3.0.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b"
|
||||
integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==
|
||||
|
||||
dnd-core@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-16.0.1.tgz#a1c213ed08961f6bd1959a28bb76f1a868360d19"
|
||||
integrity sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==
|
||||
dependencies:
|
||||
"@react-dnd/asap" "^5.0.1"
|
||||
"@react-dnd/invariant" "^4.0.1"
|
||||
redux "^4.2.0"
|
||||
|
||||
esbuild-android-64@0.15.14:
|
||||
version "0.15.14"
|
||||
resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.14.tgz#114e55b0d58fb7b45d7fa3d93516bd13fc8869cc"
|
||||
|
@ -140,12 +237,199 @@ esbuild@^0.15.14:
|
|||
esbuild-windows-64 "0.15.14"
|
||||
esbuild-windows-arm64 "0.15.14"
|
||||
|
||||
fast-deep-equal@^3.1.3:
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
dependencies:
|
||||
loose-envify "^1.0.0"
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
|
||||
|
||||
lodash.debounce@^4:
|
||||
version "4.0.8"
|
||||
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||
dependencies:
|
||||
js-tokens "^3.0.0 || ^4.0.0"
|
||||
|
||||
memoize-one@^5.1.1:
|
||||
version "5.2.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.2.1.tgz#8337aa3c4335581839ec01c3d594090cebe8f00e"
|
||||
integrity sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==
|
||||
|
||||
object-assign@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
|
||||
|
||||
prop-types@^15.5.7, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
object-assign "^4.1.1"
|
||||
react-is "^16.13.1"
|
||||
|
||||
raf-schd@^4.0.2:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.3.tgz#5d6c34ef46f8b2a0e880a8fcdb743efc5bfdbc1a"
|
||||
integrity sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==
|
||||
|
||||
react-beautiful-dnd@^13.1.1:
|
||||
version "13.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz#b0f3087a5840920abf8bb2325f1ffa46d8c4d0a2"
|
||||
integrity sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
css-box-model "^1.2.0"
|
||||
memoize-one "^5.1.1"
|
||||
raf-schd "^4.0.2"
|
||||
react-redux "^7.2.0"
|
||||
redux "^4.0.4"
|
||||
use-memo-one "^1.1.1"
|
||||
|
||||
react-debounce-input@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/react-debounce-input/-/react-debounce-input-3.3.0.tgz#85e3ebcaa41f2016e50613134a1ec9fe3cdb422e"
|
||||
integrity sha512-VEqkvs8JvY/IIZvh71Z0TC+mdbxERvYF33RcebnodlsUZ8RSgyKe2VWaHXv4+/8aoOgXLxWrdsYs2hDhcwbUgA==
|
||||
dependencies:
|
||||
lodash.debounce "^4"
|
||||
prop-types "^15.8.1"
|
||||
|
||||
react-dnd-html5-backend@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz#87faef15845d512a23b3c08d29ecfd34871688b6"
|
||||
integrity sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==
|
||||
dependencies:
|
||||
dnd-core "^16.0.1"
|
||||
|
||||
react-dnd@^16.0.1:
|
||||
version "16.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-16.0.1.tgz#2442a3ec67892c60d40a1559eef45498ba26fa37"
|
||||
integrity sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==
|
||||
dependencies:
|
||||
"@react-dnd/invariant" "^4.0.1"
|
||||
"@react-dnd/shallowequal" "^4.0.1"
|
||||
dnd-core "^16.0.1"
|
||||
fast-deep-equal "^3.1.3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
|
||||
react-dom@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
|
||||
integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
scheduler "^0.23.0"
|
||||
|
||||
react-easy-sort@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/react-easy-sort/-/react-easy-sort-1.6.0.tgz#b40cce827913f0640c1b2e5438dd4d007e26db32"
|
||||
integrity sha512-zd9Nn90wVlZPEwJrpqElN87sf9GZnFR1StfjgNQVbSpR5QTSzCHjEYK6REuwq49Ip+76KOMSln9tg/ST2KLelg==
|
||||
dependencies:
|
||||
array-move "^3.0.1"
|
||||
tslib "2.0.1"
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
react-is@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
|
||||
react-redux@^7.2.0:
|
||||
version "7.2.9"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.9.tgz#09488fbb9416a4efe3735b7235055442b042481d"
|
||||
integrity sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.15.4"
|
||||
"@types/react-redux" "^7.1.20"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^17.0.2"
|
||||
|
||||
react-sortable-hoc@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/react-sortable-hoc/-/react-sortable-hoc-2.0.0.tgz#f6780d8aa4b922a21f3e754af542f032677078b7"
|
||||
integrity sha512-JZUw7hBsAHXK7PTyErJyI7SopSBFRcFHDjWW5SWjcugY0i6iH7f+eJkY8cJmGMlZ1C9xz1J3Vjz0plFpavVeRg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.2.0"
|
||||
invariant "^2.2.4"
|
||||
prop-types "^15.5.7"
|
||||
|
||||
react@^18.2.0:
|
||||
version "18.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
|
||||
integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
reconnecting-websocket@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/reconnecting-websocket/-/reconnecting-websocket-4.4.0.tgz#3b0e5b96ef119e78a03135865b8bb0af1b948783"
|
||||
integrity sha512-D2E33ceRPga0NvTDhJmphEgJ7FUYF0v4lr1ki0csq06OdlxKfugGzN0dSkxM/NfqCxYELK4KcaTOUOjTV6Dcng==
|
||||
|
||||
redux@^4.0.0, redux@^4.0.4, redux@^4.2.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
|
||||
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.9.2"
|
||||
|
||||
regenerator-runtime@^0.14.0:
|
||||
version "0.14.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45"
|
||||
integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==
|
||||
|
||||
scheduler@^0.23.0:
|
||||
version "0.23.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
|
||||
integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
sortablejs@^1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.0.tgz#53230b8aa3502bb77a29e2005808ffdb4a5f7e2a"
|
||||
integrity sha512-bv9qgVMjUMf89wAvM6AxVvS/4MX3sPeN0+agqShejLU5z5GX4C75ow1O2e5k4L6XItUyAK3gH6AxSbXrOM5e8w==
|
||||
|
||||
tiny-invariant@^1.0.6:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.3.1.tgz#8560808c916ef02ecfd55e66090df23a4b7aa642"
|
||||
integrity sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==
|
||||
|
||||
tslib@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e"
|
||||
integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==
|
||||
|
||||
use-memo-one@^1.1.1:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.3.tgz#2fd2e43a2169eabc7496960ace8c79efef975e99"
|
||||
integrity sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==
|
||||
|
|
|
@ -10,90 +10,7 @@
|
|||
<link rel="icon" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABmJLR0QA/wD/AP+gvaeTAAACNklEQVQ4jaWSy0vUcRTFP/f7G4cUUqzAZtQsgzYWiDMjjYgotbOtYLSIFlKBix7YH9DWXQaRFRREi8hN9MCFPZDUzB4IUkRo5Oho1CLTcEbne1o0UWCI0FndC+cezr33wH/C/m5mkvFOw04JIsBrmXVVDI0OryfgfhepZH0bWA/wFaMLqDXp/nQyuWVDAhi1wIz36k5n/BXgMVAamI+vJxD6o7TS61Uw7QLbEXH+OGIOQzmtTG7IgVfBPjOdE6oXdtrQd8mOVQ6/+vCbM9XcvGmipia8xsFsLLZN6DBQZWIRKJPZISd5gLFYrCBaYF3KLB0IFxdVzyQTZYZukNXZEEA2CDaH8SUS5zHbBVZo+AE5OzrXEL+eyxHxZo2IyzL/FqywcnhsFCCktrYgnZrqWc35nsCCqHM2mJMWkXuD+RVPUIvzRxB3K0Ze3F5zg8/pj1XCEkEQ9OHoZnH5uaFAYfXjrcmhUkPvyit39v7zC9lcKO1Y3QoEwOD28fEl4CRAqiFxJyddAHuUnpwsnW9MFIdW3JeMW41Ghl++N/AOMuX5YTCqlU/nbCxWZLDXQRfQRNiNr+Z0L+OUFHZwNhm/CRBaWMh8Ki0u+iYoQeyZ3Z+4lcKeytSBqPOyZ2a+X1grWESoE6wFWAZwNRMTWaEzBk/yLtrNdAmow7gaHRm96HEpYCC/divGjGEdv+h5qLk5lM4utUvWAvxw8g8iI2MPAeYbE9VeFMrbNZl2GzoRHRrrWy+hG8ZPg13xb+XS+CIAAAAASUVORK5CYII=">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2 class="bot">Playlists</h2>
|
||||
|
||||
<section>
|
||||
<div id="items" class="item-container">
|
||||
{{ range .Playlists }}
|
||||
<div class="item" data-id="{{ .Id }}">{{ .Title }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked stop" data-id="reset">Stop</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="inputplaylist" class="input-container">
|
||||
<input class="u-full-width" name="title" type="text" placeholder="Enter name..">
|
||||
<input class="u-full-width" name="url" type="text" placeholder="https://youtube.com/playlist?list=...">
|
||||
<input id="addplaylist" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<p id="output"></p>
|
||||
|
||||
<div id="info">
|
||||
<a id="link" style="text-decoration: none;">
|
||||
<span id="channel"></span>
|
||||
<span> - </span>
|
||||
<span id="title"></span>
|
||||
</a>
|
||||
|
||||
<div class="controls">
|
||||
<input id="prev" name="prev" type="button" value="prev">
|
||||
<span id="time"></span>
|
||||
<input id="next" name="next" type="button" value="next">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<h2 class="bot">Ambiance</h2>
|
||||
|
||||
<section>
|
||||
<div id="ambiance" class="item-container">
|
||||
{{ range .Ambiance }}
|
||||
<div class="item drag" data-id="{{ .Id }}">{{ .Title }}</div>
|
||||
{{ end}}
|
||||
<div class="item locked stop" data-id="reset">Stop</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="progressambiance" class="input-container">
|
||||
<progress max="100" value="0" style="display:none">0%</progress>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="inputambiance" class="input-container">
|
||||
<input class="u-full-width" name="title" type="text" placeholder="Enter name..">
|
||||
<input class="u-full-width" name="url" type="text" placeholder="Enter url...">
|
||||
<input id="addambiance" name="submit" value="Add" type="submit">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<div id="volume_playlist" class="input-container">
|
||||
<label for="playlist-volume">Playlist</label>
|
||||
<input type="range" id="playlist-volume" min="-10" max="4" step="0.1">
|
||||
<input id="playlist-volume-number" type="number" min="-10" max="4" step="0.1" style="width:50px" />
|
||||
</div>
|
||||
<div id="volume_ambiance" class="input-container">
|
||||
<label for="ambiance-volume">Ambiance</label>
|
||||
<input type="range" id="ambiance-volume" min="-10" max="4" step="0.1">
|
||||
<input id="ambiance-volume-number" type="number" min="-10" max="4" step="0.1" style="width:50px" />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- The Modal -->
|
||||
<div id="waiting" class="modal">
|
||||
<!-- Modal content -->
|
||||
<div class="modal-content">
|
||||
<p>Waiting for bot..</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script src="/public/script.js"></script>
|
||||
<div id="content" class="container"></div>
|
||||
<script src="/public/script.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
Loading…
Reference in New Issue