Skip to content

Commit 58a8bd9

Browse files
committed
Finish todo-pwa
1 parent f037b8e commit 58a8bd9

File tree

9 files changed

+63
-30
lines changed

9 files changed

+63
-30
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ A couple of projects to get familiar with the React framework.
1616
6. **book-store [WIP]**: Online book store built over react-redux and Bootstrap for UI.
1717
7. **trip-mate**: Manage and keep track of your trips with this simple, smooth webapp. Uses react-router, custom styling. [(Live Demo)](https://react-trip-mate.firebaseapp.com)
1818
8. **property-finder**: Search for and list properties. Built on React-Native, uses react-navigation.
19-
9. **todo-pwa**: The classic Todos Tracker as a Progressive Web App. Built using React, Redux, leveraging styled-components, PaperCSS, Service Worker tools. [(Live Demo)](https://paper-todo.firebaseapp.com)
19+
9. **todo-pwa**: The classic Todos Tracker as a Progressive Web App. Built using React, Redux, leveraging styled-components, PaperCSS, Service Worker tools. Data is retained using localStorage [(Live Demo)](https://paper-todo.firebaseapp.com)
2020

2121

2222
## Development

todo-pwa/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44

55
[Live Demo](https://paper-todo.firebaseapp.com)
66

7-
The classic Todos Tracker as a Progressive Web App. Built using React, Redux, leveraging styled-components, PaperCSS, Service Worker tools.
7+
The classic Todos Tracker as a Progressive Web App. Built using React, Redux, leveraging styled-components, PaperCSS, Service Worker tools. Data is retained using localStorage.
88

99
## Features
1010

1111
* Cross-browser, cross-platform support
1212
* Installable web app
13-
* Offline functionality
13+
* Offline functionality with local storage
1414
* Fancy yet simple UI
1515
* Lightweight
1616

@@ -26,7 +26,7 @@ Serve the app to browser
2626

2727
## Checklist
2828

29-
- [ ] Use localstorage to store todos
29+
- [x] Use localstorage to store todos
3030
- [ ] Improve app responsiveness on all devices
3131

3232
## Contributing

todo-pwa/src/actions/index.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
// Action creators recieve data from the DOM event, format it as formal JSON 'action' object
22

3-
import { ADD_TODO, SET_FILTER, TOGGLE_TODO } from '../constants/action-types';
3+
import { ADD_TODO, SET_FILTER, TOGGLE_TODO, CLEAR_TODOS } from '../constants/action-types';
44

55
export const addTodo = (payload) => ({
66
type: ADD_TODO,
77
payload
88
});
99

10+
export const clearTodos = () => ({
11+
type: CLEAR_TODOS
12+
});
13+
1014
export const setFilter = (filter) => ({
1115
type: SET_FILTER,
1216
filter

todo-pwa/src/components/AddTodo.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ const AddTodo = ({onSubmit}) => {
3030
}}
3131
className="row"
3232
>
33-
<div className="col col-xs-9 padding-right-small">
33+
<div className="col padding-right-small">
3434
<input type="text" placeholder="New Todo" ref={node => { input = node }} />
3535
</div>
36-
<div className="col col-xs-3 padding-left-small">
36+
<div className="col padding-left-small">
3737
<input type="submit" value="Add" className="paper-btn btn-small" />
3838
</div>
3939
</form>

todo-pwa/src/components/Filter.js

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,36 @@ import React from 'react';
22
import { connect } from 'react-redux';
33
import styled from 'styled-components';
44

5-
import { setFilter } from '../actions';
5+
import { setFilter, clearTodos } from '../actions';
66
import { FILTER_COMPLETED, FILTER_ALL, FILTER_ACTIVE } from '../constants/filters';
77

88
const mapDispatchToProps = (dispatch) => {
99
return {
10-
onChangeFilter: (e) => dispatch(setFilter(e.target.value))
10+
onChangeFilter: (e) => dispatch(setFilter(e.target.value)),
11+
onClickClear: (e) => dispatch(clearTodos())
1112
}
1213
}
1314

1415
const Dropdown = styled.div`
1516
margin: 3em 0 0 0;
1617
`;
1718

18-
const Filter = ({onChangeFilter}) => {
19+
const Filter = ({todosLength, filter, onChangeFilter, onClickClear}) => {
1920
return (
20-
<Dropdown className="row flex-right">
21-
<span className="padding-right-small">Show:</span>
22-
<select onChange={onChangeFilter}>
23-
<option value={FILTER_ALL}>All</option>
24-
<option value={FILTER_ACTIVE}>Active</option>
25-
<option value={FILTER_COMPLETED}>Completed</option>
26-
</select>
21+
<Dropdown className="row flex-edges">
22+
<div className="col padding-small">
23+
<button className="btn-small" style={todosLength ? {} : {display: 'none'}} onClick={onClickClear}>Clear</button>
24+
</div>
25+
<div className="col padding-small">
26+
<div className="row flex-right margin-none">
27+
<span className="padding-right-small">Show:</span>
28+
<select onChange={onChangeFilter} value={filter}>
29+
<option value={FILTER_ALL}>All</option>
30+
<option value={FILTER_ACTIVE}>Active</option>
31+
<option value={FILTER_COMPLETED}>Completed</option>
32+
</select>
33+
</div>
34+
</div>
2735
</Dropdown>
2836
);
2937
}

todo-pwa/src/components/TodoList.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,20 @@ const List = styled.ul`
3333
`;
3434

3535
const TodoList = ({todos, filter}) => {
36-
if(todos.length === 0 && filter === FILTER_ALL)
37-
return <div className="row flex-center margin-top-large">Chill scenes.</div>
36+
let message;
37+
if(todos.length === 0) {
38+
let text;
39+
switch(filter) {
40+
case FILTER_COMPLETED: text = 'Nothing completed yet.'; break;
41+
case FILTER_ACTIVE: text = 'Nothing to be completed.'; break;
42+
default: text = 'Chill scenes.'; break;
43+
}
44+
message = (<div className="row flex-center margin-top-large">{text}</div>);
45+
}
3846

3947
return (
4048
<div>
49+
{message}
4150
<List className="child-borders">
4251
{todos.map(todo =>
4352
<Todo
@@ -47,7 +56,7 @@ const TodoList = ({todos, filter}) => {
4756
/>
4857
)}
4958
</List>
50-
<Filter/>
59+
<Filter todosLength={todos.length} filter={filter}/>
5160
</div>
5261
);
5362
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Using constants instead of strings used directly prevents errors due to typos etc
22

33
export const ADD_TODO = 'ADD_TODO';
4+
export const CLEAR_TODOS = 'CLEAR_TODOS';
45
export const SET_FILTER = 'SET_FILTER';
56
export const TOGGLE_TODO = 'TOGGLE_TODO';

todo-pwa/src/reducers/filter.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { SET_FILTER } from '../constants/action-types';
22
import { FILTER_ALL } from '../constants/filters';
33

4-
const filter = (state = FILTER_ALL, action) => {
4+
const storedFilter = localStorage.getItem('filter') || FILTER_ALL;
5+
6+
const filter = (state = storedFilter, action) => {
57
switch(action.type) {
68
case SET_FILTER:
9+
localStorage.setItem('filter', action.filter);
710
return action.filter;
811
default:
912
return state;

todo-pwa/src/reducers/todos.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,29 @@
11
// Reducer functions take state from Redux and action objects and returns a new state
22
// Each action is defined as a case
33

4-
import { ADD_TODO, TOGGLE_TODO } from '../constants/action-types';
4+
import { ADD_TODO, TOGGLE_TODO, CLEAR_TODOS } from '../constants/action-types';
55

6-
const todos = (state = [], action) => {
6+
const storedTodos = JSON.parse(localStorage.getItem('todos')) || [];
7+
8+
const todos = (state = storedTodos, action) => {
79
switch (action.type) {
810
case ADD_TODO:
11+
localStorage.setItem('todos', JSON.stringify([...state, action.payload]));
912
return [...state, action.payload];
13+
14+
case CLEAR_TODOS:
15+
localStorage.removeItem('todos');
16+
return [];
1017

1118
case TOGGLE_TODO:
12-
return state.map(todo => {
13-
if (todo.id === action.id) {
14-
return Object.assign({}, todo, { completed: !todo.completed });
15-
}
16-
return todo;
17-
}
18-
);
19+
let modifiedTodos = state.map(todo => {
20+
if (todo.id === action.id)
21+
return Object.assign({}, todo, { completed: !todo.completed });
22+
return todo;
23+
}
24+
);
25+
localStorage.setItem('todos', JSON.stringify(modifiedTodos));
26+
return modifiedTodos;
1927

2028
default:
2129
return state;

0 commit comments

Comments
 (0)