Removed GopherJS, basic frontend completed, need backend changes for

torrent storage
This commit is contained in:
2017-11-30 18:12:11 -05:00
parent 67fdef16b1
commit e98ad2cc88
69321 changed files with 5498914 additions and 337 deletions

View File

@@ -0,0 +1,70 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
import Dialog, {
DialogContent,
DialogTitle,
} from 'material-ui/Dialog';
//import InsertLinkIcon from 'material-ui-icons/Link';
import ReactTooltip from 'react-tooltip'
//import Icon from 'material-ui/Icon';
import AddIcon from 'material-ui-icons/AddBox';
import IconButton from 'material-ui/IconButton';
//import Dropzone from 'react-dropzone'; //the File drop acceptor
//const request = require('superagent');
const button = {
fontSize: '60px',
paddingRight: '20px',
paddingLeft: '20px',
}
const inlineStyle = {
display: 'inline-block',
}
export default class addTorrentFilePopup extends React.Component {
state = {
open: false,
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleRequestClose = () => {
this.setState({ open: false });
};
setTextValue = (event) => {
this.setState({textValue: event.target.value});
}
render() {
const { classes, onRequestClose, handleRequestClose, handleSubmit } = this.props;
return (
<div style={inlineStyle}>
<IconButton onClick={this.handleClickOpen} color="primary" data-tip="Add Torrent File" style={button} centerRipple aria-label="Add Torrent File" >
<ReactTooltip place="top" type="light" effect="float" />
<AddIcon />
</IconButton>
<Dialog open={this.state.open} onRequestClose={this.handleRequestClose} onEscapeKeyUp={this.handleRequestClose} maxWidth="md">
<DialogTitle>Add Torrent File</DialogTitle>
<DialogContent>
<form encType="multipart/form-data" method="post" action="/uploadTorrent">
<input name="fileTest" type="file" /><p />
<input type="submit" value="submit" />
</form>
</DialogContent>
</Dialog>
</div>
);
}
};

View File

@@ -0,0 +1,92 @@
import React from 'react';
import ReactDOM from 'react-dom';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
import InsertLinkIcon from 'material-ui-icons/Link';
import ReactTooltip from 'react-tooltip'
import Icon from 'material-ui/Icon';
import IconButton from 'material-ui/IconButton';
const button = {
fontSize: '60px',
paddingRight: '20px',
paddingLeft: '20px',
}
const inlineStyle = {
display: 'inline-block',
}
export default class addTorrentPopup extends React.Component {
state = {
open: false,
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleRequestClose = () => {
this.setState({ open: false });
};
handleSubmit = () => {
this.setState({ open: false });
let magnetLinkSubmit = this.state.textValue;
console.log("Sending magnet link: ", magnetLinkSubmit);
ws.send(magnetLinkSubmit);
}
setTextValue = (event) => {
this.setState({textValue: event.target.value});
}
render() {
const { classes, onRequestClose, handleRequestClose, handleSubmit } = this.props;
return (
<div style={inlineStyle}>
<IconButton onClick={this.handleClickOpen} color="primary" data-tip="Add Magnet Link" style={button} centerRipple aria-label="Add Magnet Link" >
<ReactTooltip place="top" type="light" effect="float" />
<InsertLinkIcon />
</IconButton>
<Dialog open={this.state.open} onRequestClose={this.handleRequestClose}>
<DialogTitle>Add Magnet Link</DialogTitle>
<DialogContent>
<DialogContentText>
Add a Magnet Link here and hit submit to add torrent...
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Magnet Link"
type="text"
placeholder="Enter Magnet Link Here"
fullWidth
onChange={this.setTextValue}
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleRequestClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleSubmit} color="primary">
Submit
</Button>
</DialogActions>
</Dialog>
</div>
);
}
};

View File

@@ -0,0 +1,85 @@
import React from 'react';
import ReactDOM from 'react-dom';
//css for react grid
import '../node_modules/react-grid-layout/css/styles.css';
import '../node_modules/react-resizable/css/styles.css';
//react-grid for layout
import RGL, { WidthProvider } from 'react-grid-layout';
import PropTypes from 'prop-types';
import _ from 'lodash';
//Redux
import {createStore} from 'redux';
import {Provider} from 'react-redux';
import reducer from './store/reducer';
//Menu and torrentlist imports
import TopMenu from './topMenu';
import BottomMenu from './bottomMenu';
import LeftMenu from './leftMenu';
import TorrentList from './torrentlist';
const reduxStore = createStore(reducer);
const ReactGridLayout = WidthProvider(RGL);
var background = {
backgroundColor: '#e5e5e5',
boxShadow: '0 0 20px #000',
}
class BasicLayout extends React.PureComponent {
static propTypes = {
onLayoutChange: PropTypes.func.isRequired
};
static defaultProps = {
className: "layout",
items: 4,
rowHeight: 100,
onLayoutChange: function() {},
cols: 6,
draggableCancel: '.NoDrag',
draggableHandle: '.DragHandle'
};
constructor(props) {
super(props);
var layout = [
{i: 'a', x: 0, y: 0, w: 6, h: 1, static: true},
{i: 'b', x: 0, y: 1, w: 1, h: 5},
{i: 'c', x: 1, y: 1, w: 5, h: 5, minW: 5, minH: 5, static: true},
{i: 'd', x: 1, y: 2, w: 5, h: 2, minW: 5, minH: 1}
];
this.state = { layout };
}
onLayoutChange(layout) {
this.props.onLayoutChange(layout);
}
render() {
return (
<ReactGridLayout layout={this.state.layout} onLayoutChange={this.onLayoutChange}
{...this.props}>
<div key="a" style={background} className="DragHandle"><TopMenu /></div>
<div key="b" style={background} className="DragHandle"><LeftMenu /></div>
<div key="c" style={background} className="DragHandle"><TorrentList /></div>
<div key="d"><BottomMenu /></div>
</ReactGridLayout> //returning our 4 grids
);
}
};
module.exports = BasicLayout;
//if (require.main === module) {
// require('../test-hook.jsx')(module.exports);
//}
ReactDOM.render(
<Provider store={reduxStore}><BasicLayout /></Provider>, //wrapping redux around our app
document.getElementById('app')
);

View File

@@ -0,0 +1,67 @@
import React from 'react';
import PropTypes from 'prop-types';
import 'typeface-roboto'; // contains the font for material UI
import { withStyles } from 'material-ui/styles';
import AppBar from 'material-ui/AppBar';
import Tabs, { Tab } from 'material-ui/Tabs';
function TabContainer(props) {
return <div style={{ padding: 8 * 3 }}>{props.children}</div>;
}
TabContainer.propTypes = {
children: PropTypes.node.isRequired,
};
const styles = theme => ({
root: {
// flexGrow: 1,
// marginTop: theme.spacing.unit * 3,
//backgroundColor: theme.palette.background.paper,
backgroundColor: '#e5e5e5',
height: '100%',
boxShadow: '0 0 20px #000',
},
});
class BasicTabs extends React.Component {
state = {
value: 0,
};
handleChange = (event, value) => {
this.setState({ value });
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div className={classes.root}>
<div className="DragHandle"> {/* making the appbar draggable */}
<AppBar position="static">
<Tabs value={value} onChange={this.handleChange}>
<Tab label="General"/>
<Tab label="Peers"/>
<Tab label="Files"/>
<Tab label="Speed"/>
<Tab label="Logger" href="#basic-tabs"/>
</Tabs>
</AppBar>
</div>
{value === 0 && <TabContainer>General</TabContainer>}
{value === 1 && <TabContainer>Peers</TabContainer>}
{value === 2 && <TabContainer>Files</TabContainer>}
{value === 3 && <TabContainer>Speed</TabContainer>}
{value === 4 && <TabContainer>Logger</TabContainer>}
</div>
);
}
}
BasicTabs.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(BasicTabs);

