diff --git a/.travis.yml b/.travis.yml index a9c8531..7afcc03 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,4 +10,7 @@ cache: - node_modules script: - yarn build - - yarn test \ No newline at end of file + - yarn test +dist: trusty +sudo: required +group: edge \ No newline at end of file diff --git a/package.json b/package.json index 5ecc671..90f82f9 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "jest": "^20.0.4", "json-loader": "^0.5.4", "mocha": "^3.0.1", - "node-sass": "^3.7.0", + "node-sass": "3.13.1", "nodemon": "^1.10.2", "postcss-loader": "^0.13.0", "prettier": "^1.5.2", diff --git a/src/constants.js b/src/constants.js index 74a2d3b..1edea6e 100644 --- a/src/constants.js +++ b/src/constants.js @@ -1,4 +1,5 @@ export const LIST_PATH = '/projects' +export const TODOS_PATH = '/todos' export const DETAIL_PATH = ':projectname' export const ACCOUNT_PATH = '/account' export const LOGIN_PATH = '/login' @@ -21,6 +22,7 @@ export const formNames = { export const paths = { list: LIST_PATH, + todos: TODOS_PATH, account: ACCOUNT_PATH, detail: DETAIL_PATH, login: LOGIN_PATH, diff --git a/src/containers/Navbar/Navbar.js b/src/containers/Navbar/Navbar.js index c90379d..8608265 100644 --- a/src/containers/Navbar/Navbar.js +++ b/src/containers/Navbar/Navbar.js @@ -19,6 +19,7 @@ import FlatButton from 'material-ui/FlatButton' import DownArrow from 'material-ui/svg-icons/hardware/keyboard-arrow-down' import Avatar from 'material-ui/Avatar' import defaultUserImage from 'static/User.png' +import beetimerLogo from 'static/beetimer_yellow_128.png' const buttonStyle = { color: 'white', @@ -119,13 +120,14 @@ export default class Navbar extends Component { to={accountExists ? `${LIST_PATH}` : '/'} className={classes.brand} > - material example + Beetimer } showMenuIconButton={false} iconElementRight={rightMenu} iconStyleRight={accountExists ? avatarStyles.wrapper : {}} className={classes.appBar} + // style={{ backgroundColor: Theme.palette.accent1Color }} /> ) } diff --git a/src/containers/Navbar/Navbar.scss b/src/containers/Navbar/Navbar.scss index 7863b7f..7f6bb23 100644 --- a/src/containers/Navbar/Navbar.scss +++ b/src/containers/Navbar/Navbar.scss @@ -6,14 +6,16 @@ font-weight: 200; .icon { height: 50px; - margin-top: 7px; + margin-right: 7px; @include mobile { width: 3rem; height: 3rem; - margin-top: 1.1rem; } } } +.appBar { + //border: dashed firebrick 3px; +} .appBar > div { @extend .flex-row; align-items: center; diff --git a/src/routes/Home/containers/HomeContainer.js b/src/routes/Home/containers/HomeContainer.js index 7bfdbe6..29b5399 100755 --- a/src/routes/Home/containers/HomeContainer.js +++ b/src/routes/Home/containers/HomeContainer.js @@ -1,55 +1,32 @@ -import React, { Component, PropTypes } from 'react' -import { connect } from 'react-redux' -import { map } from 'lodash' +import React, {Component, PropTypes} from 'react' +import {connect} from 'react-redux' import Theme from 'theme' -import { - firebaseConnect, - isLoaded, - pathToJS, - dataToJS, // needed for full list and once - // orderedToJS, // needed for ordered list - // populatedDataToJS // needed for populated list -} from 'react-redux-firebase' -import CircularProgress from 'material-ui/CircularProgress' +import {dataToJS, firebaseConnect, isLoaded, pathToJS,} from 'react-redux-firebase' import Snackbar from 'material-ui/Snackbar' -import { List } from 'material-ui/List' -import Paper from 'material-ui/Paper' -import Subheader from 'material-ui/Subheader' -import TodoItem from '../components/TodoItem' -import NewTodoPanel from '../components/NewTodoPanel' import classes from './HomeContainer.scss' -const populates = [{ child: 'owner', root: 'users', keyProp: 'uid' }] +const populates = [{child: 'owner', root: 'users', keyProp: 'uid'}] @firebaseConnect([ // 'todos' // sync full list of todos // { path: 'todos', type: 'once' } // for loading once instead of binding - { path: 'todos', queryParams: ['orderByKey', 'limitToLast=5'] } // 10 most recent + // { path: 'todos', queryParams: ['orderByKey', 'limitToLast=5'] } // 10 most recent // { path: 'todos', populates } // populate // { path: 'todos', storeAs: 'myTodos' } // store elsewhere in redux ]) -@connect(({ firebase }) => ({ - auth: pathToJS(firebase, 'auth'), +@connect(({firebase}) => ({ + auth: pathToJS(firebase, 'auth'), account: pathToJS(firebase, 'profile'), - todos: dataToJS(firebase, 'todos'), - // todos: orderedToJS(firebase, 'todos') // if looking for array - // todos: dataToJS(firebase, 'myTodos'), // if using storeAs - // todos: populatedDataToJS(firebase, 'todos', populates), // if populating - // todos: orderedToJS(firebase, '/todos') // if using ordering such as orderByChild })) export default class Home extends Component { static propTypes = { - todos: PropTypes.oneOfType([ - PropTypes.object, // object if using dataToJS - PropTypes.array // array if using orderedToJS - ]), firebase: PropTypes.shape({ - set: PropTypes.func.isRequired, - remove: PropTypes.func.isRequired, - push: PropTypes.func.isRequired, + set: PropTypes.func.isRequired, + remove: PropTypes.func.isRequired, + push: PropTypes.func.isRequired, database: PropTypes.oneOfType([PropTypes.object, PropTypes.func]) }), - auth: PropTypes.shape({ + auth: PropTypes.shape({ uid: PropTypes.string }) } @@ -58,93 +35,23 @@ export default class Home extends Component { error: null } - toggleDone = (todo, id) => { - const { firebase, auth } = this.props - if (!auth || !auth.uid) { - return this.setState({ error: 'You must be Logged into Toggle Done' }) - } - return firebase.set(`/todos/${id}/done`, !todo.done) - } - - deleteTodo = id => { - const { todos, auth, firebase } = this.props - if (!auth || !auth.uid) { - return this.setState({ error: 'You must be Logged into Delete' }) - } - // return this.setState({ error: 'Delete example requires using populate' }) - // only works if populated - if (todos[id].owner !== auth.uid) { - return this.setState({ error: 'You must own todo to delete' }) - } - return firebase.remove(`/todos/${id}`).catch(err => { - console.error('Error removing todo: ', err) // eslint-disable-line no-console - this.setState({ error: 'Error Removing todo' }) - return Promise.reject(err) - }) - } - - handleAdd = newTodo => { - // Attach user if logged in - if (this.props.auth) { - newTodo.owner = this.props.auth.uid - } else { - newTodo.owner = 'Anonymous' - } - // attach a timestamp - newTodo.createdAt = this.props.firebase.database.ServerValue.TIMESTAMP - // using this.props.firebase.pushWithMeta here instead would automatically attach createdBy and createdAt - return this.props.firebase.push('/todos', newTodo) - } - render() { - const { todos } = this.props - const { error } = this.state + const {todos} = this.props + const {error} = this.state return (
{error ? this.setState({ error: null })} - /> + open={!!error} + message={error} + autoHideDuration={4000} + onRequestClose={() => this.setState({error: null})} + /> : null} -
- data loaded from - - - redux-firebasev3.firebaseio.com - - - - Note: - old data is removed - -
-
- - {!isLoaded(todos) - ? - : - Todos - - {todos && - map(todos, (todo, id) => - - )} - - } -
) } diff --git a/src/routes/Home/containers/HomeContainer.scss b/src/routes/Home/containers/HomeContainer.scss index eb74576..bb47715 100755 --- a/src/routes/Home/containers/HomeContainer.scss +++ b/src/routes/Home/containers/HomeContainer.scss @@ -3,14 +3,7 @@ @extend .flex-column-center; padding-top: 4rem; } -.todos { - @extend .flex-column-center; - margin-top: 4rem; -} .paper { padding: 1rem; margin-bottom: 4rem; -} -.info { - @extend .flex-column-center; -} +} \ No newline at end of file diff --git a/src/routes/Home/components/NewTodoPanel/NewTodoPanel.js b/src/routes/Todos/components/NewTodoPanel/NewTodoPanel.js similarity index 100% rename from src/routes/Home/components/NewTodoPanel/NewTodoPanel.js rename to src/routes/Todos/components/NewTodoPanel/NewTodoPanel.js diff --git a/src/routes/Home/components/NewTodoPanel/NewTodoPanel.scss b/src/routes/Todos/components/NewTodoPanel/NewTodoPanel.scss similarity index 100% rename from src/routes/Home/components/NewTodoPanel/NewTodoPanel.scss rename to src/routes/Todos/components/NewTodoPanel/NewTodoPanel.scss diff --git a/src/routes/Home/components/NewTodoPanel/index.js b/src/routes/Todos/components/NewTodoPanel/index.js similarity index 100% rename from src/routes/Home/components/NewTodoPanel/index.js rename to src/routes/Todos/components/NewTodoPanel/index.js diff --git a/src/routes/Home/components/TodoItem/TodoItem.js b/src/routes/Todos/components/TodoItem/TodoItem.js similarity index 100% rename from src/routes/Home/components/TodoItem/TodoItem.js rename to src/routes/Todos/components/TodoItem/TodoItem.js diff --git a/src/routes/Home/components/TodoItem/TodoItem.scss b/src/routes/Todos/components/TodoItem/TodoItem.scss similarity index 100% rename from src/routes/Home/components/TodoItem/TodoItem.scss rename to src/routes/Todos/components/TodoItem/TodoItem.scss diff --git a/src/routes/Home/components/TodoItem/index.js b/src/routes/Todos/components/TodoItem/index.js similarity index 100% rename from src/routes/Home/components/TodoItem/index.js rename to src/routes/Todos/components/TodoItem/index.js diff --git a/src/routes/Todos/containers/TodosContainer.js b/src/routes/Todos/containers/TodosContainer.js new file mode 100755 index 0000000..5d0c5e9 --- /dev/null +++ b/src/routes/Todos/containers/TodosContainer.js @@ -0,0 +1,140 @@ +import React, { Component, PropTypes } from 'react' +import { connect } from 'react-redux' +import { map } from 'lodash' +import Theme from 'theme' +import { + firebaseConnect, + isLoaded, + pathToJS, + dataToJS, // needed for full list and once + // orderedToJS, // needed for ordered list + // populatedDataToJS // needed for populated list +} from 'react-redux-firebase' +import CircularProgress from 'material-ui/CircularProgress' +import Snackbar from 'material-ui/Snackbar' +import { List } from 'material-ui/List' +import Paper from 'material-ui/Paper' +import Subheader from 'material-ui/Subheader' +import TodoItem from '../components/TodoItem' +import NewTodoPanel from '../components/NewTodoPanel' +import classes from './TodosContainer.scss' + +const populates = [{ child: 'owner', root: 'users', keyProp: 'uid' }] + +@firebaseConnect([ + // 'todos' // sync full list of todos + // { path: 'todos', type: 'once' } // for loading once instead of binding + { path: 'todos', queryParams: ['orderByKey', 'limitToLast=5'] } // 10 most recent + // { path: 'todos', populates } // populate + // { path: 'todos', storeAs: 'myTodos' } // store elsewhere in redux +]) +@connect(({ firebase }) => ({ + auth: pathToJS(firebase, 'auth'), + account: pathToJS(firebase, 'profile'), + todos: dataToJS(firebase, 'todos'), + // todos: orderedToJS(firebase, 'todos') // if looking for array + // todos: dataToJS(firebase, 'myTodos'), // if using storeAs + // todos: populatedDataToJS(firebase, 'todos', populates), // if populating + // todos: orderedToJS(firebase, '/todos') // if using ordering such as orderByChild +})) +export default class Home extends Component { + static propTypes = { + todos: PropTypes.oneOfType([ + PropTypes.object, // object if using dataToJS + PropTypes.array // array if using orderedToJS + ]), + firebase: PropTypes.shape({ + set: PropTypes.func.isRequired, + remove: PropTypes.func.isRequired, + push: PropTypes.func.isRequired, + database: PropTypes.oneOfType([PropTypes.object, PropTypes.func]) + }), + auth: PropTypes.shape({ + uid: PropTypes.string + }) + } + + state = { + error: null + } + + toggleDone = (todo, id) => { + const { firebase, auth } = this.props + if (!auth || !auth.uid) { + return this.setState({ error: 'You must be Logged into Toggle Done' }) + } + return firebase.set(`/todos/${id}/done`, !todo.done) + } + + deleteTodo = id => { + const { todos, auth, firebase } = this.props + if (!auth || !auth.uid) { + return this.setState({ error: 'You must be Logged into Delete' }) + } + // return this.setState({ error: 'Delete example requires using populate' }) + // only works if populated + if (todos[id].owner !== auth.uid) { + return this.setState({ error: 'You must own todo to delete' }) + } + return firebase.remove(`/todos/${id}`).catch(err => { + console.error('Error removing todo: ', err) // eslint-disable-line no-console + this.setState({ error: 'Error Removing todo' }) + return Promise.reject(err) + }) + } + + handleAdd = newTodo => { + // Attach user if logged in + if (this.props.auth) { + newTodo.owner = this.props.auth.uid + } else { + newTodo.owner = 'Anonymous' + } + // attach a timestamp + newTodo.createdAt = this.props.firebase.database.ServerValue.TIMESTAMP + // using this.props.firebase.pushWithMeta here instead would automatically attach createdBy and createdAt + return this.props.firebase.push('/todos', newTodo) + } + + render() { + const { todos } = this.props + const { error } = this.state + + return ( +
+ {error + ? this.setState({ error: null })} + /> + : null} +
+
+ + {!isLoaded(todos) + ? + : + Todos + + {todos && + map(todos, (todo, id) => + + )} + + } +
+
+ ) + } +} diff --git a/src/routes/Todos/containers/TodosContainer.scss b/src/routes/Todos/containers/TodosContainer.scss new file mode 100755 index 0000000..eb74576 --- /dev/null +++ b/src/routes/Todos/containers/TodosContainer.scss @@ -0,0 +1,16 @@ +@import 'base'; +.container { + @extend .flex-column-center; + padding-top: 4rem; +} +.todos { + @extend .flex-column-center; + margin-top: 4rem; +} +.paper { + padding: 1rem; + margin-bottom: 4rem; +} +.info { + @extend .flex-column-center; +} diff --git a/src/routes/Todos/index.js b/src/routes/Todos/index.js new file mode 100755 index 0000000..30f0c25 --- /dev/null +++ b/src/routes/Todos/index.js @@ -0,0 +1,8 @@ +import TodosContainer from './containers/TodosContainer' +import { TODOS_PATH as path } from 'constants' + +// Sync route definition +export default { + path, + component: TodosContainer +} diff --git a/src/routes/index.js b/src/routes/index.js index 2292497..47330c0 100755 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -1,6 +1,7 @@ // We only need to import the modules necessary for initial render import CoreLayout from '../layouts/CoreLayout/CoreLayout' import Home from './Home' +import Todos from './Todos' import LoginRoute from './Login' import SignupRoute from './Signup' import ProjectsRoute from './Projects' @@ -18,6 +19,7 @@ export const createRoutes = store => ({ AccountRoute, LoginRoute, SignupRoute, + Todos, ProjectsRoute(store), // async route definitions recieve store RecoverRoute(store) // async route definitions recieve store ] diff --git a/src/static/beetimer_yellow_128.png b/src/static/beetimer_yellow_128.png new file mode 100644 index 0000000..35b2cb3 Binary files /dev/null and b/src/static/beetimer_yellow_128.png differ diff --git a/src/theme.js b/src/theme.js index 317d646..0de1101 100644 --- a/src/theme.js +++ b/src/theme.js @@ -1,6 +1,6 @@ import { blueGrey100, - blueGrey500, + indigo500, blueGrey700, pinkA200, tealA100, @@ -19,7 +19,7 @@ export default { zIndex: zIndex, fontFamily: 'Roboto, sans-serif', palette: { - primary1Color: blueGrey500, + primary1Color: indigo500, primary2Color: blueGrey700, primary3Color: blueGrey100, accent1Color: pinkA200, diff --git a/yarn.lock b/yarn.lock index f29d6f2..866c980 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1054,8 +1054,8 @@ boom@2.x.x: hoek "2.x.x" bowser@^1.0.0: - version "1.7.2" - resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.2.tgz#b94cc6925ba6b5e07c421a58e601ce4611264572" + version "1.7.3" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-1.7.3.tgz#504bdb43118ca8db9cbbadf28fd60f265af96e4f" brace-expansion@^1.1.7: version "1.1.8" @@ -4131,7 +4131,7 @@ node-pre-gyp@^0.6.36: tar "^2.2.1" tar-pack "^3.4.0" -node-sass@^3.7.0: +node-sass@3.13.1: version "3.13.1" resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-3.13.1.tgz#7240fbbff2396304b4223527ed3020589c004fc2" dependencies: @@ -5287,7 +5287,7 @@ right-align@^0.1.1: dependencies: align-text "^0.1.1" -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.5.4, rimraf@^2.6.1: +rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" dependencies: