base: setup react redux app

This commit is contained in:
André Neves 2018-11-23 13:41:50 -05:00
commit 68d781002a
41 changed files with 8160 additions and 0 deletions

11
.babelrc Normal file
View File

@ -0,0 +1,11 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react",
"@babel/preset-flow"
],
"plugins": [
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-object-rest-spread"
]
}

55
.eslintrc Normal file
View File

@ -0,0 +1,55 @@
{
"parser": "babel-eslint",
"extends": [
"airbnb",
"plugin:flowtype/recommended"
],
"env": {
"browser": true,
"node": true,
"mocha": true
},
"plugins": ["flowtype"],
"settings": {
"flowtype": {
"onlyFilesWithFlowAnnotation": true
}
},
"rules": {
"jsx-quotes": ["error", "prefer-single"],
"import/prefer-default-export": [
"off"
],
"react/jsx-filename-extension": [
1,
{ "extensions": [".js"] }
],
"jsx-a11y/anchor-is-valid": [
"error",
{
"components": [
"Link"
],
"specialLink": [
"to",
"hrefLeft",
"hrefRight"
],
"aspects": [
"noHref",
"invalidHref",
"preferButton"
]
}
],
"jsx-a11y/no-autofocus": [ 0, {
"ignoreNonDOM": true
}],
"max-len": ["error", {
"ignoreUrls": true,
"ignoreComments": true,
"ignoreStrings": true,
"ignorePattern": "<p[^>]*>.*?</p>"
}]
}
}

11
.flowconfig Normal file
View File