18595
torrent-project/src/bundle.js Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
<html>
<div id="app"></div>
</html>

View File

@@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import 'typeface-roboto'; // contains the font for material UI
import { withStyles } from 'material-ui/styles';
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Divider from 'material-ui/Divider';
import DownloadingTorrentsIcon from 'material-ui-icons/FileDownload'
import UploadingTorrentsIcon from 'material-ui-icons/FileUpload'
import ActiveTorrentsIcon from 'material-ui-icons/SwapVert'
const styles = theme => ({
root: {
width: '100%',
maxWidth: 360,
backgroundColor: '#e5e5e5',
},
icons: {
width: '40px',
height: '40px',
},
inactiveIcon: {
width: '40px',
height: '40px',
color: 'red',
}
});
function SimpleList(props) {
const { classes } = props;
return (
<div className={classes.root}>
<List>
<ListItem button>
<ListItemIcon className={classes.icons}>
<DownloadingTorrentsIcon />
</ListItemIcon>
<ListItemText primary="Downloading Torrents" />
</ListItem>
<ListItem button>
<ListItemIcon className={classes.icons}>
<UploadingTorrentsIcon />
</ListItemIcon>
<ListItemText primary="Uploading Torrents" />
</ListItem>
<ListItem button>
<ListItemIcon className={classes.icons}>
<ActiveTorrentsIcon />
</ListItemIcon>
<ListItemText primary="Active Torrents" />
</ListItem>
<ListItem button>
<ListItemIcon className={classes.inactiveIcon}>
<ActiveTorrentsIcon />
</ListItemIcon>
<ListItemText primary="Inactive Torrents" />
</ListItem>
</List>
<Divider />
</div>
);
}
SimpleList.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(SimpleList);

