1
0
mirror of https://github.com/mgerb/go-discord-bot synced 2026-01-09 16:42:48 +00:00

feat: add player controls to uploaded clips on stats page

This commit is contained in:
2019-08-24 11:12:58 -05:00
parent d29c23d9e4
commit 72ba1e5621
11 changed files with 201 additions and 70 deletions

View File

@@ -0,0 +1,74 @@
import React from 'react';
import { SoundService } from '../../services';
interface IProps {
sound: SoundType;
type: 'sounds' | 'clips';
showDiscordPlay?: boolean;
}
interface IState {}
export interface SoundType {
extension: string;
name: string;
}
export class ClipPlayerControl extends React.Component<IProps, IState> {
checkExtension(extension: string) {
switch (extension) {
case 'wav':
return true;
case 'mp3':
return true;
case 'mpeg':
return true;
default:
return false;
}
}
handlePlayAudioInBrowser(sound: SoundType, type: string) {
const url = `/public/${type.toLowerCase()}/` + sound.name + '.' + sound.extension;
const audio = new Audio(url);
audio.play();
}
onPlayDiscord = (sound: SoundType) => {
SoundService.playSound(sound);
};
render() {
const { sound, showDiscordPlay, type } = this.props;
return (
this.checkExtension(sound.extension) && (
<div className="flex flex--v-center">
<a
href={`/public/${type.toLowerCase()}/` + sound.name + '.' + sound.extension}
download
title="Download"
className="fa fa-download link"
aria-hidden="true"
/>
<i
title="Play in browser"
className="fa fa-play link"
aria-hidden="true"
style={{ paddingLeft: '15px' }}
onClick={() => this.handlePlayAudioInBrowser(sound, type)}
/>
{showDiscordPlay && (
<i
title="Play in discord"
className="fa fa-play-circle link fa-lg"
aria-hidden="true"
style={{ paddingLeft: '10px' }}
onClick={() => this.onPlayDiscord(sound)}
/>
)}
</div>
)
);
}
}

View File

@@ -1,9 +1,10 @@
import React from 'react';
import { ClipPlayerControl } from '../clip-player-control/clip-player-control';
import './sound-list.scss';
interface Props {
soundList: SoundType[];
type: string;
type: 'sounds' | 'clips';
onPlayDiscord?: (sound: SoundType) => void;
showDiscordPlay?: boolean;
}
@@ -46,7 +47,7 @@ export class SoundList extends React.Component<Props, State> {
}
render() {
const { onPlayDiscord, showDiscordPlay, soundList, type } = this.props;
const { showDiscordPlay, soundList, type } = this.props;
return (
<div className="card">
@@ -65,41 +66,7 @@ export class SoundList extends React.Component<Props, State> {
<div key={index} className="sound-list__item">
<div className="text-wrap">{(sound.prefix || '') + sound.name}</div>
{this.checkExtension(sound.extension) && this.state.showAudioControls[index] ? (
<audio
controls
src={`/public/${type.toLowerCase()}/` + sound.name + '.' + sound.extension}
itemType={'audio/' + sound.extension}
style={{ width: '100px' }}
/>
) : (
<div>
<a
href={`/public/${type.toLowerCase()}/` + sound.name + '.' + sound.extension}
download
title="Download"
className="fa fa-download link"
aria-hidden="true"
/>
<i
title="Play in browser"
className="fa fa-play link"
aria-hidden="true"
style={{ paddingLeft: '15px' }}
onClick={() => this.handlePlayAudioInBrowser(sound, type)}
/>
{showDiscordPlay &&
onPlayDiscord && (
<i
title="Play in discord"
className="fa fa-play-circle link fa-lg"
aria-hidden="true"
style={{ paddingLeft: '10px' }}
onClick={() => onPlayDiscord!(sound)}
/>
)}
</div>
)}
<ClipPlayerControl showDiscordPlay={showDiscordPlay} sound={sound} type={type} />
</div>
);
})

View File

