1
0
mirror of https://github.com/mgerb/go-discord-bot synced 2026-01-14 02:32:48 +00:00

UI Overhaul

This commit is contained in:
2018-08-19 18:17:40 -05:00
parent 5fedcd4b40
commit e593472c84
58 changed files with 633 additions and 685 deletions

View File

@@ -1 +0,0 @@
export * from './Clips';

View File

@@ -1,48 +0,0 @@
import React from 'react';
import './Home.scss';
interface Props {}
interface State {}
export class Home extends React.Component<Props, State> {
render() {
return (
<div className="content">
<div className="card">
<div className="card__header">Go Discord Bot</div>
<h3>04-09-18 Update</h3>
<ul>
<li>pubg stats no longer updated on this site</li>
<li>client dependencies all updated (including webpack 4 and react router 4)</li>
</ul>
<h3>Audio Clipping</h3>
<p>
<em>NEW:</em> Audio clipping now supported! Try it out with the <code>clip</code> command!
</p>
<h3>PUBG Stats</h3>
<p>PUBG stats are pulled from the score API.</p>
<h3>Youtube Downloader</h3>
<p>Convert Youtube URL's to MP3 files.</p>
<h3>Soundboard Upload</h3>
<p>Drag and drop files to upload. Sounds can be played in discord by typing the commands on the next page.</p>
<p>
Check out the source code on
<a href="https://github.com/mgerb/GoBot" target="_blank">
{' '}
GitHub
<i className="fa fa-github" aria-hidden="true" />
</a>
</p>
</div>
</div>
);
}
}

View File

@@ -1,12 +0,0 @@
import React from 'react';
import './NotFound.scss';
interface Props {}
interface State {}
export class NotFound extends React.Component<Props, State> {
render() {
return <div className="NotFound">404 Not Found</div>;
}
}

View File

@@ -1,33 +0,0 @@
@import '../../scss/variables';
.pubg__container {
padding: 10px;
}
.pubg__table {
border-collapse: collapse;
width: 100%;
text-align: left;
margin-top: 20px;
tr + tr {
border-top: 1px solid $gray3;
}
td,
th {
padding: 5px;
}
}
.pubg__button-row {
margin-bottom: 10px;
.button {
min-width: 100px;
& + .button {
margin-left: 5px;
}
}
}

View File

@@ -1,159 +0,0 @@
/**
* DEPRECATED
*/
import React from 'react';
import { axios } from '../../services';
import * as _ from 'lodash';
import './Pubg.scss';
interface Props {}
interface State {
players: Player[];
selectedRegion: string;
selectedMatch: string;
statList: string[];
}
interface Player {
PlayerName: string;
agg?: any;
as?: any;
na?: any;
sa?: any;
}
export class Pubg extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
players: [],
selectedRegion: 'agg',
selectedMatch: 'squad',
statList: [],
};
}
componentDidMount() {
axios.get('/api/stats/pubg').then(res => {
this.setState({
players: _.map(res.data) as any,
});
this.setStatList();
});
}
// get stat list
setStatList() {
// hacky way to find existing content -- to tired to make it pretty
let i = 0;
let stats;
while (!stats) {
if (i > this.state.players.length) {
return;
}
stats = _.find(
_.get(this.state, `players[${i}].Stats`),
(s: any) => s.Match === this.state.selectedMatch.toLowerCase(),
);
i++;
}
if (stats) {
this.setState({
statList: _.sortBy(_.map(stats.Stats, 'field')) as any,
});
}
}
insertRows(): any {
return this.state.statList.map((val: any, index: any) => {
return (
<tr key={index}>
<td>{val}</td>
{this.state.players.map((player: any, i: number) => {
// find player stats for field
let playerStat = _.find(player.Stats, (p: any) => {
return (
p.Match === this.state.selectedMatch.toLowerCase() &&
p.Region === this.state.selectedRegion.toLowerCase()
);
});
return (
<td key={i}>{_.get(_.find(_.get(playerStat, 'Stats'), (p: any) => p.field === val), 'displayValue')}</td>
);
})}
</tr>
);
});
}
buttonRegion(title: string) {
let lowerTitle = title === 'All' ? 'agg' : title.toLowerCase();
return (
<button
className={`button ${lowerTitle === this.state.selectedRegion ? 'button--primary' : ''}`}
onClick={() => {
this.setState({ selectedRegion: lowerTitle });
this.setStatList();
}}
>
{title}
</button>
);
}
buttonMatch(title: string) {
let lowerTitle = title.toLowerCase();
return (
<button
className={`button ${lowerTitle === this.state.selectedMatch ? 'button--primary' : ''}`}
onClick={() => {
this.setState({ selectedMatch: lowerTitle });
this.setStatList();
}}
>
{title}
</button>
);
}
render() {
return (
<div className="pubg__container">
<div className="card" style={{ maxWidth: 'initial' }}>
<div className="card__header">PUBG Stats</div>
<div className="pubg__button-row">
{this.buttonMatch('Solo')}
{this.buttonMatch('Duo')}
{this.buttonMatch('Squad')}
</div>
<div className="pubg__button-row">
{this.buttonRegion('All')}
{this.buttonRegion('Na')}
{this.buttonRegion('As')}
{this.buttonRegion('Au')}
</div>
<table className="pubg__table">
<tbody>
<tr>
<th />
{this.state.players.map((val: any, index: number) => {
return <th key={index}>{val.PlayerName}</th>;
})}
</tr>
{this.insertRows()}
</tbody>
</table>
</div>
</div>
);
}
}