@ -0,0 +1,11 @@
[ignore]
[include]
[libs]
[lints]
[options]
[strict]

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
node_modules
.vscode
dist
webpack-stats/*
.DS_Store

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Zcash Foundation
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

30
NODE.md Normal file
View File

@ -0,0 +1,30 @@
# Zcash Node Setup
## Installation
Download the attached files, verify checksum of the archive and extract
```bash
shasum -a 256 -c zcash-macos-v2.0.1a.tar.bz2.hash
tar -xvf zcash-macos-v2.0.1a.tar.bz2
```
## How to use
When launching Zcash on MacOS for the first time, certain initialization steps should be completed.
Please run the commands below once for the first time
```
cd zcash-macos-v2.0.1a/usr/local/bin
./zcash-fetch-params
./zcash-init
./zcashd
```
You can just run Zcash by launching the daemon afterwards:
```
./zcashd
```
You can refer to NODE_COMMANDS for basic usage

59
NODE_COMMANDS.md Normal file
View File

@ -0,0 +1,59 @@
# for detailed information use the guides below
https://github.com/zcash/zcash/wiki/1.0-User-Guide
https://github.com/zcash/zcash/blob/master/doc/payment-api.md
https://en.bitcoin.it/wiki/Original_Bitcoin_client/API_calls_list
# list info for a command
$ ./zcash-cli help <command>
# list all accounts (therefore all t-addr's)
$ ./zcash-cli listreceivedbyaddress 0 true
# list of t-addr's for default account (again all t-addr's)
$ ./zcash-cli getaddressesbyaccount ""
# list all unspent transaction outputs (t-addr UTXO's)
$ ./zcash-cli listunspent
# create new t-addr's
$ ./zcash-cli getnewaddress
# list all z-addr's
$ ./zcash-cli z_listaddresses
# create new z-addr's
$ ./zcash-cli z_getnewaddress
# get total balance
$ ./zcash-cli z_gettotalbalance
# send funds
$ export TADDR='t1TGVDzsEK2qbG1N8FJQFSAzV1bWWHMGGCS'
$ export ZADDR='zcNeXiyD3JkhKTrU38xM9C6HQGy9aP5qqVFH25qFzQGnmdwYZ2Dr53Jy7iRp64D4CzkMZdmKagN6mmtu3jVKHuZ8xZp8fw3'
$ export FRIEND='zcfZJW3qLHpSc7q7W1SXRGdVjgM6Q6kRwdkz1DHW5sP2EqcMHf5RCp3Frpf2qnb81j9K6upzRN4HoVxfboVwLTRaZ7bKn8b'
# send from t-addr to z-addr (with memo and fee)
$ ./zcash-cli z_sendmany "$TADDR" "[{\"address\": \"$ZADDR\", \"amount\": 0.1, \"memo\": \"0123456789\"}]" 1 0.002
# send from t-addr to t-addr (with fee)
$ ./zcash-cli z_sendmany "$TADDR1" "[{\"address\": \"$TADDR2\", \"amount\": 0.09}]" 1 0.002
# send from z-addr to z-addr (with memo and fee)
$ ./zcash-cli z_sendmany "$ZADDR" "[{\"address\": \"$FRIEND\", \"amount\": 0.05, \"memo\": \"9876543210\"}]" 1 0.002
# get send result
$ ./zcash-cli z_getoperationresult [\"$OPID\"]
# list amounts received by z-addr
$ ./zcash-cli z_listreceivedbyaddress "$ZADDR"
# list balance both for t-addr and z-addr
$ ./zcash-cli z_getbalance "$TADDR"

39
README.md Normal file
View File

@ -0,0 +1,39 @@
# Zcash Reference Wallet
Reference Wallet for the Zcash Network
## Stack Information
- [React](https://facebook.github.io/react/): UI view layer
- [Redux](http://redux.js.org/): predictable state container
- [Webpack](http://webpack.github.io/): module bundler
- [Webpack-Dev-Server](https://webpack.github.io/docs/webpack-dev-server.html): development server
- [Babel](http://babeljs.io/): ES6/JSX transpilling
- [ESLint](http://eslint.org/): code rules and linting
- [React Router](https://github.com/reactjs/react-router): routing solution for react
## Installation
```bash
yarn install
```
## Development
To run the application on port 8080
```bash
yarn dev
```
## Webpack Bundle Analysis
To visualize the bundle size of your application
```bash
yarn analyze
```
## License
© Zcash Foundation 2018

15
app/actions/add-todo.js Normal file
View File

@ -0,0 +1,15 @@
// @flow
import uuidv4 from 'uuid/v4';
import { ADD_TODO } from '../constants/actions';
import { getTimestamp } from '../utils/timestamp';
export const addTodo = (text: string) => ({
type: ADD_TODO,
payload: {
text,
id: uuidv4(),
editing: false,
createdAt: getTimestamp(),
},
});

View File

@ -0,0 +1,8 @@
// @flow
import { CANCEL_UPDATE_TODO } from '../constants/actions';
export const cancelUpdateTodo = (id: string) => ({
type: CANCEL_UPDATE_TODO,
payload: { id },
});

View File

@ -0,0 +1,8 @@
// @flow
import { DELETE_TODO } from '../constants/actions';
export const deleteTodo = (id: string) => ({
type: DELETE_TODO,
payload: { id },
});

View File

@ -0,0 +1,8 @@
// @flow
import { TOGGLE_EDIT_TODO } from '../constants/actions';
export const toggleEdit = (id: string) => ({
type: TOGGLE_EDIT_TODO,
payload: { id },
});

View File

@ -0,0 +1,11 @@
// @flow
import { UPDATE_TODO } from '../constants/actions';
export const updateTodo = (id: string, text: string) => ({
type: UPDATE_TODO,
payload: {
text,
id,
},
});

15
app/app.js Normal file
View File

@ -0,0 +1,15 @@
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter } from 'react-router-dom';
import configureStore from './store/configure';
import Router from './router';
const store = configureStore({});
export default () => (
<BrowserRouter>
<Provider store={store}>
<Router />
</Provider>
</BrowserRouter>
);

View File

@ -0,0 +1 @@
<svg id="Layer_1" xmlns="http://www.w3.org/2000/svg" viewBox="-793 545.5 96 100"><style>.st0{fill:#ffffff}</style><path class="st0" d="M-711.1 556.6h-63.8c-3.5 0-6.3 2.9-6.3 6.6v75.5c0 3.6 2.8 6.6 6.3 6.6h63.8c3.5 0 6.3-2.9 6.3-6.6v-75.5c0-3.6-2.8-6.6-6.3-6.6zm.8 79s.2 1.5-.5 2.3c-.7.8-2.1.7-2.1.7h-60.7s-.9 0-1.5-.8c-.6-.6-.6-2-.6-2v-70.4s0-1 .6-1.6c.7-.6 1.6-.7 1.6-.7h60.7s1.2 0 1.7.5c.7.7.7 1.5.7 1.5l.1 70.5zM-737.3 547.8c0 1.4-1.3 2.5-2.8 2.5h-5.6c-1.5 0-2.8-1.1-2.8-2.5s1.3-2.5 2.8-2.5h5.6c1.6 0 2.8 1.2 2.8 2.5z"/><path class="st0" d="M-730.9 553.3c0-1.5-.1-3.9-1.3-3.9h-21.5c-1.1 0-1.4 2.4-1.4 3.9h24.2z"/><path class="st0" d="M-725.7 558.3c0-2.1-.2-5.6-1.9-5.6h-30.8c-1.6 0-2 3.5-1.9 5.5l34.6.1zM-755.8 577.6h35.4v3.2h-35.4zM-767 588.8v7.9h7.9v-7.9h-7.9zm1.2 6.7V590h5.5v5.5h-5.5zM-755.8 592.6h35.4v3.2h-35.4zM-755.8 606.9h35.4v3.2h-35.4zM-755.8 620.9h35.4v3.1h-35.4zM-767 573.9v7.9h7.9v-7.9h-7.9zm1.2 6.7V575h5.5v5.5h-5.5zM-767 603.2v7.9h7.9v-7.9h-7.9zm1.2 6.7v-5.5h5.5v5.5h-5.5zM-767 617.2v7.9h7.9v-7.9h-7.9zm1.2 6.7v-5.5h5.5v5.5h-5.5z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,81 @@
// @flow
import React, { Component } from 'react';
import type { TodoType } from '../../types/todo';
type Props = {
updateTodo: Function,
todo: TodoType,
cancelUpdateTodo: Function,
};
type State = {
value: string,
};
export default class TodoEditInput extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
value: props.todo.text || '',
};
}
handleSubmit = (event: SyntheticInputEvent<HTMLInputElement>, id: string) => {
const { value } = this.state;
const { updateTodo } = this.props;
const trimValue = value.trim();
event.preventDefault();
if (trimValue !== '') {
updateTodo(id, trimValue);
this.setState({ value: '' });
}
}
handleCancel = (id: string) => {
const { cancelUpdateTodo } = this.props;
cancelUpdateTodo(id);
}
handleInputChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
const { target: { value } } = event;
this.setState({ value });
}
render() {
const { value } = this.state;
const { todo } = this.props;
return (
<div className='todo-item__view todo-item__view--edit'>
<form
className='todo-item__input'
onSubmit={e => this.handleSubmit(e, todo.id)}
>
<input
value={value}
onChange={this.handleInputChange}
className='todo-item__input-field'
autoFocus
/>
<button
type='submit'
className='todo-item__input-button'
>
Update
</button>
</form>
<button
type='button'
className='todo-item__input-cancel'
onClick={() => this.handleCancel(todo.id)}
>
Cancel
</button>
</div>
);
}
}

View File

@ -0,0 +1,58 @@
// @flow
import React, { Component } from 'react';
type Props = {
addTodo: Function,
};
type State = {
value: string,
};
export default class TodoInput extends Component<Props, State> {
state = {
value: '',
};
handleSubmit = (event: SyntheticInputEvent<HTMLInputElement>) => {
const { value } = this.state;
const { addTodo } = this.props;
const trimValue = value.trim();
event.preventDefault();
if (trimValue !== '') {
addTodo(trimValue);
this.setState({ value: '' });
}
}
handleInputChange = (event: SyntheticInputEvent<HTMLInputElement>) => {
const { target: { value } } = event;
this.setState({ value });
}
render() {
const { value } = this.state;
return (
<form
className='todo__input'
onSubmit={this.handleSubmit}
>
<input
value={value}
onChange={this.handleInputChange}
className='todo__input-field'
/>
<button
type='submit'
className='todo__input-button'
>
Submit
</button>
</form>
);
}
}

View File

@ -0,0 +1,56 @@
// @flow
import React, { PureComponent } from 'react';
import type { TodoType } from '../../types/todo';
type Props = {
todo: TodoType,
deleteTodo: Function,
toggleEdit: Function,
};
export default class TodoListItem extends PureComponent<Props> {
handleDelete = (id: string) => {
if (!id) return;
const { deleteTodo } = this.props;
deleteTodo(id);
}
handleEditToggle = (id: string) => {
if (!id) return;
const { toggleEdit } = this.props;
toggleEdit(id);
}
render() {
const {
todo,
} = this.props;
return (
<div className='todo-item__view todo-item__view--view'>
<span className='todo-item__text'>
{todo.text}
</span>
<div className='todo-item__buttons'>
<button
type='button'
onClick={() => this.handleEditToggle(todo.id)}
className='todo-item__button'
>
<svg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 528.899 528.899'><path className='todo-item__svg' d='M328.883 89.125l107.59 107.589-272.34 272.34L56.604 361.465l272.279-272.34zm189.23-25.948l-47.981-47.981c-18.543-18.543-48.653-18.543-67.259 0l-45.961 45.961 107.59 107.59 53.611-53.611c14.382-14.383 14.382-37.577 0-51.959zM.3 512.69c-1.958 8.812 5.998 16.708 14.811 14.565l119.891-29.069L27.473 390.597.3 512.69z' /></svg>
</button>
<button
type='button'
onClick={() => this.handleDelete(todo.id)}
className='todo-item__button'
>
<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 512 512'><path className='todo-item__svg' fill='#1D1D1B' d='M459.232 60.687h-71.955c-1.121-17.642-15.631-31.657-33.553-31.657H161.669c-17.921 0-32.441 14.015-33.553 31.657H64.579c-18.647 0-33.767 15.12-33.767 33.768v8.442c0 18.648 15.12 33.768 33.767 33.768h21.04v342.113c0 13.784 11.179 24.963 24.963 24.963h308.996c13.784 0 24.964-11.179 24.964-24.963V136.665h14.691c18.663 0 33.768-15.12 33.768-33.768v-8.442c-.001-18.648-15.105-33.768-33.769-33.768zM196.674 443.725c0 12.58-10.197 22.803-22.802 22.803-12.598 0-22.803-10.223-22.803-22.803v-284.9c0-12.597 10.205-22.802 22.803-22.802 12.605 0 22.802 10.206 22.802 22.802v284.9zm91.213 0c0 12.58-10.205 22.803-22.803 22.803s-22.803-10.223-22.803-22.803v-284.9c0-12.597 10.205-22.802 22.803-22.802s22.803 10.206 22.803 22.802v284.9zm91.212 0c0 12.58-10.205 22.803-22.803 22.803-12.613 0-22.803-10.223-22.803-22.803v-284.9c0-12.597 10.189-22.802 22.803-22.802 12.598 0 22.803 10.206 22.803 22.802v284.9z' /></svg>
</button>
</div>
</div>
);
}
}

View File

@ -0,0 +1,73 @@
// @flow
import React, { PureComponent } from 'react';
import TodoEditInput from './todo-edit-input';
import TodoListItem from './todo-list-item';
import type { TodoType } from '../../types/todo';
type Props = {
todos: Array<TodoType>,
deleteTodo: Function,
toggleEdit: Function,
updateTodo: Function,
cancelUpdateTodo: Function,
};
export default class TodoList extends PureComponent<Props> {
renderTodoView = (todo: TodoType) => {
const { deleteTodo, toggleEdit } = this.props;
return (
<TodoListItem
todo={todo}
deleteTodo={deleteTodo}
toggleEdit={toggleEdit}
/>
);
}
renderEditView = (todo: TodoType) => {
const { updateTodo, cancelUpdateTodo } = this.props;
return (
<TodoEditInput
todo={todo}
updateTodo={updateTodo}
cancelUpdateTodo={cancelUpdateTodo}
/>
);
}
renderList = () => {
const { todos } = this.props;
const sortTodosByTime = todos.sort((a, b) => b.createdAt - a.createdAt);
return (
<ul className='todo__list'>
{sortTodosByTime.map(todo => (
<li
key={todo.id}
className='todo__list-item todo-item'
>
{todo.editing ? (
this.renderEditView(todo)
) : (
this.renderTodoView(todo)
)}
</li>
))}
</ul>
);
}
renderEmptyState = () => (
<p className='todo__list todo__list--empty'>No todos right now</p>
);
render() {
const { todos } = this.props;
const hasTodos = todos.length;
return hasTodos ? this.renderList() : this.renderEmptyState();
}
}

5
app/constants/actions.js Normal file
View File

@ -0,0 +1,5 @@
export const ADD_TODO = 'ADD_TODO';
export const DELETE_TODO = 'DELETE_TODO';
export const UPDATE_TODO = 'UPDATE_TODO';
export const TOGGLE_EDIT_TODO = 'TOGGLE_EDIT_TODO';
export const CANCEL_UPDATE_TODO = 'CANCEL_UPDATE_TODO';

28
app/containers/todo.js Normal file
View File

@ -0,0 +1,28 @@
// @flow
import { connect } from 'react-redux';
import TodoView from '../views/todo';
import { addTodo } from '../actions/add-todo';
import { deleteTodo } from '../actions/delete-todo';
import { toggleEdit } from '../actions/toggle-edit-todo';
import { updateTodo } from '../actions/update-todo';
import { cancelUpdateTodo } from '../actions/cancel-update-todo';
import type { AppState } from '../types/app-state';
import type { Dispatch } from '../types/redux';
const mapStateToProps = (state: AppState) => ({
todos: state.todos,
});
const mapDispatchToProps = (dispatch: Dispatch) => ({
addTodo: text => dispatch(addTodo(text)),
deleteTodo: id => dispatch(deleteTodo(id)),
toggleEdit: id => dispatch(toggleEdit(id)),
updateTodo: (id, text) => dispatch(updateTodo(id, text)),
cancelUpdateTodo: id => dispatch(cancelUpdateTodo(id)),
});
export default connect(
mapStateToProps,
mapDispatchToProps,
)(TodoView);

6
app/index.js Normal file
View File

@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
const el = document.getElementById('root');
ReactDOM.render(<App />, el);

8
app/reducers/index.js Normal file
View File

@ -0,0 +1,8 @@
import { combineReducers } from 'redux';
import todoReducer from './todo';
const rootReducer = combineReducers({
todos: todoReducer,
});
export default rootReducer;

53
app/reducers/todo.js Normal file
View File

@ -0,0 +1,53 @@
// @flow
import {
ADD_TODO,
DELETE_TODO,
UPDATE_TODO,
CANCEL_UPDATE_TODO,
TOGGLE_EDIT_TODO,
} from '../constants/actions';
import type { TodoType } from '../types/todo';
import type { Action } from '../types/redux';
const initialState = [];
export default (state: Array<TodoType> = initialState, action: Action) => {
switch (action.type) {
case ADD_TODO:
return [
...state,
action.payload,
];
case DELETE_TODO:
// $FlowFixMe
return state.filter((todo: Object) => todo.id !== action.payload.id);
case TOGGLE_EDIT_TODO: {
const { id } = action.payload;
const todos = [...state];
const index = todos.map(todo => todo.id).indexOf(id);
todos[index].editing = true;
return todos;
}
case UPDATE_TODO: {
const { id, text } = action.payload;
const todos = [...state];
const index = todos.map(todo => todo.id).indexOf(id);
todos[index].text = text;
todos[index].editing = false;
return todos;
}
case CANCEL_UPDATE_TODO: {
const { id } = action.payload;
const todos = [...state];
const index = todos.map(todo => todo.id).indexOf(id);
todos[index].editing = false;
return todos;
}
default:
return state;
}
};

20
app/router/index.js Normal file
View File

@ -0,0 +1,20 @@
import React, { Fragment } from 'react';
import { Route, Switch, Link } from 'react-router-dom';
import TodoContainer from '../containers/todo';
import AboutView from '../views/about';
import NotFoundView from '../views/not-found';
export default () => (
<Fragment>
<div className='header-menu'>
<Link to='/'>Todo</Link>
<Link to='/about'>About</Link>
</div>
<Switch>
<Route path='/' exact component={TodoContainer} />
<Route path='/about' component={AboutView} />
<Route component={NotFoundView} />
</Switch>
</Fragment>
);

24
app/store/configure.js Normal file
View File

@ -0,0 +1,24 @@
// @flow
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState: Object) {
const middleware = applyMiddleware(thunk);
let enhancer;
if (
process.env.NODE_ENV !== 'production'
|| process.env.NODE_ENV !== 'staging'
) {
enhancer = compose(
middleware,
window.devToolsExtension ? window.devToolsExtension() : f => f,
);
} else {
enhancer = compose(middleware);
}
return createStore(rootReducer, initialState, enhancer);
}

7
app/types/app-state.js Normal file
View File

@ -0,0 +1,7 @@
// @flow
import type { TodoType } from './todo';
export type AppState = {
todos: Array<TodoType>,
};

7
app/types/redux.js Normal file
View File

@ -0,0 +1,7 @@
// @flow
type State = {| |};
export type Action = { type: $Subtype<string>, payload: Object };
export type GetState = () => State;
export type Dispatch = (action: Action) => any;

8
app/types/todo.js Normal file
View File

@ -0,0 +1,8 @@
// @flow
export type TodoType = {
id: string,
text: string,
editing: boolean,
createdAt: number
};

1
app/utils/timestamp.js Normal file
View File

@ -0,0 +1 @@
export const getTimestamp = () => new Date().getTime();

9
app/views/about.js Normal file
View File

@ -0,0 +1,9 @@
import React from 'react';
export default () => (
<div className='about'>
<h1 className='about__header'>React Redux Starter</h1>
<p>This is a react-redux starter kit for Big Human Engineers.</p>
<p>It uses Redux to manage application state and Thunk to support asynchronous actions. Flow is used for static type checking which replaces the prop-types package.</p>
</div>
);

5
app/views/not-found.js Normal file
View File

@ -0,0 +1,5 @@
import React from 'react';
export default () => (
<div>Not Found | 404</div>
);

44
app/views/todo.js Normal file
View File

@ -0,0 +1,44 @@
// @flow
import React from 'react';
import TodoInput from '../components/todo/todo-input';
import TodoList from '../components/todo/todo-list';
import type { TodoType } from '../types/todo';
import checklist from '../assets/images/checklist.svg';
type Props = {
addTodo: Function,
deleteTodo: Function,
toggleEdit: Function,
todos: Array<TodoType>,
updateTodo: Function,
cancelUpdateTodo: Function,
};
export default (props: Props) => {
const {
addTodo,
todos,
deleteTodo,
toggleEdit,
updateTodo,
cancelUpdateTodo,
} = props;
return (
<div className='todo'>
<div className='todo__heading'>
<img src={checklist} alt='Testing File Loader' className='todo__image' />
<h1 className='todo__header'>Todo List App</h1>
</div>
<TodoInput addTodo={addTodo} />
<TodoList
todos={todos}
deleteTodo={deleteTodo}
toggleEdit={toggleEdit}
updateTodo={updateTodo}
cancelUpdateTodo={cancelUpdateTodo}
/>
</div>
);
};

View File

@ -0,0 +1,18 @@
const path = require('path');
const mainWebpack = require('./webpack-main.config');
const outputPath = path.resolve(__dirname, '../', 'dist');
module.exports = {
output: {
path: outputPath,
filename: 'bundle.js',
chunkFilename: '[name].chunk.js',
publicPath: '/',
},
devServer: {
historyApiFallback: true,
host: process.env.HOST || '0.0.0.0',
},
...mainWebpack,
};

View File

@ -0,0 +1,72 @@
const HtmlWebPackPlugin = require('html-webpack-plugin'); // eslint-disable-line
const UglifyJSPlugin = require('uglifyjs-webpack-plugin'); // eslint-disable-line
const autoprefixer = require('autoprefixer');
module.exports = {
entry: {
index: './app/index.js',
},
optimization: {
minimizer: [
new UglifyJSPlugin({ sourceMap: true }),
],
},
devtool: 'cheap-module-source-map',
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
},
},
{
test: /\.scss$/,
use: [{
loader: 'style-loader',
}, {
loader: 'css-loader',
}, {
loader: 'postcss-loader',
ident: 'postcss',
options: {
plugins: () => [autoprefixer({
browsers: ['> 1%', 'not ie 11'],
})],
},
}, {
loader: 'sass-loader',
}],
},
{
test: /\.(png|jpe?g|gif|svg)$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/',
},
},
],
},
{
test: /\.(woff(2)?|ttf|eot)(\?v=\d+\.\d+\.\d+)?$/,
use: [{
loader: 'file-loader',
options: {
name: '[name].[ext]',
outputPath: 'assets/fonts/',
},
}],
},
],
},
plugins: [
new HtmlWebPackPlugin({
template: './public/index.html',
filename: './index.html',
}),
],
};

View File

@ -0,0 +1,14 @@
const path = require('path');
const mainWebpack = require('./webpack-main.config');
const outputPath = path.resolve(__dirname, '../', 'dist');
module.exports = {
output: {
path: outputPath,
filename: 'bundle.js',
chunkFilename: '[name].chunk.js',
publicPath: '/',
},
...mainWebpack,
};

67
package.json Normal file
View File

@ -0,0 +1,67 @@
{
"name": "zcash-reference-wallet",
"version": "0.1.0",
"description": "Zcash Reference Wallet",
"main": "index.js",
"scripts": {
"start": "yarn dev",
"dev": "webpack-dev-server --config config/webpack-dev.config.js --mode development --open --hot",
"build": "rm -rf dist && webpack --config config/webpack-prod.config.js --mode production --env.NODE_ENV=production",
"lint:precommit": "eslint ./app/",
"flow:precommit": "glow",
"flow:coverage": "flow-coverage-report -t html -i 'app/**/*.js' -x 'dist/*.js' --threshold 70",
"flow:report": "yarn flow:coverage && cd ./flow-coverage && open index.html"
},
"author": {
"name": "André Neves",
"email": "andrerfneves@protonmail.com",
"url": "https://andrenev.es"
},
"private": true,
"devDependencies": {
"@babel/cli": "^7.0.0",
"@babel/core": "^7.0.0",
"@babel/plugin-proposal-class-properties": "^7.0.0",
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/preset-flow": "^7.0.0",
"@babel/preset-react": "^7.0.0",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.4",
"css-loader": "^1.0.1",
"eslint": "^5.8.0",
"eslint-config-airbnb": "^17.1.0",
"eslint-plugin-flowtype": "^3.2.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-jsx-a11y": "^6.0.3",
"eslint-plugin-react": "^7.7.0",
"file-loader": "^2.0.0",
"flow-bin": "^0.85.0",
"glow": "^1.2.2",
"html-webpack-plugin": "^3.1.0",
"node-sass": "^4.8.3",
"postcss-loader": "^3.0.0",
"pre-commit": "^1.2.2",
"redux-logger": "^3.0.6",
"sass-loader": "^7.1.0",
"style-loader": "^0.23.1",
"webpack": "^4.4.1",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-cli": "^3.1.2",
"webpack-dev-server": "^3.1.1"
},
"dependencies": {
"autoprefixer": "^9.3.1",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react-redux": "^5.0.7",
"react-router-dom": "^4.2.2",
"redux": "^4.0.1",
"redux-thunk": "^2.2.0",
"uuid": "^3.3.2"
},
"pre-commit": [
"lint:precommit",
"flow:precommit"
]
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

25
public/index.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<!-- Android Manifest -->
<link rel="manifest" href="manifest.json">
<link rel="shortcut icon" href="favicon.ico">
<!-- End Android Manifest -->
<title>React Redux Boilerplate</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<!-- React App Inject -->
<div id="root"></div>
<!-- End React App Inject -->
</body>
</html>

15
public/manifest.json Normal file
View File

@ -0,0 +1,15 @@
{
"short_name": "React",
"name": "React Redux Starter",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": "./index.html",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

7159
yarn.lock Normal file

File diff suppressed because it is too large Load Diff