@@ -2,12 +2,14 @@ import * as _ from 'lodash';
import { DateTime } from 'luxon';
import React from 'react';
import { ISound } from '../../model';
import { ClipPlayerControl } from '../clip-player-control/clip-player-control';
interface IProps {
sounds: ISound[];
showDiscordPlay?: boolean;
}
export const UploadHistory = ({ sounds }: IProps) => {
export const UploadHistory = ({ sounds, showDiscordPlay }: IProps) => {
const sortedSounds = _.orderBy(sounds, 'created_at', 'desc');
return (
<div className="card">
@@ -15,7 +17,7 @@ export const UploadHistory = ({ sounds }: IProps) => {
<table className="table">
<thead>
<tr>
<th>Date</th>
<th className="hide-tiny">Date</th>
<th>Sound</th>
<th className="hide-tiny">Ext</th>
<th>Username</th>
@@ -27,7 +29,9 @@ export const UploadHistory = ({ sounds }: IProps) => {
const formattedDate = DateTime.fromISO(s.created_at).toLocaleString();
return (
<tr key={i}>
<td title={formattedDate}>{formattedDate}</td>
<td className="hide-tiny" title={formattedDate}>
{formattedDate}
</td>
<td title={s.name}>{s.name}</td>
<td className="hide-tiny" title={s.extension}>
{s.extension}
@@ -36,6 +40,9 @@ export const UploadHistory = ({ sounds }: IProps) => {
<td className="hide-tiny" title={s.user.email}>
{s.user.email}
</td>
<td>
<ClipPlayerControl showDiscordPlay={showDiscordPlay} sound={s} type="sounds"></ClipPlayerControl>
</td>
</tr>
);
})}

View File

@@ -37,7 +37,7 @@ export class Clips extends React.Component<Props, State> {
return (
<div className="content">
<div className="column">
<SoundList soundList={this.state.clipList} type="Clips" />
<SoundList soundList={this.state.clipList} type="clips" />
</div>
</div>
);

View File

@@ -1,7 +1,6 @@
import { inject, observer } from 'mobx-react';
import React from 'react';
import { SoundList, SoundType, Uploader } from '../../components';
import { Permissions } from '../../model';
import { axios, SoundService } from '../../services';
import { AppStore } from '../../stores';
import './soundboard.scss';
@@ -73,9 +72,9 @@ export class Soundboard extends React.Component<Props, State> {
<Uploader onComplete={this.onUploadComplete} />
<SoundList
soundList={soundList}
type="Sounds"
type="sounds"
onPlayDiscord={this.onPlayDiscord}
showDiscordPlay={appStore!.claims && appStore!.claims!.permissions >= Permissions.Mod}
showDiscordPlay={appStore!.hasModPermissions()}
/>
</div>
);

View File

@@ -74,7 +74,7 @@ export class Stats extends Component<IProps, IState> {
},
};
const { claims } = this.props.appStore;
const { claims, hasModPermissions } = this.props.appStore;
return (
<div className="content">
@@ -82,7 +82,7 @@ export class Stats extends Component<IProps, IState> {
<div className="card__header">Posts containing links</div>
<HorizontalBar data={data} />
</div>
{claims && <UploadHistory sounds={this.state.sounds} />}
{claims && <UploadHistory sounds={this.state.sounds} showDiscordPlay={hasModPermissions()} />}
</div>
);
}

View File

@@ -1,6 +1,6 @@
import jwt_decode from 'jwt-decode';
import { action, observable } from 'mobx';
import { IClaims } from '../model';
import { IClaims, Permissions } from '../model';
import { axios, StorageService } from '../services';
import { Util } from '../util';
@@ -40,4 +40,8 @@ export class AppStore {
public toggleNavbar = () => {
this.navbarOpen = !this.navbarOpen;
};
public hasModPermissions = () => {
return this.claims && this.claims.permissions >= Permissions.Mod;
};
}

View File

@@ -20,9 +20,9 @@
"integrity": "sha512-aWw2YTtAdT7CskFyxEX2K21/zSDStuf/ikI3yBqmwpwJF0pS+/IX5DWv+1UFffZIbruP6cnT9/LAJV1gFwAT1A=="
},
"@types/lodash": {
"version": "4.14.110",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.110.tgz",
"integrity": "sha512-iXYLa6olt4tnsCA+ZXeP6eEW3tk1SulWeYyP/yooWfAtXjozqXgtX4+XUtMuOCfYjKGz3F34++qUc3Q+TJuIIw=="
"version": "4.14.137",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.137.tgz",
"integrity": "sha512-g4rNK5SRKloO+sUGbuO7aPtwbwzMgjK+bm9BBhLD7jGUiGR7zhwYEhSln/ihgYQBeIJ5j7xjyaYzrWTcu3UotQ=="
},
"@types/luxon": {
"version": "1.2.2",
@@ -5405,9 +5405,9 @@
}
},
"lodash": {
"version": "4.17.10",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg=="
"version": "4.17.15",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
},
"lodash.assign": {
"version": "4.2.0",
@@ -6237,9 +6237,9 @@
}
},
"normalize.css": {
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.0.tgz",
"integrity": "sha512-iXcbM3NWr0XkNyfiSBsoPezi+0V92P9nj84yVV1/UZxRUrGczgX/X91KMAGM0omWLY2+2Q1gKD/XRn4gQRDB2A=="
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz",
"integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg=="
},
"npm-run-path": {
"version": "2.0.2",
@@ -7983,9 +7983,9 @@
}
},
"react-chartjs-2": {
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.7.2.tgz",
"integrity": "sha512-ncTSOZjlI9j3aXGbsiXEoCtb6oS5nixYe9wLWK+3af1aq7K/vcFkzA9faA5Z/q+fgMDWLEa078cWAyzymxvvYg==",
"version": "2.7.6",
"resolved": "https://registry.npmjs.org/react-chartjs-2/-/react-chartjs-2-2.7.6.tgz",
"integrity": "sha512-xDr0jhgt/o26atftXxTVsepz+QYZI2GNKBYpxtLvYgwffLUm18a9n562reUJAHvuwKsy2v+qMlK5HyjFtSW0mg==",
"requires": {
"lodash": "^4.17.4",
"prop-types": "^15.5.8"

View File

@@ -12,7 +12,7 @@
"dependencies": {
"@types/chart.js": "^2.7.22",
"@types/jwt-decode": "^2.2.1",
"@types/lodash": "^4.14.110",
"@types/lodash": "^4.14.137",
"@types/luxon": "^1.2.2",
"@types/node": "^10.3.4",
"@types/nprogress": "0.0.29",
@@ -39,18 +39,18 @@
"font-awesome": "^4.7.0",
"html-webpack-plugin": "^3.2.0",
"jwt-decode": "^2.2.0",
"lodash": "^4.17.10",
"lodash": "^4.17.15",
"luxon": "^1.3.3",
"mini-css-extract-plugin": "^0.4.2",
"mobx": "^5.0.3",
"mobx-react": "^5.2.5",
"node-sass": "^4.9.3",
"normalize.css": "^8.0.0",
"normalize.css": "^8.0.1",
"nprogress": "^0.2.0",
"postcss-loader": "^2.1.5",
"query-string": "^6.1.0",
"react": "^16.4.1",
"react-chartjs-2": "^2.7.2",
"react-chartjs-2": "^2.7.6",
"react-dom": "^16.4.1",
"react-dropzone": "^4.2.11",
"react-router-dom": "^4.3.1",