Skip to content

Commit

Permalink
Feature: add basic functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
cerebrl committed Aug 14, 2019
1 parent c242fdb commit ddb09fb
Show file tree
Hide file tree
Showing 24 changed files with 224 additions and 871 deletions.
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
# "Offline-First" TodoMVC Example

## Progress

- [ ] Feature: Add routing capability
- [ ] Feature: Add edit capability
- [ ] Feature: Add all bulk functionality: complete all, delete completed
- [x] Feature: Add all basic todo functionality: add, complete, delete
- [x] Get the app to rerender when the PouchDB is updated
- [x] Get the TodoMVC app to render todos stored within IndexedDB using PouchDB
- [x] Initialize the application from [this previous TodoMVC application](https://github.com/cerebralideas/todomvc-universal-react-pouchdb)


## Up and Running

1. Install the dependencies found in the `package.json`
2. Compile the TypeScript to JavaScript
3. Run the server

```
npm install
npm run build
```

4. Open `public/index.html` in your browser.
3. Open `public/index.html` in your browser.

## What's an "offline-first" application?

Expand Down Expand Up @@ -92,9 +102,3 @@ _If you have other helpful links to share, or find any of the links above no lon
## How it works

Coming soon ...

## TODO

- [ ] Get the app to rerender when the PouchDB is updated
- [x] Get the TodoMVC app to render todos stored within IndexedDB using PouchDB
- [x] Initialize the application from [this previous TodoMVC application](https://github.com/cerebralideas/todomvc-universal-react-pouchdb)
13 changes: 0 additions & 13 deletions client/actions/index.ts

This file was deleted.

14 changes: 8 additions & 6 deletions client/components/footer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { AppContext } from '../store/state-mgmt';
import { completeAll, clearCompleted } from '../events/client-events';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/todo-filters';
import { AppContext } from '../state/state-mgmt';
import { completeAll, clearCompleted } from '../events';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants';

import { State } from '../interfaces';
import { State, Todo } from '../interfaces';

let FILTER_TITLES = {
[SHOW_ALL]: 'All',
Expand All @@ -16,9 +16,10 @@ interface Props {
count?: number;
filter?: string;
active?: number;
todos?: Todo[]
}

function Footer({ filter, count, completed, active }: Props) {
function Footer({ filter, count, completed, active, todos }: Props) {

if (count) {
function RenderTodoCount() {
Expand Down Expand Up @@ -48,7 +49,7 @@ function Footer({ filter, count, completed, active }: Props) {
if (completed > 0) {
return (
<button className="clear-completed"
onClick={ () => clearCompleted() }>
onClick={ (event) => clearCompleted(event, todos) }>
Clear completed
</button>
);
Expand Down Expand Up @@ -93,6 +94,7 @@ export default function () {
count={ count }
completed={ completed }
active={ active }
todos={ todos }
/>
);
}}
Expand Down
2 changes: 1 addition & 1 deletion client/components/header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { AppContext } from '../store/state-mgmt';
import { AppContext } from '../state/state-mgmt';
import TodoTextInput from './todo-text-input';

import { State } from '../interfaces';
Expand Down
12 changes: 6 additions & 6 deletions client/components/todo-item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useState } from 'react';
import TodoTextInput from './todo-text-input';
import { deleteTodo, completeTodo } from '../events/client-events';
import { deleteTodo, completeTodo } from '../events';

import { Todo } from '../interfaces';

Expand Down Expand Up @@ -45,29 +45,29 @@ export default function TodoItem ({ todo, filter }: Prop) { // save
<div className="view">
<form id="completeForm"
method="POST"
action={ `/todos/${ todo.id }?type=COMPLETE_TODO&filter=${ filter }` }>
action={ `/todos/${ todo._id }?type=COMPLETE_TODO&filter=${ filter }` }>
<input id="completeTodo"
className="toggle"
type="checkbox"
checked={ todo.completed }
onChange={ (event) => completeTodo(event, todo.id) } />
onChange={ (event) => completeTodo(event, todo._id) } />
<label htmlFor="completeTodo">
<button type="submit"
style={ cssCompleteButton }
onClick={ (event) => completeTodo(event, todo.id) }
onClick={ (event) => completeTodo(event, todo._id) }
onDoubleClick={ () => flipEdit(true) }>
{todo.title}
</button>
</label>
</form>
<form id="deleteForm"
method="POST"
action={ `/todos/${ todo.id }?type=DELETE_TODO&filter=${ filter }` }
action={ `/todos/${ todo._id }?type=DELETE_TODO&filter=${ filter }` }
style={ cssDestroyForm }>
<button type="submit"
style={ cssDestroyEl}
className="destroy"
onClick={ (event) => deleteTodo(event, todo.id) }>
onClick={ (event) => deleteTodo(event, todo._id) }>
<span style={ cssDestroyText }>Delete Todo</span>
</button>
</form>
Expand Down
6 changes: 3 additions & 3 deletions client/components/todo-list.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import { AppContext } from '../store/state-mgmt';
import { AppContext } from '../state/state-mgmt';
import TodoItem from './todo-item';
import ToggleAll from './toggle-all';
import Footer from './footer';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants/todo-filters';
import { SHOW_ALL, SHOW_COMPLETED, SHOW_ACTIVE } from '../constants';

import { State } from '../interfaces';

Expand All @@ -21,7 +21,7 @@ export function TodoList({ todos, filter }: State) {
<ToggleAll />
<ul className="todo-list">
{filteredTodos.map(todo => (
<TodoItem key={ todo.id } todo={ todo } filter={ filter } />
<TodoItem key={ todo._id } todo={ todo } filter={ filter } />
))}
</ul>
<Footer />
Expand Down
6 changes: 3 additions & 3 deletions client/components/todo-text-input.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { formSubmission } from '../events/client-events';
import { formSubmission } from '../events';

import { Todo } from '../interfaces';

Expand Down Expand Up @@ -34,7 +34,7 @@ function TodoTextInput({
method='POST'
action={ `/todos?=filter=${filter ? filter : '' }`}
onSubmit={
(event) => formSubmission(event, todo && todo.id, flipEdit)
(event) => formSubmission(event, todo && todo._id, flipEdit)
}>

<input id="todoInput"
Expand All @@ -44,7 +44,7 @@ function TodoTextInput({
placeholder={ placeholder }
defaultValue={ todo.title }
autoFocus={ editing }
onBlur={ (event) => saveChange(event, todo.id) } />
onBlur={ (event) => saveChange(event, todo._id) } />
</form>
);
}
Expand Down
13 changes: 7 additions & 6 deletions client/components/toggle-all.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import React from 'react';
import { AppContext } from '../store/state-mgmt';
import { completeAll } from '../events/client-events';
import { AppContext } from '../state/state-mgmt';
import { completeAll } from '../events';

import { State } from '../interfaces';
import { State, Todo } from '../interfaces';

interface Props {
count: number;
completed: number;
todos: Todo[]
}

function ToggleAll({ count, completed }: Props) {
function ToggleAll({ count, completed, todos }: Props) {
if (count > 0) {
return (
<>
<input id="toggle-all"
className="toggle-all"
type="checkbox"
checked={ completed === count }
onChange={ completeAll }/>
onChange={ event => completeAll(event, todos) }/>
<label htmlFor="toggle-all" />
</>
);
Expand All @@ -36,7 +37,7 @@ export default function () {
),
0
);
return <ToggleAll count={ count } completed={ completed } />
return <ToggleAll count={ count } completed={ completed } todos={ todos } />
}}
</AppContext.Consumer>
);
Expand Down
File renamed without changes.
50 changes: 0 additions & 50 deletions client/events/client-events.ts

This file was deleted.

83 changes: 83 additions & 0 deletions client/events/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import db from '../state/store';

/** *******************************
* Client & Server db Sync actions
*/
export function formSubmission(event, _id: string, flipEdit?): void {
let title: string =
event.currentTarget.id === 'todoForm'
? event.currentTarget.elements.todoInput.value
: event.currentTarget.value;

event.preventDefault();

// If adding an empty todo, just return out.
if (!_id && !title) {
return;
} else if (_id && title.length) {
// Was this an edit?
// Fire action on db
db.get(_id).then((doc) => {
db.put({
...doc,
title
}).then((response) => {
console.log('Todo has been updated');
});
});
} else if (_id && !title) {
// If provided id, but title is empty delete item
// Fire action on db
db.get(_id).then((doc) => {
db.put({
...doc,
_deleted: true
}).then((response) => {
console.log('Todo has been deleted');
});
});
} else {
// This was a new item
// Fire action on db
db.put({
_id: new Date().toJSON(),
completed: false,
title: title
}).then((response) => {
console.log('Todo has been saved');
});
event.currentTarget.elements.todoInput.value = '';
}
}
export function completeTodo(event, _id: string): void {
event.preventDefault();
// Fire action on db
db.get(_id).then((doc: any) => {
db.put({
...doc,
completed: !doc.completed
}).then((response) => {
console.log('Todo has been completed');
});
});
}
export function deleteTodo(event, _id: string): void {
event.preventDefault();
// Fire action on db
db.get(_id).then((doc) => {
db.put({
...doc,
_deleted: true
}).then((response) => {
console.log('Todo has been deleted');
});
});
}
export function completeAll(event, todos): void {
// Fire action on db
return;
}
export function clearCompleted(event, todos): void {
// Fire action on db
return;
}
11 changes: 7 additions & 4 deletions client/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './views/app';
import db from './store/store';
import db from './state/store';

import 'todomvc-app-css/index.css';
import './views/app.css';
Expand All @@ -14,17 +14,20 @@ if (WEBPACK_ENV !== 'production') {

document.addEventListener("DOMContentLoaded", (): void => {

db.allDocs({ include_docs: true })
db.allDocs({
include_docs: true,
descending: true
})
.then((response) => {
const state = {
const data = {
todos: response.rows ?
response.rows.map((row) => row.doc):
[],
filter: "show_all"
};

ReactDOM.render(
<App state={ state } />,
<App initialData={ data } />,
document.getElementById('root')
);
})
Expand Down
Loading

0 comments on commit ddb09fb

Please sign in to comment.