Move from Redux to vanilla Flux

This commit is contained in:
John Furrow
2015-12-14 21:30:19 -08:00
parent 86c5886ad2
commit 81aa20e60d
23 changed files with 999 additions and 1064 deletions
@@ -1,79 +0,0 @@
import {connect} from 'react-redux';
import React from 'react';
import Action from '../components/action-bar/Action';
import {addTorrent, startTorrent, stopTorrent} from '../actions/ClientActions';
import {displayModal} from '../actions/UIActions';
import {setTorrentsSort} from '../actions/UIActions';
import SortDropdown from '../components/action-bar/SortDropdown';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'handleAddTorrents',
'handleSortChange',
'handleStart',
'handleStop'
];
class ActionBar extends React.Component {
constructor() {
super();
this.state = {
selectedTorrents: []
};
methodsToBind.forEach((method) => {
this[method] = this[method].bind(this);
});
}
handleAddTorrents() {
this.props.dispatch(displayModal({
modal: 'add-torrents'
}));
}
handleSortChange(sortBy) {
this.props.dispatch(setTorrentsSort({
sortBy
}));
}
handleStart() {
this.props.dispatch(startTorrent(this.props.torrentList.selected));
}
handleStop() {
this.props.dispatch(stopTorrent(this.props.torrentList.selected));
}
render() {
return (
<nav className="action-bar">
<div className="actions action-bar__item action-bar__item--sort-torrents">
<SortDropdown onSortChange={this.handleSortChange}
selectedItem={this.props.torrentList.sortBy} />
</div>
<div className="actions action-bar__item action-bar__item--torrent-operations">
<div className="action-bar__group">
<Action label="Start Torrent" slug="start-torrent" icon="start"
clickHandler={this.handleStart} />
<Action label="Stop Torrent" slug="stop-torrent" icon="stop"
clickHandler={this.handleStop} />
<Action label="Pause Torrent" slug="pause-torrent" icon="pause"
clickHandler={this.handlePause} />
</div>
<div className="action-bar__group action-bar__group--has-divider">
<Action label="Add Torrent" slug="add-torrent" icon="add"
clickHandler={this.handleAddTorrents} />
</div>
</div>
</nav>
);
}
}
export default connect(uiSelector)(ActionBar);
+4 -9
View File
@@ -1,26 +1,21 @@
import {connect} from 'react-redux';
import React from 'react';
import ActionBar from '../containers/ActionBar';
import ActionBar from '../components/action-bar/ActionBar';
import Modals from '../components/modals/Modals';
import Sidebar from './Sidebar';
import rootSelector from '../selectors/rootSelector';
import TorrentList from '../containers/TorrentList';
import TorrentListHeader from '../components/torrent-list/TorrentListHeader';
import Sidebar from '../components/sidebar/Sidebar';
import TorrentListContainer from '../components/torrent-list/TorrentListContainer';
export default class FloodApp extends React.Component {
render() {
return (
<div className="flood">
<Sidebar />
<main className="content">
<ActionBar />
<TorrentList />
<TorrentListContainer />
</main>
<Modals />
</div>
);
}
}
@@ -1,47 +0,0 @@
import {connect} from 'react-redux';
import React from 'react';
import ClientStats from '../components/sidebar/ClientStats';
import {setTorrentsFilter, setTorrentsSearch} from '../actions/UIActions';
import StatusFilters from '../components/sidebar/StatusFilters';
import SearchBox from '../components/sidebar/SearchBox';
import UIActions from '../actions/UIActions';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'handleFilterChange',
'handleSearchChange'
];
class Sidebar extends React.Component {
constructor() {
super();
methodsToBind.forEach((method) => {
this[method] = this[method].bind(this);
});
}
handleFilterChange(value) {
this.props.dispatch(setTorrentsFilter(value));
}
handleSearchChange(value) {
this.props.dispatch(setTorrentsSearch(value));
}
render() {
return (
<aside className="sidebar">
<ClientStats transferData={this.props.transferData} />
<SearchBox handleSearchChange={this.handleSearchChange} />
<StatusFilters handleFilterChange={this.handleFilterChange}
activeFilter={this.props.torrentList.filterBy} />
</aside>
);
}
}
export default connect(uiSelector)(Sidebar);
@@ -1,251 +0,0 @@
import _ from 'lodash';
import classNames from 'classnames';
import {connect} from 'react-redux';
import React from 'react';
import ReactDOM from 'react-dom';
import {getTorrents, getTorrentDetails} from '../actions/ClientActions';
import {handleTorrentClick} from '../actions/UIActions';
import Torrent from '../components/torrent-list/Torrent';
import TorrentDetails from '../components/torrent-list/TorrentDetails';
import torrentSelector from '../selectors/torrentSelector';
import UIActions from '../actions/UIActions';
import uiSelector from '../selectors/uiSelector';
const methodsToBind = [
'componentDidMount',
'componentWillUnmount',
'handleDetailsClick',
'handleTorrentClick',
'getListPadding',
'getTorrents',
'getViewportLimits',
'setScrollPosition',
'setViewportHeight',
'shouldComponentUpdate'
];
class TorrentList extends React.Component {
constructor() {
super();
this.state = {
detailsPanelOpen: false,
count: 0,
maxTorrentIndex: 4,
minTorrentIndex: 0,
scrollPosition: 0,
spaceBottom: 0,
spaceTop: 0,
torrentCount: 0,
torrentFetchInterval: null,
torrentHeight: 64,
torrentRenderBuffer: 2,
viewportHeight: 0
};
methodsToBind.forEach((method) => {
this[method] = this[method].bind(this);
});
this.handleScroll = _.throttle(this.setScrollPosition, 150, {
leading: true
});
this.handleWindowResize = _.throttle(this.setViewportHeight, 350, {
leading: true
});
}
componentDidMount() {
window.addEventListener('resize', this.handleWindowResize);
this.setViewportHeight();
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowResize);
clearInterval(this.state.torrentFetchInterval);
}
componentWillMount() {
let getTorrents = this.getTorrents;
this.state.torrentFetchInterval = setInterval(function() {
getTorrents();
}, 5000);
getTorrents();
}
componentWillReceiveProps(nextProps) {
if ((nextProps.selectedTorrents.length !== 1 &&
this.state.detailsPanelOpen) || nextProps.torrents.length === 0) {
// Close the detail side panel if more than one torrent is selected.
this.setState({
detailsPanelOpen: false
});
} else if (this.state.detailsPanelOpen) {
// Close the detail side panel if the currently selected torrent isn't
// visible.
let searchingForSelectedTorrent = true;
let index = 0;
while (searchingForSelectedTorrent) {
if (nextProps.torrents[index].hash === nextProps.selectedTorrents[0]) {
// The currently selected torrent is visible.
searchingForSelectedTorrent = false;
} else if (index === nextProps.torrents.length - 1) {
// The currently selected torrent is not visible, so clos the details
// panel.
this.setState({
detailsPanelOpen: false
});
index++;
searchingForSelectedTorrent = false;
} else {
index++;
}
}
}
}
shouldComponentUpdate(nextProps) {
if (nextProps.isFetching === true) {
return false;
} else {
return true;
}
}
getTorrents() {
this.props.dispatch(getTorrents());
}
handleDetailsClick(torrent) {
this.props.dispatch(getTorrentDetails(torrent.hash));
this.setState({
detailsPanelOpen: !this.state.detailsPanelOpen
});
}
handleTorrentClick(hash, event) {
if (this.state.detailsPanelOpen) {
this.props.dispatch(getTorrentDetails(hash));
}
this.props.dispatch(handleTorrentClick({
hash,
event,
torrentList: this.props.torrents
}));
}
getListPadding(minTorrentIndex, maxTorrentIndex, torrentCount) {
if (maxTorrentIndex > torrentCount - 1) {
maxTorrentIndex = torrentCount - 1;
}
let hiddenBottom = torrentCount - 1 - maxTorrentIndex;
let hiddenTop = minTorrentIndex;
let bottom = maxTorrentIndex <= torrentCount ? hiddenBottom *
this.state.torrentHeight : 0;
let top = minTorrentIndex > 0 ? hiddenTop * this.state.torrentHeight : 0;
return {
bottom,
top
};
}
getViewportLimits() {
let buffer = 10;
let elementsInView = Math.floor(this.state.viewportHeight /
this.state.torrentHeight);
let minTorrentIndex = Math.floor(this.state.scrollPosition /
this.state.torrentHeight) - buffer;
let maxTorrentIndex = minTorrentIndex + elementsInView + buffer * 2;
return {
minTorrentIndex,
maxTorrentIndex
};
}
setScrollPosition() {
this.setState({
scrollPosition: ReactDOM.findDOMNode(this.refs.torrentList).scrollTop
});
}
setViewportHeight() {
this.setState({
viewportHeight: ReactDOM.findDOMNode(this.refs.torrentList).offsetHeight
});
}
render() {
let classes = classNames({
'torrents': true,
'has-sidepanel': this.state.detailsPanelOpen
});
let selectedTorrents = this.props.selectedTorrents;
let torrentList = null;
let torrents = this.props.torrents;
let viewportLimits = this.getViewportLimits();
let listPadding = this.getListPadding(viewportLimits.minTorrentIndex,
viewportLimits.maxTorrentIndex,
torrents.length);
if (torrents && torrents.length) {
torrentList = torrents.map(function(torrent, index) {
if (index >= viewportLimits.minTorrentIndex &&
index <= viewportLimits.maxTorrentIndex) {
let isSelected = false;
let hash = torrent.hash;
if (selectedTorrents.indexOf(hash) > -1) {
isSelected = true;
}
return (
<Torrent key={hash} data={torrent} selected={isSelected}
handleClick={this.handleTorrentClick}
handleDetailsClick={this.handleDetailsClick} />
);
}
}, this);
}
return (
<div className={classes}>
<div className="torrent__list__wrapper" onScroll={this.handleScroll}
ref="torrentList">
<ul className="torrent__list">
<li className="torrent__spacer torrent__spacer--top"
style={{
height: listPadding.top + 'px'
}}></li>
{torrentList}
<li className="torrent__spacer torrent__spacer--bottom"
style={{
height: listPadding.bottom + 'px'
}}></li>
</ul>
</div>
<TorrentDetails selectedTorrents={selectedTorrents}
torrents={torrents}
visible={this.state.detailsPanelOpen} />
</div>
);
}
}
export default connect(torrentSelector)(TorrentList);