Compare commits
No commits in common. "705b1f5e1242c4fb0e92d91f644a4adf915b44c6" and "b5a0d8bda291aa721e3ce72969242e5012a3f078" have entirely different histories.
705b1f5e12
...
b5a0d8bda2
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -25,18 +25,3 @@
|
||||||
.App-menu {
|
.App-menu {
|
||||||
max-width: 98%;
|
max-width: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Footer {
|
|
||||||
max-width: 98%;
|
|
||||||
background-color: #6207a4 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Footer a {
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.FooterCell {
|
|
||||||
border: none !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import logo from './../../logo.svg';
|
import logo from './../../logo.svg';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
import Container from '@material-ui/core/Container';
|
||||||
import Typography from '@material-ui/core/Typography';
|
import Typography from '@material-ui/core/Typography';
|
||||||
import { Button, Paper, Container, AppBar, Table, TableBody, TableCell, TableContainer, TableRow } from '@material-ui/core';
|
import { Button, Paper } from '@material-ui/core';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const client_id = "sau3e70wvs369jw1u25ex8g3cve599"
|
const client_id = "sau3e70wvs369jw1u25ex8g3cve599"
|
||||||
|
@ -43,22 +44,6 @@ function App() {
|
||||||
<Typography component="p" gutterBottom>
|
<Typography component="p" gutterBottom>
|
||||||
...Coming "Soon"(tm)
|
...Coming "Soon"(tm)
|
||||||
</Typography>
|
</Typography>
|
||||||
<AppBar position="static" className="Footer">
|
|
||||||
<TableContainer component={Container}>
|
|
||||||
<Table><TableBody>
|
|
||||||
<TableRow className="FooterRow">
|
|
||||||
<TableCell className="FooterCell"><a href="https://discord.gg/sVgZeRt">Discord</a></TableCell>
|
|
||||||
<TableCell className="FooterCell"><a href="https://git.martyn.berlin/martyn/twitchsingstools">Source Code</a></TableCell>
|
|
||||||
<TableCell className="FooterCell"><a href="https://twitch.tv/iMartynOnTwitch">Martyn's Twitch</a></TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow className="FooterRow">
|
|
||||||
<TableCell className="FooterCell">Announcements, Support, Optional notifications</TableCell>
|
|
||||||
<TableCell className="FooterCell">Geek out at the really hacky source code from an SRE type person</TableCell>
|
|
||||||
<TableCell className="FooterCell">Streaming and singing is my hobby, not my job, so only if you want to...</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableBody></Table>
|
|
||||||
</TableContainer>
|
|
||||||
</AppBar>
|
|
||||||
</Paper>
|
</Paper>
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
|
|
@ -26,18 +26,3 @@
|
||||||
.App-menu {
|
.App-menu {
|
||||||
max-width: 98%;
|
max-width: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Footer {
|
|
||||||
max-width: 98%;
|
|
||||||
background-color: #6207a4 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.Footer a {
|
|
||||||
color: white;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
|
|
||||||
.FooterCell {
|
|
||||||
border: none !important;
|
|
||||||
color: white !important;
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ import Typography from '@material-ui/core/Typography';
|
||||||
import TopTenSongs from "../TopTenSongs/TopTenSongs";
|
import TopTenSongs from "../TopTenSongs/TopTenSongs";
|
||||||
import TopTenSingers from "../TopTenSingers/TopTenSingers";
|
import TopTenSingers from "../TopTenSingers/TopTenSingers";
|
||||||
import DuetData from "../DuetData/DuetData";
|
import DuetData from "../DuetData/DuetData";
|
||||||
import CacheDeets from "../CacheDeets/CacheDeets";
|
|
||||||
import BotDeets from "../BotDeets/BotDeets";
|
|
||||||
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@material-ui/core';
|
import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow } from '@material-ui/core';
|
||||||
import ProblemContainer from "../ProblemContainer/ProblemContainer";
|
import ProblemContainer from "../ProblemContainer/ProblemContainer";
|
||||||
|
|
||||||
|
@ -42,159 +40,82 @@ function a11yProps(index) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppAdmin extends React.Component {
|
function AppAdmin() {
|
||||||
constructor(props) {
|
const [page, setPage] = React.useState(0);
|
||||||
super(props);
|
if (
|
||||||
this.setPage = this.setPage.bind(this);
|
(window.location.href.split("/").length < 6) ||
|
||||||
this.reloadSongComponent = this.reloadSongComponent.bind(this);
|
(window.location.href.split("/")[3].search(/^admin/) < 0) ||
|
||||||
this.reloadSingersComponent = this.reloadSingersComponent.bind(this);
|
(window.location.href.split("/")[5].length !== 48)
|
||||||
this.reloadDuetComponent = this.reloadDuetComponent.bind(this);
|
) {
|
||||||
this.state = {
|
|
||||||
page: 0,
|
|
||||||
reloadSongComponent: false,
|
|
||||||
reloadSingersComponent: false,
|
|
||||||
reloadDuetComponent: false,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
setPage(pageNum) {
|
|
||||||
let newState = this.state;
|
|
||||||
newState.page = pageNum;
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadSongComponent(components) {
|
|
||||||
let newState = this.state;
|
|
||||||
newState.reloadComponents = components;
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadSingersComponent(components) {
|
|
||||||
let newState = this.state;
|
|
||||||
newState.reloadComponents = components;
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
reloadDuetComponent(components) {
|
|
||||||
let newState = this.state;
|
|
||||||
newState.reloadDuetComponent = components;
|
|
||||||
console.log("I'm trying!")
|
|
||||||
this.setState(newState);
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const page = this.state.page
|
|
||||||
if (
|
|
||||||
(window.location.href.split("/").length < 6) ||
|
|
||||||
(window.location.href.split("/")[3].search(/^admin/) < 0) ||
|
|
||||||
(window.location.href.split("/")[5].length !== 48)
|
|
||||||
) {
|
|
||||||
if (window.location.href.split("/")[2] !== "192.168.1.111:3000") {
|
|
||||||
return (
|
|
||||||
<ProblemContainer />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const channelName = window.location.href.split("/")[4]
|
|
||||||
const adminToken = window.location.href.split("/")[5]
|
|
||||||
const csvURL = "/csv/"+ channelName + "/" + adminToken
|
|
||||||
const tsvURL = "/tsv/"+ channelName + "/" + adminToken
|
|
||||||
const setPage = this.setPage
|
|
||||||
const reloadDuetComponent = this.state.reloadDuetComponent
|
|
||||||
const reloadSongComponent = this.state.reloadSongComponent
|
|
||||||
const reloadSingersComponent = this.state.reloadSingersComponent
|
|
||||||
|
|
||||||
function handleChange(event, newValue) {
|
|
||||||
setPage(newValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className="App">
|
<ProblemContainer />
|
||||||
<Paper>
|
)
|
||||||
<img src={logo} className="App-logo" alt="logo" />
|
|
||||||
<Typography variant="h4" component="h1" gutterBottom>
|
|
||||||
Twitch Sings Tools
|
|
||||||
</Typography>
|
|
||||||
<AppBar position="static" className="App-menu">
|
|
||||||
<Tabs value={page} onChange={handleChange} aria-label="simple tabs example">
|
|
||||||
<Tab label="Top Songs" {...a11yProps(0)}/>
|
|
||||||
<Tab label="Top Singers" {...a11yProps(1)}/>
|
|
||||||
<Tab label="Data" {...a11yProps(2)}/>
|
|
||||||
<Tab label="Export" {...a11yProps(3)}/>
|
|
||||||
<Tab label="Cache Details" {...a11yProps(4)}/>
|
|
||||||
<Tab label="Chatbot" {...a11yProps(4)}/>
|
|
||||||
</Tabs>
|
|
||||||
</AppBar>
|
|
||||||
<TabPanel value={page} index={0}>
|
|
||||||
<TopTenSongs reloadSongComponent={reloadSongComponent}
|
|
||||||
onReloadedChange={this.reloadSongComponent}/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={page} index={1}>
|
|
||||||
<TopTenSingers reloadSingersComponent={reloadSingersComponent}
|
|
||||||
onReloadedChange={this.reloadSingersComponent}/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={page} index={2}>
|
|
||||||
<DuetData reloadDuetComponent={reloadDuetComponent}
|
|
||||||
onReloadedChange={this.reloadDuetComponent}/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={page} index={3}>
|
|
||||||
<Typography variant="h5" component="h2" gutterBottom>
|
|
||||||
Download your data as :
|
|
||||||
</Typography>
|
|
||||||
<TableContainer component={Paper}>
|
|
||||||
<Table>
|
|
||||||
<TableHead>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell>TSV</TableCell>
|
|
||||||
<TableCell>CSV</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableHead>
|
|
||||||
<TableBody>
|
|
||||||
<TableRow>
|
|
||||||
<TableCell><a href={tsvURL}>here</a></TableCell>
|
|
||||||
<TableCell><a href={csvURL}>here</a></TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableBody>
|
|
||||||
</Table>
|
|
||||||
</TableContainer>
|
|
||||||
<Typography variant="h5" component="h2" gutterBottom>
|
|
||||||
Note: Excel is not very good at handling CSV format it seems...
|
|
||||||
</Typography>
|
|
||||||
<Typography component="p" gutterBottom>It is important to "Import Data" not "Open" the csv in many cases (8 year old discussion of this behaviour <a href="https://superuser.com/questions/407082/easiest-way-to-open-csv-with-commas-in-excel">here</a>) - from that post the instructions are :</Typography>
|
|
||||||
<Typography component="q" gutterBottom>In Excel, DATA tab, in the Get External Data subsection, click "From Text" and import your CSV in the Wizard.</Typography>
|
|
||||||
<Typography component="p" gutterBottom>LibreOffice calc kinda just works...just sayin' ;-)</Typography>
|
|
||||||
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={page} index={4}>
|
|
||||||
<CacheDeets
|
|
||||||
onReloadSongsChange={this.reloadSongComponent}
|
|
||||||
onReloadSingersChange={this.reloadSingersComponent}
|
|
||||||
onReloadDuetChange={this.reloadDuetComponent}
|
|
||||||
/>
|
|
||||||
</TabPanel>
|
|
||||||
<TabPanel value={page} index={5}>
|
|
||||||
<BotDeets />
|
|
||||||
</TabPanel>
|
|
||||||
<AppBar position="static" className="Footer">
|
|
||||||
<TableContainer component={Container}>
|
|
||||||
<Table><TableBody>
|
|
||||||
<TableRow className="FooterRow">
|
|
||||||
<TableCell className="FooterCell"><a href="https://discord.gg/sVgZeRt">Discord</a></TableCell>
|
|
||||||
<TableCell className="FooterCell"><a href="https://git.martyn.berlin/martyn/twitchsingstools">Source Code</a></TableCell>
|
|
||||||
<TableCell className="FooterCell"><a href="https://twitch.tv/iMartynOnTwitch">Martyn's Twitch</a></TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow className="FooterRow">
|
|
||||||
<TableCell className="FooterCell">Announcements, Support, Optional notifications</TableCell>
|
|
||||||
<TableCell className="FooterCell">Geek out at the really hacky source code from an SRE type person</TableCell>
|
|
||||||
<TableCell className="FooterCell">Streaming and singing is my hobby, not my job, so only if you want to...</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</TableBody></Table>
|
|
||||||
</TableContainer>
|
|
||||||
</AppBar>
|
|
||||||
</Paper>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const channelName = window.location.href.split("/")[4]
|
||||||
|
const adminToken = window.location.href.split("/")[5]
|
||||||
|
const csvURL = "/csv/"+ channelName + "/" + adminToken
|
||||||
|
const tsvURL = "/tsv/"+ channelName + "/" + adminToken
|
||||||
|
|
||||||
|
function handleChange(event, newValue) {
|
||||||
|
setPage(newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Container className="App">
|
||||||
|
<Paper>
|
||||||
|
<img src={logo} className="App-logo" alt="logo" />
|
||||||
|
<Typography variant="h4" component="h1" gutterBottom>
|
||||||
|
Twitch Sings Tools
|
||||||
|
</Typography>
|
||||||
|
<AppBar position="static" className="App-menu">
|
||||||
|
<Tabs value={page} onChange={handleChange} aria-label="simple tabs example">
|
||||||
|
<Tab label="Top Songs" {...a11yProps(0)}/>
|
||||||
|
<Tab label="Top Singers" {...a11yProps(1)}/>
|
||||||
|
<Tab label="Data" {...a11yProps(2)}/>
|
||||||
|
<Tab label="Export" {...a11yProps(3)}/>
|
||||||
|
</Tabs>
|
||||||
|
</AppBar>
|
||||||
|
<TabPanel value={page} index={0}>
|
||||||
|
<TopTenSongs />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={page} index={1}>
|
||||||
|
<TopTenSingers />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={page} index={2}>
|
||||||
|
<DuetData />
|
||||||
|
</TabPanel>
|
||||||
|
<TabPanel value={page} index={3}>
|
||||||
|
<Typography variant="h5" component="h2" gutterBottom>
|
||||||
|
Download your data as :
|
||||||
|
</Typography>
|
||||||
|
<TableContainer component={Paper}>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>TSV</TableCell>
|
||||||
|
<TableCell>CSV</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell><a href={tsvURL}>here</a></TableCell>
|
||||||
|
<TableCell><a href={csvURL}>here</a></TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
<Typography variant="h5" component="h2" gutterBottom>
|
||||||
|
Note: Excel is not very good at handling CSV format it seems...
|
||||||
|
</Typography>
|
||||||
|
<Typography component="p" gutterBottom>It is important to "Import Data" not "Open" the csv in many cases (8 year old discussion of this behaviour <a href="https://superuser.com/questions/407082/easiest-way-to-open-csv-with-commas-in-excel">here</a>) - from that post the instructions are :</Typography>
|
||||||
|
<Typography component="q" gutterBottom>In Excel, DATA tab, in the Get External Data subsection, click "From Text" and import your CSV in the Wizard.</Typography>
|
||||||
|
<Typography component="p" gutterBottom>LibreOffice calc kinda just works...just sayin' ;-)</Typography>
|
||||||
|
|
||||||
|
</TabPanel>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AppAdmin;
|
export default AppAdmin;
|
||||||
|
|
|
@ -1,53 +0,0 @@
|
||||||
import React, { useEffect } from 'react';
|
|
||||||
import { Container, Typography, Button } from '@material-ui/core';
|
|
||||||
|
|
||||||
const channelName = window.location.href.split("/")[4]
|
|
||||||
const adminToken = window.location.href.split("/")[5]
|
|
||||||
const dataURL = "/botdeets/"+ channelName + "/" + adminToken
|
|
||||||
|
|
||||||
function BotDeets() {
|
|
||||||
|
|
||||||
const [botState, setBotState] = React.useState({loading: true, botData: []})
|
|
||||||
useEffect(() => {
|
|
||||||
// We should only fetch once!
|
|
||||||
if (botState.loading) {
|
|
||||||
fetch(dataURL).then((res) => res.json().then((data)=>{
|
|
||||||
setBotState({botData: data, loading: false })
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}, [setBotState, botState.loading]);
|
|
||||||
|
|
||||||
if (botState.loading) {
|
|
||||||
return <p>Sorry, still loading...</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
let dd = botState.botData
|
|
||||||
if (dd.hasleft) {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Typography variant="h5" component="h2" gutterBottom>
|
|
||||||
The chatbot is currently <b>not</b> in your channel. To change the settings, invite it to your channel using the button below :
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="p">
|
|
||||||
You really don't wanna do that, because it's not ready yet.
|
|
||||||
</Typography>
|
|
||||||
<Typography variant="p">
|
|
||||||
<Button variant="contained" color="primary">Join my channel!</Button>
|
|
||||||
</Typography>
|
|
||||||
</Container>
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Typography variant="h5" component="h2" gutterBottom>
|
|
||||||
Coming soon! Bot control panel!
|
|
||||||
</Typography>
|
|
||||||
<Typography component="tt" gutterBottom>
|
|
||||||
{dd}
|
|
||||||
</Typography>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default BotDeets;
|
|
|
@ -1,81 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import { Container, Typography, Button } from '@material-ui/core';
|
|
||||||
|
|
||||||
const channelName = window.location.href.split("/")[4]
|
|
||||||
const adminToken = window.location.href.split("/")[5]
|
|
||||||
const dataURL = "/cachedeets/"+ channelName + "/" + adminToken
|
|
||||||
const forceURL = "/force/"+ channelName + "/" + adminToken
|
|
||||||
|
|
||||||
class CacheDeets extends React.Component {
|
|
||||||
constructor(props) {
|
|
||||||
super(props);
|
|
||||||
this.state = {
|
|
||||||
cacheData: [],
|
|
||||||
loading: true
|
|
||||||
};
|
|
||||||
this.setCacheData = this.setCacheData.bind(this);
|
|
||||||
this.setLoading = this.setLoading.bind(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
setCacheData(data) {
|
|
||||||
let newState = this.state
|
|
||||||
newState.cacheData = data
|
|
||||||
this.setState(newState)
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(data) {
|
|
||||||
let newState = this.state
|
|
||||||
newState.loading = data
|
|
||||||
this.setState(newState)
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
// We should only fetch once!
|
|
||||||
if (this.state.loading) {
|
|
||||||
let actualURL = dataURL
|
|
||||||
if (window.location.href.split("/")[2] === "192.168.1.111:3000") {
|
|
||||||
//Frontend dev mode only
|
|
||||||
actualURL = "/sampleData/cache.json"
|
|
||||||
}
|
|
||||||
fetch(actualURL).then((res) => res.json().then((data)=>{
|
|
||||||
this.setLoading(false)
|
|
||||||
this.setCacheData(data)
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const loading = this.state.loading
|
|
||||||
const setLoading = this.setLoading
|
|
||||||
const cacheData = this.state.cacheData
|
|
||||||
|
|
||||||
function handleClick(e) {
|
|
||||||
setLoading(true)
|
|
||||||
fetch(forceURL).then((data)=>{
|
|
||||||
setLoading(false)
|
|
||||||
window.location.reload()
|
|
||||||
})
|
|
||||||
console.log('The link was clicked.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return <p>Sorry, still loading...</p>
|
|
||||||
}
|
|
||||||
|
|
||||||
let dd = cacheData
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Container>
|
|
||||||
<Typography variant="h5" component="h2" gutterBottom>
|
|
||||||
Server has cached data from <b>{dd.SongCount} published</b> performances
|
|
||||||
</Typography>
|
|
||||||
<Typography component="p" gutterBottom>
|
|
||||||
Cache data is from about {dd.AgeStr} - it automatically gets updated if it's older than an hour. If you're convinced using Martyn's bandwidth to refresh the cache earlier than that is worth it, here's the button below :
|
|
||||||
</Typography>
|
|
||||||
<Button variant="contained" color="primary" onClick={handleClick}>Force refresh cache</Button>
|
|
||||||
</Container>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default CacheDeets;
|
|
Loading…
Reference in New Issue