View File

@@ -0,0 +1,56 @@
import React from 'react';
import Button from 'material-ui/Button';
import TextField from 'material-ui/TextField';
import Dialog, {
DialogActions,
DialogContent,
DialogContentText,
DialogTitle,
} from 'material-ui/Dialog';
export default class FormDialog extends React.Component {
state = {
open: false,
};
handleClickOpen = () => {
this.setState({ open: true });
};
handleRequestClose = () => {
this.setState({ open: false });
};
render() {
return (
<div>
<Button onClick={this.handleClickOpen}>Open form dialog</Button>
<Dialog open={this.state.open} onRequestClose={this.handleRequestClose}>
<DialogTitle>Subscribe</DialogTitle>
<DialogContent>
<DialogContentText>
To subscribe to this website, please enter your email address here. We will send
updates occationally.
</DialogContentText>
<TextField
autoFocus
margin="dense"
id="name"
label="Email Address"
type="email"
fullWidth
/>
</DialogContent>
<DialogActions>
<Button onClick={this.handleRequestClose} color="primary">
Cancel
</Button>
<Button onClick={this.handleRequestClose} color="primary">
Subscribe
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}

View File

@@ -0,0 +1,3 @@
export const SORTLIST = 'SORTLIST';
export const CHANGE_SELECTION = 'CHANGE_SELECTION';

View File

@@ -0,0 +1,27 @@
import * as actionTypes from './actions';
const initialState = {
buttonState: "default",
sorting: [],
selection: [],
}
const reducer = (state = initialState, action) => {
switch(action.type){
case actionTypes.CHANGE_SELECTION:
console.log("Selection change", action.selection)
return state;
case actionTypes.SORTLIST:
console.log("List Sort", action.sorting)
return state;
}
console.log("no actiontypes found", action)
return state;
}
export default reducer;

View File

@@ -0,0 +1,150 @@
import React from 'react';
import PropTypes from 'prop-types';
import 'typeface-roboto'; // contains the font for material UI
import { withStyles } from 'material-ui/styles';
import Icon from 'material-ui/Icon';
import IconButton from 'material-ui/IconButton';
import AddTorrentLinkPopup from './addTorrentLinkModal';
import AddTorrentFilePopup from './addTorrentFileModal';
import StartTorrentIcon from 'material-ui-icons/PlayArrow';
import PauseTorrentIcon from 'material-ui-icons/Pause';
import StopTorrentIcon from 'material-ui-icons/Stop';
import DeleteTorrentIcon from 'material-ui-icons/Delete';
import RSSTorrentIcon from 'material-ui-icons/RssFeed';
import SettingsIcon from 'material-ui-icons/Settings';
import ReactTooltip from 'react-tooltip'
import DeleteIcon from 'material-ui-icons/Delete';
import AddShoppingCartIcon from 'material-ui-icons/AddShoppingCart';
//import PhotoCamera from 'material-ui-icons/PhotoCamera';
//Redux
import {connect} from 'react-redux';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
fontSize: '60px',
},
input: {
display: 'none',
},
paddingTest: {
display: 'inline-block'
},
padding: {
paddingTop: '10px',
paddingLeft: '10px',
},
verticalDivider: {
borderLeft: '2px solid grey',
padding: '20px',
height: '40px',
position: 'absolute',
display: 'inline-block',
paddingRight: '30px',
paddingLeft: '30px',
},
background: {
backgroundColor: theme.palette.background.paper,
}
});
class IconButtons extends React.Component {
constructor(props){
super(props);
let buttonState = "default"
console.log("buttonState", buttonState)
switch(buttonState){
case "paused":
startTorrentState: "primary"
pauseTorrentState: "disabled"
stopTorrentState: "primary"
deleteTorrentState: "accent"
case "stopped":
startTorrentState: "primary"
pauseTorrentState: "disabled"
stopTorrentState: "primary"
deleteTorrentState: "accent"
case "downloading":
startTorrentState: "disabled"
pauseTorrentState: "primary"
stopTorrentState: "primary"
deleteTorrentState: "accent"
default:
startTorrentState: "disabled"
pauseTorrentState: "disabled"
stopTorrentState: "disabled"
deleteTorrentState: "disabled"
}
}
/* onGlobalSelectRow = (selectedRowProps) => {
console.log("Here...", selectedRowProps)
}
*/
render() {
const { classes } = this.props;
return (
<div className={classes.padding}>
<AddTorrentFilePopup />
<AddTorrentLinkPopup />
<div className={classes.verticalDivider}></div>
<IconButton color={this.props.startTorrentState} data-tip="Start Torrent" className={classes.button} aria-label="Start Torrent">
<ReactTooltip place="top" type="light" effect="float" />
<StartTorrentIcon />
</IconButton>
<IconButton color={this.props.pauseTorrentState} data-tip="Pause Torrent" className={classes.button} aria-label="Pause Torrent">
<ReactTooltip place="top" type="light" effect="float" />
<PauseTorrentIcon />
</IconButton>
<IconButton color={this.props.stopTorrentState} data-tip="Stop Torrent" className={classes.button} aria-label="Stop Torrent">
<ReactTooltip place="top" type="light" effect="float" />
<StopTorrentIcon />
</IconButton>
<IconButton color={this.props.deleteTorrentState} data-tip="Delete Torrent" className={classes.button} aria-label="Delete Torrent">
<ReactTooltip place="top" type="error" effect="float" />
<DeleteTorrentIcon />
</IconButton>
<div className={classes.verticalDivider}></div>
<IconButton color="primary" data-tip="Manage RSS Feeds" className={classes.button} aria-label="RSS Feeds">
<ReactTooltip place="top" type="light" effect="float" />
<RSSTorrentIcon />
</IconButton>
<IconButton color="primary" data-tip="Settings" className={classes.button} aria-label="Settings">
<ReactTooltip place="top" type="light" effect="float" />
<SettingsIcon />
</IconButton>
</div>
);
}
}
IconButtons.propTypes = {
classes: PropTypes.object.isRequired,
};
const mapStateToProps = state => {
return {
buttonState: state.selection
};
}
export default withStyles(styles)(connect(mapStateToProps)(IconButtons))

