base: setup react redux app
This commit is contained in:
commit
68d781002a
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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>"
|
||||
}]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
[ignore]
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
|
||||
[lints]
|
||||
|
||||
[options]
|
||||
|
||||
[strict]
|
|
@ -0,0 +1,5 @@
|
|||
node_modules
|
||||
.vscode
|
||||
dist
|
||||
webpack-stats/*
|
||||
.DS_Store
|
|
@ -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.
|
|
@ -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
|
|
@ -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"
|
||||
|
|
@ -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
|
|
@ -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(),
|
||||
},
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import { CANCEL_UPDATE_TODO } from '../constants/actions';
|
||||
|
||||
export const cancelUpdateTodo = (id: string) => ({
|
||||
type: CANCEL_UPDATE_TODO,
|
||||
payload: { id },
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import { DELETE_TODO } from '../constants/actions';
|
||||
|
||||
export const deleteTodo = (id: string) => ({
|
||||
type: DELETE_TODO,
|
||||
payload: { id },
|
||||
});
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import { TOGGLE_EDIT_TODO } from '../constants/actions';
|
||||
|
||||
export const toggleEdit = (id: string) => ({
|
||||
type: TOGGLE_EDIT_TODO,
|
||||
payload: { id },
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
// @flow
|
||||
|
||||
import { UPDATE_TODO } from '../constants/actions';
|
||||
|
||||
export const updateTodo = (id: string, text: string) => ({
|
||||
type: UPDATE_TODO,
|
||||
payload: {
|
||||
text,
|
||||
id,
|
||||
},
|
||||
});
|
|
@ -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>
|
||||
);
|
|
@ -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 |
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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';
|
|
@ -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);
|
|
@ -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);
|
|
@ -0,0 +1,8 @@
|
|||
import { combineReducers } from 'redux';
|
||||
import todoReducer from './todo';
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
todos: todoReducer,
|
||||
});
|
||||
|
||||
export default rootReducer;
|
|
@ -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;
|
||||
}
|
||||
};
|
|
@ -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>
|
||||
);
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import type { TodoType } from './todo';
|
||||
|
||||
export type AppState = {
|
||||
todos: Array<TodoType>,
|
||||
};
|
|
@ -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;
|
|
@ -0,0 +1,8 @@
|
|||
// @flow
|
||||
|
||||
export type TodoType = {
|
||||
id: string,
|
||||
text: string,
|
||||
editing: boolean,
|
||||
createdAt: number
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
export const getTimestamp = () => new Date().getTime();
|
|
@ -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>
|
||||
);
|
|
@ -0,0 +1,5 @@
|
|||
import React from 'react';
|
||||
|
||||
export default () => (
|
||||
<div>Not Found | 404</div>
|
||||
);
|
|
@ -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>
|
||||
);
|
||||
};
|
|
@ -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,
|
||||
};
|
|
@ -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',
|
||||
}),
|
||||
],
|
||||
};
|
|
@ -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,
|
||||
};
|
|
@ -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"
|
||||
]
|
||||
}
|
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
|
@ -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>
|
|
@ -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"
|
||||
}
|
Loading…
Reference in New Issue