Skip to content

Commit

Permalink
working rendering of UploadList (w/o events connected up); refinement…
Browse files Browse the repository at this point in the history
… of behavior on switching target user
  • Loading branch information
jebeck committed Jan 22, 2016
1 parent 05b7376 commit abc4a9e
Show file tree
Hide file tree
Showing 13 changed files with 557 additions and 172 deletions.
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
stage: 0
}
6 changes: 3 additions & 3 deletions lib/components/Upload.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ var React = require('react');
var sundial = require('sundial');
var getIn = require('../core/getIn');
var deviceInfo = require('../core/deviceInfo');
var ProgressBar = require('./ProgressBar.jsx');
var LoadingBar = require('./LoadingBar.jsx');
var ProgressBar = require('./ProgressBar');
var LoadingBar = require('./LoadingBar');

var Upload = React.createClass({
propTypes: {
Expand Down Expand Up @@ -318,7 +318,7 @@ var Upload = React.createClass({
},

isCarelinkUpload: function() {
return this.props.upload.carelink;
return this.props.upload.key === 'carelink';
},

isFetchingCarelinkData: function() {
Expand Down
139 changes: 40 additions & 99 deletions lib/components/UploadList.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

/*
* == BSD2 LICENSE ==
* Copyright (c) 2014, Tidepool Project
* Copyright (c) 2016, Tidepool Project
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the associated License, which is identical to the BSD 2-Clause
Expand All @@ -15,117 +16,57 @@
* == BSD2 LICENSE ==
*/

var _ = require('lodash');
var React = require('react');
var cx = require('classnames');
var Upload = require('./Upload.jsx');
import _ from 'lodash';
import React, { Component, PropTypes } from 'react';
import cx from 'classnames';

var UploadList = React.createClass({
propTypes: {
uploads: React.PropTypes.array.isRequired,
targetedUploads: React.PropTypes.array.isRequired,
onUpload: React.PropTypes.func.isRequired,
onReset: React.PropTypes.func.isRequired,
readFile: React.PropTypes.func.isRequired,
groupsDropdown: React.PropTypes.bool.isRequired,
text: React.PropTypes.object
},
getInitialState: function() {
return {
showErrorDetails: []
};
},
getDefaultProps: function(){
return {
text: {
SHOW_ERROR : 'Error details',
HIDE_ERROR : 'Hide details',
UPLOAD_FAILED : 'Upload Failed: '
}
};
},
makeHandleShowDetailsFn: function(upload){
var self = this;
import Upload from './Upload';

return function(e) {
if(e){
e.preventDefault();
}
// add or remove this upload's key to the list of uploads to show errors for
var showErrorsList = self.state.showErrorDetails;
if (_.includes(showErrorsList, upload.key)) {
showErrorsList = _.reject(showErrorsList, function(i) { return i === upload.key; });
}
else {
showErrorsList.push(upload.key);
}
self.setState({showErrorDetails: showErrorsList});
};
},
renderErrorForUpload: function(upload) {
if (_.isEmpty(upload) || _.isEmpty(upload.error)) {
return;
}
var showDetailsThisUpload = _.includes(this.state.showErrorDetails, upload.key);
var errorDetails = showDetailsThisUpload ? (<div className="UploadList-error-details">{upload.error.debug}</div>) : null;
var showErrorsText = showDetailsThisUpload ? this.props.text.HIDE_ERROR : this.props.text.SHOW_ERROR;
var errorMessage = upload.error.driverLink ? <div className="UploadList-error-message-wrapper">
<span className="UploadList-error-message">{this.props.text.UPLOAD_FAILED}</span>
<span className="UploadList-error-message-friendly">{upload.error.friendlyMessage}</span>
<span className="UploadList-error-message-link"><a href={upload.error.driverLink} target="_blank">{upload.error.driverName}</a></span>
</div> :
<span className="UploadList-error-message">{this.props.text.UPLOAD_FAILED + upload.error.friendlyMessage}</span>;
export default class UploadList extends Component {
static propTypes = {
devices: PropTypes.object.isRequired,
potentialUploads: PropTypes.array.isRequired,
// targetId can be null when logged in user is not a data storage account
// for example a clinic worker
targetId: PropTypes.string,
userDropdownShowing: PropTypes.bool.isRequired
};

var clickHandler = this.makeHandleShowDetailsFn(upload);
static defaultProps = {
SHOW_ERROR : 'Error details',
HIDE_ERROR : 'Hide details',
UPLOAD_FAILED : 'Upload Failed: '
};

return (
<div className="UploadList-error-item">
{errorMessage}
<div className="UploadList-error-text"><a href="" onClick={clickHandler}>{showErrorsText}</a></div>
{errorDetails}
</div>
);
},
render: function() {
var self = this;
var uploadListClasses = cx({
constructor(props) {
super(props);
}

render() {
const uploadListClasses = cx({
UploadList: true,
'UploadList--onlyme': !this.props.groupsDropdown,
'UploadList--groups': this.props.groupsDropdown
'UploadList--onlyme': !this.props.userDropdownShowing,
'UploadList--selectuser': this.props.userDropdownShowing
});

var nodes = _.map(this.props.targetedUploads, function(target){
var keyToMatch;
var index = _.findIndex(self.props.uploads, function(upload) {
if(upload.key === target.key){
keyToMatch = target.key;
return true;
}
return false;
});
var matchingUpload = _.find(self.props.targetedUploads, function(upload) {
return upload.key === keyToMatch;
});
const { devices } = this.props;

const items = _.map(this.props.potentialUploads, (deviceKey) => {
return (
<div key={index} className="UploadList-item">
<div key={deviceKey} className='UploadList-item'>
<Upload
upload={matchingUpload}
onUpload={self.props.onUpload.bind(null, index)}
onReset={self.props.onReset.bind(null, index)}
readFile={self.props.readFile.bind(null, index, self.props.targetId)} />
{self.renderErrorForUpload(matchingUpload)}
upload={devices[deviceKey]}
onReset={_.noop}
onUpload={_.noop}
readFile={_.noop} />
</div>
);
});

return (
<div>
<div className={uploadListClasses}>
{nodes}
</div>
<div className={uploadListClasses}>
{items}
</div>
);
);
}
});

module.exports = UploadList;
}
82 changes: 55 additions & 27 deletions lib/containers/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,20 +41,21 @@ import Loading from '../components/Loading';
import Login from '../components/Login';
import LoggedInAs from '../components/LoggedInAs';
import TimezoneDropdown from '../components/TimezoneDropdown';
import UploadList from '../components/UploadList';
import UserDropdown from '../components/UserDropdown';

export default class App extends Component {
static propTypes = {
api: PropTypes.func.isRequired
};

constructor(props) {
super(props);
this.log = bows('App');
this.handleClickChooseDevices = this.handleClickChooseDevices.bind(this);
this.handleDismissDropdown = this.handleDismissDropdown.bind(this);
}

componentWillMount() {
const { api } = this.props;
this.props.async.doAppInit(config, {
api,
api: props.api,
carelink,
device,
localStore,
Expand Down Expand Up @@ -114,7 +115,7 @@ export default class App extends Component {
}

renderPage() {
const { devices, os, page, showingUserSelectionDropdown, url, users } = this.props;
const { page, showingUserSelectionDropdown, users } = this.props;

let userDropdown = showingUserSelectionDropdown ?
this.renderUserDropdown() : null;
Expand All @@ -125,22 +126,30 @@ export default class App extends Component {
return (
<Login
errorMessage={users.errorMessage || null}
forgotPasswordUrl={url.forgotPassword}
forgotPasswordUrl={this.props.url.forgotPassword}
isFetching={users.isFetching}
onLogin={this.props.async.doLogin} />
);
} else if (page === pages.MAIN) {
return null;
return (
<div>
{userDropdown}
<UploadList
devices={this.props.devices}
potentialUploads={this.props.potentialUploads}
targetId={users.uploadTargetUser}
userDropdownShowing={showingUserSelectionDropdown} />
</div>
);
} else if (page === pages.SETTINGS) {
let timezoneDropdown = this.renderTimezoneDropdown();
const targetUser = users[users.uploadTargetUser] || {};
return (
<div>
{userDropdown}
{timezoneDropdown}
<DeviceSelection
devices={devices}
os={os}
devices={this.props.devices}
os={this.props.os}
targetDevices={this.props.selectedTargetDevices}
targetId={users.uploadTargetUser}
timezoneIsSelected={Boolean(this.props.selectedTimezone)}
Expand Down Expand Up @@ -184,7 +193,7 @@ export default class App extends Component {
return (
<UserDropdown
page={page}
onGroupChange={this.props.sync.setUploadTargetUser}
onGroupChange={this.props.async.setUploadTargetUserAndMaybeRedirect}
users={users}
isUploadInProgress={null}
targetId={users.uploadTargetUser} />
Expand All @@ -199,31 +208,50 @@ App.propTypes = {
// wrap the component to inject dispatch and state into it
export default connect(
(state) => {
function hasSomeoneLoggedIn(state) {
return !_.includes([pages.LOADING, pages.LOGIN], state.page);
}
function getPotentialUploadsForUploadTargetUser(state) {
return Object.keys(
_.get(state, ['uploads', state.users.uploadTargetUser], {})
);
}
function getSelectedTargetDevices(state) {
return _.get(
state.users[state.users.uploadTargetUser],
['targets', 'devices'],
// fall back to the targets stored under 'noUserSelected', if any
_.get(state.users['noUserSelected'], ['targets', 'devices'], [])
);
}
function getSelectedTimezone(state) {
return _.get(
state.users[state.users.uploadTargetUser],
['targets', 'timezone'],
// fall back to the timezone stored under 'noUserSelected', if any
_.get(state.users['noUserSelected'], ['targets', 'timezone'], null)
);
}
function shouldShowUserSelectionDropdown(state) {
return !_.isEmpty(state.users.targetsForUpload) &&
state.users.targetsForUpload.length > 1;
}
return {
// plain state
devices: state.devices,
dropdown: state.dropdown,
os: state.os,
page: state.page,
version: state.version,
uploads: state.uploads,
url: state.url,
users: state.users,
// derived state
isLoggedIn: !_.includes([pages.LOADING, pages.LOGIN], state.page),
selectedTargetDevices: _.get(
state.users[state.users.uploadTargetUser],
['targets', 'devices'],
// fall back to the targets stored under 'noUserSelected', if any
_.get(state.users['noUserSelected'], ['targets', 'devices'], [])
),
selectedTimezone: _.get(
state.users[state.users.uploadTargetUser],
['targets', 'timezone'],
// fall back to the timezone stored under 'noUserSelected', if any
_.get(state.users['noUserSelected'], ['targets', 'timezone'], null)
),
showingUserSelectionDropdown: !_.isEmpty(state.users.targetsForUpload) &&
state.users.targetsForUpload.length > 1
isLoggedIn: hasSomeoneLoggedIn(state),
potentialUploads: getPotentialUploadsForUploadTargetUser(state),
selectedTargetDevices: getSelectedTargetDevices(state),
selectedTimezone: getSelectedTimezone(state),
showingUserSelectionDropdown: shouldShowUserSelectionDropdown(state)
};
},
(dispatch) => {
Expand Down
Loading

0 comments on commit abc4a9e

Please sign in to comment.