View File

@@ -0,0 +1,17 @@
import {Grid, TableView, TableHeaderRow} from '@devexpress/dx-react-grid-material-ui'/* or '@devexpress/dx-react-grid-material-ui' */;
const App = () => (
<Grid
rows={[
{ id: 0, product: 'DevExtreme', owner: 'DevExpress' },
{ id: 1, product: 'DevExtreme Reactive', owner: 'DevExpress' },
]}
columns={[
{ name: 'id', title: 'ID' },
{ name: 'product', title: 'Product' },
{ name: 'owner', title: 'Owner' },
]}>
<TableView />
<TableHeaderRow />
</Grid>
);

View File

@@ -0,0 +1,177 @@
import React from 'react';
import ReactDOM from 'react-dom';
import styles from '../node_modules/react-bootstrap-table/dist/react-bootstrap-table-all.min.css';
import {BootstrapTable, TableHeaderColumn} from 'react-bootstrap-table';
import {
SortingState, LocalSorting, PagingState, VirtualTableLayout, SelectionState,
} from '@devexpress/dx-react-grid';
import {
Grid, TableView, TableHeaderRow, PagingPanel, VirtualTableView, TableSelection, TableColumnResizing,
DragDropContext, TableColumnReordering,
} from '@devexpress/dx-react-grid-material-ui';
//react redux
import {connect} from 'react-redux';
import * as actionTypes from './store/actions';
/* var torrentLinkSubmit = document.getElementById('torrentLinkSubmit');
var magnetLink = document.getElementById('magnetLink');
var myTextArea = document.getElementById("loggerData");
var torrentHash = document.getElementById("hash");
initialize an empty torrents field before update.
var torrentLinkSubmit = document.getElementById('torrentLinkSubmit');
var magnetLink = document.getElementById('magnetLink');
var myTextArea = document.getElementById("loggerData");
var torrentHash = document.getElementById("hash");
var torrentLinkSubmit = document.getElementById('torrentLinkSubmit');
var torrentLinkModal = document.getElementById('addTorrentLinkModal');
var btnTorrentLink = document.getElementById("addTorrentLink");
*/
var title = document.title; //Set the number of active torrents in the title
let torrents= [];
//websocket is started in kickwebsocket.js and is picked up here so "ws" is already defined
ws.onmessage = function (evt) //When we recieve a message from the websocket
{
if(evt.data == "clientUpdate") {
console.log("Client Update Incoming...")
} else {
console.log("Recieved Client Update...")
var clientUpdate = JSON.parse(evt.data);
// myTextArea.innerHTML = myTextArea.innerHTML + "</br>" + "Client Update Event...";
//myTextArea.innerHTML = myTextArea.innerHTML + "</br>" + clientUpdate.LocalTorrentInfo.DateAdded;
// myTextArea.innerHTML = myTextArea.innerHTML + "</br>" + evt.data;
//myTextArea.innerHTML = myTextArea.innerHTML + "</br>" + clientUpdate[0].TorrentHashString;
// torrentHash.innerHTML = "Hash: " + clientUpdate.data[0].TorrentHashString;
torrents = []; //clearing out the torrent array to make room for new (so that it does keep adding)
for(var i = 0; i < clientUpdate.total; i++){
//console.log("TorrentName: ", clientUpdate.data[i].TorrentName)
torrents.push({
TorrentHashString: clientUpdate.data[i].TorrentHashString,
TorrentName: clientUpdate.data[i].TorrentName,
DownloadedSize: clientUpdate.data[i].DownloadedSize,
DownloadSpeed: clientUpdate.data[i].DownloadedSpeed,
UploadSpeed: clientUpdate.data[i].UploadSpeed,
PercentDone: clientUpdate.data[i].PercentDone,
StoragePath: clientUpdate.data[i].StoragePath,
DateAdded: clientUpdate.data[i].DateAdded,
Status: clientUpdate.data[i].Status,
BytesCompleted: clientUpdate.data[i].BytesCompleted,
ActivePeers: clientUpdate.data[i].ActivePeers,
TotalPeers: clientUpdate.data[i].TotalPeers,
})
}
var newTitle = '(' + clientUpdate.total + ')' + title; //updating the title
document.title = newTitle;
}
}
ws.onclose = function()
{
//var myTextArea = document.getElementById("loggerData");
//myTextArea.innerHTML = myTextArea.innerHTML + "</br>" + "Connection closed";
console.log('Closing connection')
};
function sendEvent(message)
{
ws.send(message);
console.log('Sending message... ', message)
}
class TorrentListTable extends React.Component {
constructor(props) {
super(props);
this.state = {
torrentList: torrents,
columns: [
{ name: 'TorrentName', title: 'Torrent Name' },
{ name: 'DownloadedSize', title: 'Dl Size'},
{ name: 'Size', title: 'Size'},
{ name: 'Done', title: 'Percent Done'},
{ name: 'Status', title: 'Status'},
{ name: 'DownloadSpeed', title: 'Download Speed'},
{ name: 'UploadSpeed', title: 'Upload Speed'},
{ name: 'ActivePeers', title: 'Active Peers' },
{ name: 'TotalPeers', title: 'Total Peers' },
{ name: 'ETA', title: 'ETA'},
{ name: 'Ratio', title: 'Ratio'},
{ name: 'Availability', title: 'Availability'},
{ name: 'TorrentHashString', title: 'Torrent Hash' }
],
columnOrder: ['TorrentName', 'DownloadedSize', 'Size', 'Done', 'Status', 'DownloadSpeed', 'UploadSpeed','ActivePeers', 'TotalPeers', 'ETA', 'Ratio', 'Availability', 'TorrentHashString'],
columnWidths: {TorrentName: 250, DownloadedSize: 175, Size: 175, Done: 175, Status: 250, DownloadSpeed: 100, UploadSpeed: 100, ActivePeers: 100, TotalPeers: 100, ETA: 175, Ratio: 175, Availability: 175, TorrentHashString: 250,}
};
this.changeColumnOrder = columnOrder => this.setState({columnOrder});
this.changeColumnWidths = columnWidths => this.setState({columnWidths});
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
2000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
ws.send("clientUpdateRequest")//talking to the server to get the torrent list
this.setState({torrentList: torrents});
//console.log("STATE:", this.state.torrentList)
//console.log("Torrents", torrents);
}
render() {
return (
<Grid rows={this.state.torrentList} columns={this.state.columns}>
<SortingState sorting={this.props.sorting} onSortingChange={this.props.changeSorting} />
<LocalSorting />
<SelectionState onSelectionChange={this.props.changeSelection} selection={this.props.selection}/>
<TableView />
<DragDropContext />
<TableColumnResizing columnWidths={this.state.columnWidths} onColumnWidthsChange={this.changeColumnWidths}/>
<TableColumnReordering order={this.state.columnOrder} onOrderChange={this.changeColumnOrder} />
<TableSelection selectByRowClick highlightSelected />
<TableHeaderRow allowSorting allowResizing allowDragging />
</Grid>
);
}
}
const mapStateToProps = state => {
return {
};
}
const mapDispatchToProps = dispatch => {
return {
changeSorting: (sorting) => dispatch({type: actionTypes.SORTLIST, sorting }),
changeSelection: (selection) => dispatch({type: actionTypes.CHANGE_SELECTION, selection}),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(TorrentListTable);