View File

@@ -1,40 +0,0 @@
@import '../../scss/variables';
.Soundboard {
display: flex;
padding: 10px;
}
.Soundboard__column {
flex: 1;
}
.Soundboard__input {
display: block;
width: 200px;
margin-bottom: 10px;
margin-right: auto;
margin-left: auto;
}
.Dropzone {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 2px solid $primaryBlue;
border-radius: 1em;
padding: 20px;
margin-right: auto;
margin-left: auto;
color: $lightGray;
width: 400px;
height: 400px;
background-color: $gray2;
transition: box-shadow 0.1s linear, background-color 0.1s linear;
}
.Dropzone--active {
background-color: $gray3;
box-shadow: 0px 0px 5px 1px $primaryBlue;
}

View File

@@ -1,130 +0,0 @@
import React from 'react';
import Dropzone from 'react-dropzone';
import { axios } from '../../services';
import { SoundList, SoundType } from '../../components/SoundList';
import './Soundboard.scss';
import { AxiosRequestConfig } from 'axios';
let self: any;
interface Props {}
interface State {
percentCompleted: number;
uploaded: boolean;
uploadError: string;
soundList: SoundType[];
}
export class Soundboard extends React.Component<Props, State> {
private config: AxiosRequestConfig;
private soundListCache: any;
constructor(props: Props) {
super(props);
(this.state = {
percentCompleted: 0,
uploaded: false,
uploadError: ' ',
soundList: [],
}),
(self = this);
}
componentDidMount() {
this.config = {
headers: {
'Content-Type': 'multipart/form-data',
},
onUploadProgress: progressEvent => {
this.setState({
percentCompleted: Math.round(progressEvent.loaded * 100 / progressEvent.total),
});
},
};
this.getSoundList();
}
private getSoundList() {
if (!this.soundListCache) {
axios
.get('/api/soundlist')
.then(response => {
this.soundListCache = response.data;
this.setState({
soundList: response.data,
});
})
.catch((error: any) => {
console.error(error.response.data);
});
} else {
this.setState({
soundList: this.soundListCache,
});
}
}
onDrop(acceptedFiles: any) {
if (acceptedFiles.length > 0) {
self.uploadFile(acceptedFiles[0]);
}
}
uploadFile(file: any) {
let formData = new FormData();
formData.append('name', file.name);
formData.append('file', file);
axios
.post('/api/upload', formData, this.config)
.then(() => {
this.setState({
percentCompleted: 0,
uploaded: true,
uploadError: ' ',
});
this.soundListCache = undefined;
this.getSoundList();
})
.catch(err => {
this.setState({
percentCompleted: 0,
uploaded: false,
uploadError: err.response.data,
});
});
}
render() {
const { soundList } = this.state;
return (
<div className="Soundboard">
<div className="column">
<SoundList soundList={soundList} type="Sounds" />
</div>
<div className="column">
<div>
<Dropzone
className="Dropzone"
activeClassName="Dropzone--active"
onDrop={this.onDrop}
multiple={false}
disableClick={true}
maxSize={10000000000}
accept={'audio/*'}
>
<div style={{ fontSize: '20px' }}>Drop file here to upload.</div>
{this.state.percentCompleted > 0 ? <div>Uploading: {this.state.percentCompleted}</div> : ''}
{this.state.uploaded ? <div style={{ color: 'green' }}>File uploded!</div> : ''}
<div style={{ color: '#f95f59' }}>{this.state.uploadError}</div>
</Dropzone>
</div>
</div>
</div>
);
}
}

View File

@@ -1,8 +1,7 @@
import React from 'react';
import { SoundList, SoundType } from '../../components';
import { axios } from '../../services';
import { SoundList, SoundType } from '../../components/SoundList';
interface Props {}
interface State {
@@ -36,7 +35,7 @@ export class Clips extends React.Component<Props, State> {
render() {
return (
<div className="Soundboard">
<div className="content">
<div className="column">
<SoundList soundList={this.state.clipList} type="Clips" />
</div>

View File

@@ -1,14 +1,10 @@
.Downloader {
padding: 10px;
}
.Downloader__input {
.downloader__input {
width: 100%;
margin-top: 10px;
margin-bottom: 10px;
}
.Downloader__button {
.downloader__button {
& + & {
margin-left: 10px;
}

View File

@@ -1,7 +1,6 @@
import React from 'react';
import { axios } from '../../services';
import './Downloader.scss';
import './downloader.scss';
interface Props {}
@@ -70,13 +69,13 @@ export class Downloader extends React.Component<Props, State> {
render() {
return (
<div className="Downloader">
<div className="content">
<div className="card">
<div className="card__header">Youtube to MP3</div>
<input
placeholder="Enter Youtube URL"
className="input Downloader__input"
className="input downloader__input"
value={this.state.url}
onChange={event => this.setState({ url: event.target.value })}
/>

View File

@@ -0,0 +1,6 @@
export * from './clips/clips';
export * from './downloader/downloader';
export * from './not-found/not-found';
export * from './oauth/oauth';
export * from './soundboard/soundboard';
export * from './stats/stats';

View File

@@ -1,8 +1,7 @@
.NotFound {
.not-found {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
}

View File

@@ -0,0 +1,4 @@
import React from 'react';
import './not-found.scss';
export const NotFound = () => <div className="not-found">404 Not Found</div>;

View File

@@ -1,7 +1,7 @@
import React from 'react';
import { axios, StorageService } from '../../services';
import queryString from 'query-string';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { axios, StorageService } from '../../services';
interface Props extends RouteComponentProps<any> {}

View File

@@ -0,0 +1,66 @@
import React from 'react';
import { SoundList, SoundType, Uploader } from '../../components';
import { axios } from '../../services';
import './soundboard.scss';
interface Props {}
interface State {
percentCompleted: number;
uploaded: boolean;
uploadError: string;
soundList: SoundType[];
}
export class Soundboard extends React.Component<Props, State> {
private soundListCache: any;
constructor(props: Props) {
super(props);
this.state = {
percentCompleted: 0,
uploaded: false,
uploadError: ' ',
soundList: [],
};
}
componentDidMount() {
this.getSoundList();
}
private getSoundList() {
if (!this.soundListCache) {
axios
.get('/api/soundlist')
.then(response => {
this.soundListCache = response.data;
this.setState({
soundList: response.data,
});
})
.catch((error: any) => {
console.error(error.response.data);
});
} else {
this.setState({
soundList: this.soundListCache,
});
}
}
onUploadComplete = () => {
delete this.soundListCache;
this.getSoundList();
};
render() {
const { soundList } = this.state;
return (
<div className="content">
<Uploader onComplete={this.onUploadComplete} />
<SoundList soundList={soundList} type="Sounds" />
</div>
);
}
}

View File

@@ -1,3 +0,0 @@
.Stats {
padding: 10px;
}