From abe4cbf12408c844cd6a8b75b0bc4339c5ebd009 Mon Sep 17 00:00:00 2001 From: Santiago Alvarado <34078372+saperez17@users.noreply.github.com> Date: Sun, 29 Aug 2021 21:44:46 -0500 Subject: [PATCH 1/5] Completed excercises 6.3-6.8 Completed implementation of the anecdotes voting app using redux. Key concepts: redux library, react-redux library, useSelector and useDispatch hooks. Store, reducer, action, action creators. --- src/App.js | 36 ++++-------------- src/components/AnecdoteForm.js | 28 ++++++++++++++ src/components/AnecdoteList.js | 31 ++++++++++++++++ src/reducers/anecdoteReducer.js | 65 +++++++++++++++++++++++++-------- 4 files changed, 116 insertions(+), 44 deletions(-) create mode 100644 src/components/AnecdoteForm.js create mode 100644 src/components/AnecdoteList.js diff --git a/src/App.js b/src/App.js index eb8bb53..10f101a 100644 --- a/src/App.js +++ b/src/App.js @@ -1,35 +1,15 @@ -import React from 'react' -import { useSelector, useDispatch } from 'react-redux' +import React from "react"; +import AnecdoteForm from "./components/AnecdoteForm"; +import AnecdoteList from "./components/AnecdoteList"; const App = () => { - const anecdotes = useSelector(state => state) - const dispatch = useDispatch() - - const vote = (id) => { - console.log('vote', id) - } - return (

Anecdotes

- {anecdotes.map(anecdote => -
-
- {anecdote.content} -
-
- has {anecdote.votes} - -
-
- )} -

create new

-
-
- -
+ +
- ) -} + ); +}; -export default App \ No newline at end of file +export default App; diff --git a/src/components/AnecdoteForm.js b/src/components/AnecdoteForm.js new file mode 100644 index 0000000..eeab005 --- /dev/null +++ b/src/components/AnecdoteForm.js @@ -0,0 +1,28 @@ +import React from "react"; +import { useDispatch } from "react-redux"; +import { addAnecdote } from "../reducers/anecdoteReducer"; + +const AnecdoteForm = (props) => { + const dispatch = useDispatch(); + + const newAnecdote = (event) => { + event.preventDefault(); + const anecdoteContent = event.target.content.value; + dispatch(addAnecdote(anecdoteContent)); + event.target.content.value = ""; + }; + + return ( +
+

create new

+
+
+ +
+ +
+
+ ); +}; + +export default AnecdoteForm; diff --git a/src/components/AnecdoteList.js b/src/components/AnecdoteList.js new file mode 100644 index 0000000..89a119e --- /dev/null +++ b/src/components/AnecdoteList.js @@ -0,0 +1,31 @@ +import React from "react"; +import { useSelector, useDispatch } from "react-redux"; +import { voteAnecdote } from "../reducers/anecdoteReducer"; + +const AnecdoteList = (props) => { + const anecdotes = useSelector((state) => state); + const dispatch = useDispatch(); + + const sortedAnecdotes = anecdotes.sort((a, b) => b.votes - a.votes); + + const vote = (id) => { + console.log("vote", id); + dispatch(voteAnecdote(id)); + }; + + return ( +
+ {sortedAnecdotes.map((anecdote) => ( +
+
{anecdote.content}
+
+ has {anecdote.votes} + +
+
+ ))} +
+ ); +}; + +export default AnecdoteList; diff --git a/src/reducers/anecdoteReducer.js b/src/reducers/anecdoteReducer.js index 95f1662..4ab0e4c 100644 --- a/src/reducers/anecdoteReducer.js +++ b/src/reducers/anecdoteReducer.js @@ -1,29 +1,62 @@ const anecdotesAtStart = [ - 'If it hurts, do it more often', - 'Adding manpower to a late software project makes it later!', - 'The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.', - 'Any fool can write code that a computer can understand. Good programmers write code that humans can understand.', - 'Premature optimization is the root of all evil.', - 'Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.' -] + "If it hurts, do it more often", + "Adding manpower to a late software project makes it later!", + "The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.", + "Any fool can write code that a computer can understand. Good programmers write code that humans can understand.", + "Premature optimization is the root of all evil.", + "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." +]; -const getId = () => (100000 * Math.random()).toFixed(0) +const getId = () => (100000 * Math.random()).toFixed(0); const asObject = (anecdote) => { return { content: anecdote, id: getId(), votes: 0 - } -} + }; +}; -const initialState = anecdotesAtStart.map(asObject) +const initialState = anecdotesAtStart.map(asObject); const reducer = (state = initialState, action) => { - console.log('state now: ', state) - console.log('action', action) + // console.log("state now: ", state); + // console.log("action", action); + + switch (action.type) { + case "VOTE_ANECDOTE": + const anecdoteToBeVoted = state.filter( + (anecdote) => anecdote.id === action.data.id + )[0]; + anecdoteToBeVoted.votes += 1; + console.log(anecdoteToBeVoted); + return state.map((anecdote) => + anecdote.id !== action.data.id ? anecdote : anecdoteToBeVoted + ); + case "NEW_ANECDOTE": + return [...state, action.data]; + default: { + return state; + } + } +}; - return state -} +//Action creators +export const addAnecdote = (content) => { + const noteToAdd = asObject(content); + return { + type: "NEW_ANECDOTE", + data: { + ...noteToAdd + } + }; +}; + +export const voteAnecdote = (id) => { + return { + type: "VOTE_ANECDOTE", + data: { id } + }; +}; -export default reducer \ No newline at end of file +export default reducer; From d210678ea73055498290935868edd05287914707 Mon Sep 17 00:00:00 2001 From: saperez17 Date: Mon, 30 Aug 2021 01:00:18 -0500 Subject: [PATCH 2/5] use redux-store with complex state. created multiple reducers, action creators, redux devtools and combined reducers. exercises 6.9-6.12 --- jsconfig.json | 6 ++++++ src/App.js | 4 ++++ src/components/AnecdoteList.js | 7 ++++++- src/components/Filter.js | 22 ++++++++++++++++++++ src/components/Notification.js | 5 +++-- src/index.js | 5 +---- src/reducers/filterReducer.js | 15 ++++++++++++++ src/reducers/notificationReducer.js | 32 +++++++++++++++++++++++++++++ src/store.js | 14 +++++++++++++ 9 files changed, 103 insertions(+), 7 deletions(-) create mode 100644 jsconfig.json create mode 100644 src/components/Filter.js create mode 100644 src/reducers/filterReducer.js create mode 100644 src/reducers/notificationReducer.js create mode 100644 src/store.js diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..ea773e9 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "baseUrl": "src" + }, + "include": ["src"] + } \ No newline at end of file diff --git a/src/App.js b/src/App.js index 10f101a..638e5b1 100644 --- a/src/App.js +++ b/src/App.js @@ -1,11 +1,15 @@ import React from "react"; import AnecdoteForm from "./components/AnecdoteForm"; import AnecdoteList from "./components/AnecdoteList"; +import Filter from "components/Filter"; +import Notification from "components/Notification"; const App = () => { return (

Anecdotes

+ +
diff --git a/src/components/AnecdoteList.js b/src/components/AnecdoteList.js index 89a119e..9ecdd1f 100644 --- a/src/components/AnecdoteList.js +++ b/src/components/AnecdoteList.js @@ -3,7 +3,12 @@ import { useSelector, useDispatch } from "react-redux"; import { voteAnecdote } from "../reducers/anecdoteReducer"; const AnecdoteList = (props) => { - const anecdotes = useSelector((state) => state); + const anecdotes = useSelector((state) => { + if (state.filter !== ""){ + return state.anecdotes.filter(anecdote => anecdote.content.includes(state.filter.toLowerCase())) + } + return state.anecdotes + }); const dispatch = useDispatch(); const sortedAnecdotes = anecdotes.sort((a, b) => b.votes - a.votes); diff --git a/src/components/Filter.js b/src/components/Filter.js new file mode 100644 index 0000000..025c970 --- /dev/null +++ b/src/components/Filter.js @@ -0,0 +1,22 @@ +import React from 'react' +import { useSelector, useDispatch } from 'react-redux' + +const Filter = () => { + const dispatch = useDispatch() + const handleChange = (event) => { + // input-field value is in variable event.target.value + const filterValue = event.target.value + dispatch({type:'CHANGE', data:filterValue}) + } + const style = { + marginBottom: 10 + } + + return ( +
+ filter +
+ ) +} + +export default Filter \ No newline at end of file diff --git a/src/components/Notification.js b/src/components/Notification.js index b9c9c63..7f9ed82 100644 --- a/src/components/Notification.js +++ b/src/components/Notification.js @@ -1,7 +1,8 @@ - import React from 'react' +import { useSelector } from 'react-redux' const Notification = () => { + const notification = useSelector(state => state.notification) const style = { border: 'solid', padding: 10, @@ -9,7 +10,7 @@ const Notification = () => { } return (
- render here notification... + {notification}
) } diff --git a/src/index.js b/src/index.js index 05da98a..2149dfc 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,8 @@ import React from 'react' import ReactDOM from 'react-dom' -import { createStore } from 'redux' import { Provider } from 'react-redux' import App from './App' -import reducer from './reducers/anecdoteReducer' - -const store = createStore(reducer) +import store from 'store' ReactDOM.render( diff --git a/src/reducers/filterReducer.js b/src/reducers/filterReducer.js new file mode 100644 index 0000000..551295a --- /dev/null +++ b/src/reducers/filterReducer.js @@ -0,0 +1,15 @@ + +const initialState = "" + +const reducer = (state=initialState, action) => { + switch(action.type){ + case 'CHANGE':{ + return action.data + } + default:{ + return state + } + } +} + +export default reducer \ No newline at end of file diff --git a/src/reducers/notificationReducer.js b/src/reducers/notificationReducer.js new file mode 100644 index 0000000..557bdc9 --- /dev/null +++ b/src/reducers/notificationReducer.js @@ -0,0 +1,32 @@ +const notificationInitialState = "Welcome" + +const reducer = (state=notificationInitialState, action) =>{ + + switch(action.type){ + case 'SET':{ + return action.data + } + case 'REMOVE':{ + return "" + } + default:{ + return state + } + } +} + +//action creators +export const notificationSet = (message)=>{ + return { + type:"SET", + data: message + } +} + +export const notificationRemove = ()=>{ + return { + type:"REMOVE" + } +} + +export default reducer; diff --git a/src/store.js b/src/store.js new file mode 100644 index 0000000..e3fe2ea --- /dev/null +++ b/src/store.js @@ -0,0 +1,14 @@ +import { createStore, combineReducers} from 'redux' +import anecdoteReducer from "reducers/anecdoteReducer" +import notificationReducer from "reducers/notificationReducer" +import filterReducer from 'reducers/filterReducer' +import { composeWithDevTools } from 'redux-devtools-extension' + +const reducer = combineReducers({ + anecdotes: anecdoteReducer, + notification: notificationReducer, + filter: filterReducer +}) +const store = createStore(reducer, composeWithDevTools()) + +export default store; From 0396ad7de5abb3c1235bad782f673d321d45acb2 Mon Sep 17 00:00:00 2001 From: saperez17 Date: Mon, 6 Sep 2021 00:40:10 -0500 Subject: [PATCH 3/5] excercises 6.13 - 6.18. Asynchronous action creators with redux-thunk --- db.json | 74 +++ package-lock.json | 984 ++++++++++++++++++++++++++++ package.json | 9 +- src/App.js | 16 +- src/components/AnecdoteForm.js | 23 +- src/components/AnecdoteList.js | 8 +- src/reducers/anecdoteReducer.js | 60 +- src/reducers/notificationReducer.js | 23 +- src/services/anecdotes.js | 26 + src/store.js | 9 +- 10 files changed, 1200 insertions(+), 32 deletions(-) create mode 100644 db.json create mode 100644 src/services/anecdotes.js diff --git a/db.json b/db.json new file mode 100644 index 0000000..d190146 --- /dev/null +++ b/db.json @@ -0,0 +1,74 @@ +{ + "anecdotes": [ + { + "content": "If it hurts, do it more often", + "id": 1, + "votes": 5 + }, + { + "content": "Adding manpower to a late software project makes it later!", + "id": 2, + "votes": 3 + }, + { + "content": "The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.", + "id": 3, + "votes": 0 + }, + { + "content": "Any fool can write code that a computer can understand. Good programmers write code that humans can understand.", + "id": 4, + "votes": 0 + }, + { + "content": "Premature optimization is the root of all evil.", + "id": 5, + "votes": 0 + }, + { + "content": "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.", + "id": 6, + "votes": 0 + }, + { + "content": "today I studied js closures", + "votes": 0, + "id": 7 + }, + { + "content": "studying JS stuff is fun", + "votes": 0, + "id": 8 + }, + { + "content": "redux-thunk is a redux-middleware", + "votes": 0, + "id": 9 + }, + { + "content": "Web development is fun, but hard.", + "votes": 0, + "id": 10 + }, + { + "content": "I like to grow silently, and demonstrate when the time is right", + "votes": 0, + "id": 11 + }, + { + "content": "Good programers share their kwnoledge", + "votes": 0, + "id": 12 + }, + { + "content": "What is going on?", + "votes": 0, + "id": 13 + }, + { + "content": "Tomorrow is work day, let's go!", + "votes": 6, + "id": 14 + } + ] +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index f359494..2b7720c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1826,6 +1826,12 @@ } } }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "dev": true + }, "@sinonjs/commons": { "version": "1.8.2", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.2.tgz", @@ -1960,6 +1966,15 @@ "loader-utils": "^2.0.0" } }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "dev": true, + "requires": { + "defer-to-connect": "^1.0.1" + } + }, "@testing-library/dom": { "version": "7.29.4", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.29.4.tgz", @@ -2701,6 +2716,55 @@ "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=" }, + "ansi-align": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", + "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", + "dev": true, + "requires": { + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -2985,6 +3049,21 @@ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.1.1.tgz", "integrity": "sha512-5Kgy8Cz6LPC9DJcNb3yjAXTu3XihQgEdnIg50c//zOC/MyLP0Clg+Y8Sh9ZjjnvBrDZU4DgXS9C3T9r4/scGZQ==" }, + "axios": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.3.tgz", + "integrity": "sha512-JtoZ3Ndke/+Iwt5n+BgSli/3idTvpt5OjKyoCmz4LX5+lPiY5l7C1colYezhlxThjNa/NhngCUWZSZFypIFuaA==", + "requires": { + "follow-redirects": "^1.14.0" + }, + "dependencies": { + "follow-redirects": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.3.tgz", + "integrity": "sha512-3MkHxknWMUtb23apkgz/83fDoe+y+qr0TdgacGIA7bew+QLBo3vdgEN2xEsuXNivpFy4CyDhBBZnNZOtalmenw==" + } + } + }, "axobject-query": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz", @@ -3472,6 +3551,15 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + } + }, "batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", @@ -3586,6 +3674,90 @@ "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" }, + "boxen": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.0.1.tgz", + "integrity": "sha512-49VBlw+PrWEF51aCmy7QIteYPIFZxSpvqBdP/2itCPPlJ49kj9zg/XPRFrdkne2W+CfwXUls8exMvu1RysZpKA==", + "dev": true, + "requires": { + "ansi-align": "^3.0.0", + "camelcase": "^6.2.0", + "chalk": "^4.1.0", + "cli-boxes": "^2.2.1", + "string-width": "^4.2.0", + "type-fest": "^0.20.2", + "widest-line": "^3.1.0", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + } + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3794,6 +3966,44 @@ "unset-value": "^1.0.0" } }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "normalize-url": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", + "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", + "dev": true + } + } + }, "call-bind": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", @@ -3995,6 +4205,12 @@ "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" }, + "cli-boxes": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", + "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", + "dev": true + }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -4005,6 +4221,15 @@ "wrap-ansi": "^6.2.0" } }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", @@ -4183,6 +4408,52 @@ } } }, + "configstore": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", + "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", + "dev": true, + "requires": { + "dot-prop": "^5.2.0", + "graceful-fs": "^4.1.2", + "make-dir": "^3.0.0", + "unique-string": "^2.0.0", + "write-file-atomic": "^3.0.0", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "requires": { + "crypto-random-string": "^2.0.0" + } + } + } + }, "confusing-browser-globals": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.10.tgz", @@ -4193,6 +4464,12 @@ "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==" }, + "connect-pause": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/connect-pause/-/connect-pause-0.1.1.tgz", + "integrity": "sha1-smmyu4Ldsaw9tQmcD7WCq6mfs3o=", + "dev": true + }, "console-browserify": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", @@ -4298,6 +4575,16 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cosmiconfig": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz", @@ -4754,6 +5041,15 @@ "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, "dedent": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", @@ -4772,6 +5068,12 @@ "regexp.prototype.flags": "^1.2.0" } }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true + }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -4791,6 +5093,12 @@ "ip-regex": "^2.1.0" } }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", + "dev": true + }, "define-properties": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", @@ -5120,6 +5428,12 @@ "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==" }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, "duplexify": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", @@ -5308,6 +5622,16 @@ "stackframe": "^1.1.1" } }, + "errorhandler": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz", + "integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==", + "dev": true, + "requires": { + "accepts": "~1.3.7", + "escape-html": "~1.0.3" + } + }, "es-abstract": { "version": "1.18.0-next.2", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.2.tgz", @@ -5373,6 +5697,12 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" }, + "escape-goat": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", + "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", + "dev": true + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -6176,6 +6506,33 @@ } } }, + "express-urlrewrite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/express-urlrewrite/-/express-urlrewrite-1.4.0.tgz", + "integrity": "sha512-PI5h8JuzoweS26vFizwQl6UTF25CAHSggNv0J25Dn/IKZscJHWZzPrI5z2Y2jgOzIaw2qh8l6+/jUcig23Z2SA==", + "dev": true, + "requires": { + "debug": "*", + "path-to-regexp": "^1.0.3" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "path-to-regexp": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", + "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", + "dev": true, + "requires": { + "isarray": "0.0.1" + } + } + } + }, "ext": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", @@ -6823,6 +7180,23 @@ "is-glob": "^4.0.1" } }, + "global-dirs": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", + "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "dev": true, + "requires": { + "ini": "2.0.0" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + } + } + }, "global-modules": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", @@ -6859,6 +7233,25 @@ "slash": "^3.0.0" } }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "dev": true, + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, "graceful-fs": { "version": "4.2.5", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.5.tgz", @@ -6968,6 +7361,12 @@ } } }, + "has-yarn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", + "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", + "dev": true + }, "hash-base": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", @@ -7179,6 +7578,12 @@ } } }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, "http-deceiver": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", @@ -7417,6 +7822,12 @@ } } }, + "import-lazy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", + "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "dev": true + }, "import-local": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", @@ -7671,6 +8082,24 @@ "is-extglob": "^2.1.1" } }, + "is-installed-globally": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", + "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", + "dev": true, + "requires": { + "global-dirs": "^3.0.0", + "is-path-inside": "^3.0.2" + }, + "dependencies": { + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + } + } + }, "is-module": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", @@ -7681,6 +8110,12 @@ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==" }, + "is-npm": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", + "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", + "dev": true + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -7730,6 +8165,12 @@ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=" }, + "is-promise": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz", + "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==", + "dev": true + }, "is-regex": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz", @@ -7798,6 +8239,12 @@ "is-docker": "^2.0.0" } }, + "is-yarn-global": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", + "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", + "dev": true + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9359,6 +9806,12 @@ } } }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha1-o6vicYryQaKykE+EpiWXDzia4yo=", + "dev": true + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9416,6 +9869,12 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "dev": true + }, "json-parse-better-errors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", @@ -9426,6 +9885,15 @@ "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" }, + "json-parse-helpfulerror": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/json-parse-helpfulerror/-/json-parse-helpfulerror-1.0.3.tgz", + "integrity": "sha1-E/FM4C7tTpgSl7ZOueO5MuLdE9w=", + "dev": true, + "requires": { + "jju": "^1.1.0" + } + }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", @@ -9436,6 +9904,134 @@ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, + "json-server": { + "version": "0.16.3", + "resolved": "https://registry.npmjs.org/json-server/-/json-server-0.16.3.tgz", + "integrity": "sha512-tbsBONiefH7SR5EhSmK4EzwP3kCHuOduUq5hWAQjCwXTva4OBeKVcPrciHNWOK/+12ygtUnjuWcAxuHgqTuBLA==", + "dev": true, + "requires": { + "body-parser": "^1.19.0", + "chalk": "^4.1.0", + "compression": "^1.7.4", + "connect-pause": "^0.1.1", + "cors": "^2.8.5", + "errorhandler": "^1.5.1", + "express": "^4.17.1", + "express-urlrewrite": "^1.3.0", + "json-parse-helpfulerror": "^1.0.3", + "lodash": "^4.17.20", + "lodash-id": "^0.14.0", + "lowdb": "^1.0.0", + "method-override": "^3.0.0", + "morgan": "^1.10.0", + "nanoid": "^3.1.16", + "please-upgrade-node": "^3.2.0", + "pluralize": "^8.0.0", + "server-destroy": "^1.0.1", + "update-notifier": "^5.0.1", + "yargs": "^16.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true + } + } + }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", @@ -9488,6 +10084,15 @@ "object.assign": "^4.1.2" } }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "dev": true, + "requires": { + "json-buffer": "3.0.0" + } + }, "killable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz", @@ -9530,6 +10135,15 @@ "webpack-sources": "^1.1.0" } }, + "latest-version": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", + "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", + "dev": true, + "requires": { + "package-json": "^6.3.0" + } + }, "leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", @@ -9603,6 +10217,12 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" }, + "lodash-id": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/lodash-id/-/lodash-id-0.14.1.tgz", + "integrity": "sha512-ikQPBTiq/d5m6dfKQlFdIXFzvThPi2Be9/AHxktOnDSfSxE1j9ICbBT5Elk1ke7HSTgM38LHTpmJovo9/klnLg==", + "dev": true + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -9653,6 +10273,27 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lowdb": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-1.0.0.tgz", + "integrity": "sha512-2+x8esE/Wb9SQ1F9IHaYWfsC9FIecLOPrK4g17FGEayjUWH172H6nwicRovGvSE2CPZouc2MCIqCI7h9d+GftQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.3", + "is-promise": "^2.1.0", + "lodash": "4", + "pify": "^3.0.0", + "steno": "^0.4.1" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + } + } + }, "lower-case": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", @@ -9668,6 +10309,12 @@ } } }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", + "dev": true + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -9794,6 +10441,35 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" }, + "method-override": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", + "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", + "dev": true, + "requires": { + "debug": "3.1.0", + "methods": "~1.1.2", + "parseurl": "~1.3.2", + "vary": "~1.1.2" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -9852,6 +10528,12 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", @@ -10006,6 +10688,42 @@ "minimist": "^1.2.5" } }, + "morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "dev": true, + "requires": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + } + } + }, "move-concurrently": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", @@ -10517,6 +11235,12 @@ "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=" }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", + "dev": true + }, "p-each-series": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.2.0.tgz", @@ -10564,6 +11288,26 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "package-json": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", + "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", + "dev": true, + "requires": { + "got": "^9.6.0", + "registry-auth-token": "^4.0.0", + "registry-url": "^5.0.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -10854,6 +11598,21 @@ } } }, + "please-upgrade-node": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz", + "integrity": "sha512-gQR3WpIgNIKwBMVLkpMUeR3e1/E1y42bqDQZfql+kDeXd8COYfM8PQA4X6y7a8u9Ua9FHmsrrmirW2vHs45hWg==", + "dev": true, + "requires": { + "semver-compare": "^1.0.0" + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, "pnp-webpack-plugin": { "version": "1.6.4", "resolved": "https://registry.npmjs.org/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz", @@ -12068,6 +12827,15 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, + "pupa": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", + "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", + "dev": true, + "requires": { + "escape-goat": "^2.0.0" + } + }, "q": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", @@ -12150,6 +12918,26 @@ } } }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + } + } + }, "react": { "version": "17.0.1", "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", @@ -12505,6 +13293,17 @@ "symbol-observable": "^1.2.0" } }, + "redux-devtools-extension": { + "version": "2.13.9", + "resolved": "https://registry.npmjs.org/redux-devtools-extension/-/redux-devtools-extension-2.13.9.tgz", + "integrity": "sha512-cNJ8Q/EtjhQaZ71c8I9+BPySIBVEKssbPpskBfsXqb8HJ002A3KRVHfeRzwRo6mGPqsm7XuHTqNSNeS1Khig0A==", + "dev": true + }, + "redux-thunk": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-2.3.0.tgz", + "integrity": "sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==" + }, "regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -12572,6 +13371,24 @@ "unicode-match-property-value-ecmascript": "^1.2.0" } }, + "registry-auth-token": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", + "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, + "registry-url": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", + "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "dev": true, + "requires": { + "rc": "^1.2.8" + } + }, "regjsgen": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", @@ -12835,6 +13652,15 @@ } } }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "dev": true, + "requires": { + "lowercase-keys": "^1.0.0" + } + }, "ret": { "version": "0.1.15", "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", @@ -13216,6 +14042,29 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=", + "dev": true + }, + "semver-diff": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", + "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", + "dev": true, + "requires": { + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, "send": { "version": "0.17.1", "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", @@ -13327,6 +14176,12 @@ "send": "0.17.1" } }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha1-8Tv5KOQrnD55OD5hzDmYtdFObN0=", + "dev": true + }, "set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", @@ -13827,6 +14682,15 @@ "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, + "steno": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", + "integrity": "sha1-BxEFvfwobmYVwEA8J+nXtdy4Vcs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.3" + } + }, "stream-browserify": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", @@ -14451,6 +15315,12 @@ } } }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", + "dev": true + }, "to-regex": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", @@ -14725,6 +15595,88 @@ "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==" }, + "update-notifier": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", + "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", + "dev": true, + "requires": { + "boxen": "^5.0.0", + "chalk": "^4.1.0", + "configstore": "^5.0.1", + "has-yarn": "^2.1.0", + "import-lazy": "^2.1.0", + "is-ci": "^2.0.0", + "is-installed-globally": "^0.4.0", + "is-npm": "^5.0.0", + "is-yarn-global": "^0.3.0", + "latest-version": "^5.1.0", + "pupa": "^2.1.1", + "semver": "^7.3.4", + "semver-diff": "^3.1.1", + "xdg-basedir": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -14785,6 +15737,23 @@ "requires-port": "^1.0.0" } }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "dev": true, + "requires": { + "prepend-http": "^2.0.0" + }, + "dependencies": { + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "dev": true + } + } + }, "use": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", @@ -16073,6 +17042,15 @@ "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" }, + "widest-line": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", + "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", + "dev": true, + "requires": { + "string-width": "^4.0.0" + } + }, "word-wrap": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", @@ -16345,6 +17323,12 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.3.tgz", "integrity": "sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA==" }, + "xdg-basedir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", + "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", + "dev": true + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", diff --git a/package.json b/package.json index 5fb50a6..4c83232 100644 --- a/package.json +++ b/package.json @@ -6,18 +6,21 @@ "@testing-library/jest-dom": "^5.11.9", "@testing-library/react": "^11.2.5", "@testing-library/user-event": "^12.6.3", + "axios": "^0.21.3", "react": "^17.0.1", "react-dom": "^17.0.1", "react-redux": "^7.2.2", "react-scripts": "4.0.2", "redux": "^4.0.5", + "redux-thunk": "^2.3.0", "web-vitals": "^1.1.0" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", - "eject": "react-scripts eject" + "eject": "react-scripts eject", + "server": "json-server -p3001 --watch db.json" }, "eslintConfig": { "extends": [ @@ -36,5 +39,9 @@ "last 1 firefox version", "last 1 safari version" ] + }, + "devDependencies": { + "json-server": "^0.16.3", + "redux-devtools-extension": "^2.13.9" } } diff --git a/src/App.js b/src/App.js index 638e5b1..319e3f3 100644 --- a/src/App.js +++ b/src/App.js @@ -1,10 +1,24 @@ -import React from "react"; +import React, { useEffect } from "react"; import AnecdoteForm from "./components/AnecdoteForm"; import AnecdoteList from "./components/AnecdoteList"; import Filter from "components/Filter"; import Notification from "components/Notification"; +import { initAnecdotes } from 'reducers/anecdoteReducer' +import { useDispatch } from "react-redux"; +import anecdoteService from 'services/anecdotes'; const App = () => { + const dispatch = useDispatch() + //Without asynchronous action creators we have to communicate with the server from within our component + // useEffect(()=>{ + // anecdoteService.getAll() + // .then(anecdotes => { + // dispatch(initAnecdotes(anecdotes)) + // }) + // },[]) + + //With async action creators we can simply dispatch an action + dispatch(initAnecdotes()) return (

Anecdotes

diff --git a/src/components/AnecdoteForm.js b/src/components/AnecdoteForm.js index eeab005..c0c4253 100644 --- a/src/components/AnecdoteForm.js +++ b/src/components/AnecdoteForm.js @@ -1,6 +1,8 @@ import React from "react"; import { useDispatch } from "react-redux"; import { addAnecdote } from "../reducers/anecdoteReducer"; +import anecdoteService from 'services/anecdotes'; +import { notificationSet } from 'reducers/notificationReducer'; const AnecdoteForm = (props) => { const dispatch = useDispatch(); @@ -8,8 +10,25 @@ const AnecdoteForm = (props) => { const newAnecdote = (event) => { event.preventDefault(); const anecdoteContent = event.target.content.value; - dispatch(addAnecdote(anecdoteContent)); - event.target.content.value = ""; + + //Without async action creators + // anecdoteService.createNew(anecdoteContent) + // .then(response => { + // console.log('response: ', response) + // dispatch(addAnecdote(response)) + // event.target.content.value = ""; + // }) + + //With async action creators + dispatch(addAnecdote(anecdoteContent)) + .then(()=>{ + // dispatch(notificationSet(`New anecdote ${anecdoteContent}`)) + // setTimeout(()=>{ + // dispatch(notificationRemove()) + // }, 5000) + dispatch(notificationSet(anecdoteContent, 5000)) + event.target.content.value = "" + }) }; return ( diff --git a/src/components/AnecdoteList.js b/src/components/AnecdoteList.js index 9ecdd1f..256d179 100644 --- a/src/components/AnecdoteList.js +++ b/src/components/AnecdoteList.js @@ -13,9 +13,9 @@ const AnecdoteList = (props) => { const sortedAnecdotes = anecdotes.sort((a, b) => b.votes - a.votes); - const vote = (id) => { - console.log("vote", id); - dispatch(voteAnecdote(id)); + const vote = (id, votes) => { + // console.log("vote", id); + dispatch(voteAnecdote(id, votes+1)); }; return ( @@ -25,7 +25,7 @@ const AnecdoteList = (props) => {
{anecdote.content}
has {anecdote.votes} - +
))} diff --git a/src/reducers/anecdoteReducer.js b/src/reducers/anecdoteReducer.js index 4ab0e4c..f816262 100644 --- a/src/reducers/anecdoteReducer.js +++ b/src/reducers/anecdoteReducer.js @@ -1,3 +1,5 @@ +import anecdoteService from 'services/anecdotes'; + const anecdotesAtStart = [ "If it hurts, do it more often", "Adding manpower to a late software project makes it later!", @@ -19,7 +21,7 @@ const asObject = (anecdote) => { const initialState = anecdotesAtStart.map(asObject); -const reducer = (state = initialState, action) => { +const reducer = (state=[], action) => { // console.log("state now: ", state); // console.log("action", action); @@ -29,12 +31,14 @@ const reducer = (state = initialState, action) => { (anecdote) => anecdote.id === action.data.id )[0]; anecdoteToBeVoted.votes += 1; - console.log(anecdoteToBeVoted); + // console.log(anecdoteToBeVoted); return state.map((anecdote) => anecdote.id !== action.data.id ? anecdote : anecdoteToBeVoted ); case "NEW_ANECDOTE": return [...state, action.data]; + case "INIT": + return [...action.data] default: { return state; } @@ -42,21 +46,47 @@ const reducer = (state = initialState, action) => { }; //Action creators -export const addAnecdote = (content) => { - const noteToAdd = asObject(content); - return { - type: "NEW_ANECDOTE", - data: { - ...noteToAdd - } - }; +export const addAnecdote = (anecdote) => { + // const noteToAdd = asObject(content); + // return { + // type: "NEW_ANECDOTE", + // data: anecdote + // }; + return async (dispatch)=>{ + const response = await anecdoteService.createNew(anecdote); + dispatch({ + type:'NEW_ANECDOTE', + data: response + }) + } + }; -export const voteAnecdote = (id) => { - return { - type: "VOTE_ANECDOTE", - data: { id } - }; +export const voteAnecdote = (id, votes) => { + // return { + // type: "VOTE_ANECDOTE", + // data: { id } + // }; + return async (dispatch)=>{ + const response = await anecdoteService.voteAnecdote(id, votes); + dispatch({ + type: "VOTE_ANECDOTE", + data: { id:response.id } + }) + } }; +export const initAnecdotes = () => { + // return { + // type: "INIT", + // data: anecdotes + // }; + return async (dispatch) => { + const anecdotes = await anecdoteService.getAll(); + dispatch({ + type:"INIT", + data: anecdotes + }) + } +}; export default reducer; diff --git a/src/reducers/notificationReducer.js b/src/reducers/notificationReducer.js index 557bdc9..a7456ff 100644 --- a/src/reducers/notificationReducer.js +++ b/src/reducers/notificationReducer.js @@ -6,7 +6,7 @@ const reducer = (state=notificationInitialState, action) =>{ case 'SET':{ return action.data } - case 'REMOVE':{ + case 'CLEAR':{ return "" } default:{ @@ -16,16 +16,25 @@ const reducer = (state=notificationInitialState, action) =>{ } //action creators -export const notificationSet = (message)=>{ - return { - type:"SET", - data: message +export const notificationSet = (message, timeout)=>{ + // return { + // type:"SET", + // data: message + // } + return async (dispatch)=>{ + dispatch({ + type: "SET", + data: message + }) + setTimeout(()=>{ + dispatch({ type: "CLEAR" }) + }, timeout) } } -export const notificationRemove = ()=>{ +export const clearNotification = ()=>{ return { - type:"REMOVE" + type:"CLEAR" } } diff --git a/src/services/anecdotes.js b/src/services/anecdotes.js new file mode 100644 index 0000000..be2aea9 --- /dev/null +++ b/src/services/anecdotes.js @@ -0,0 +1,26 @@ +import axios from "axios"; + +const baseUrl = "http://localhost:3001/anecdotes"; + +const getAll = async() => { + const response = await axios.get(baseUrl); + return response.data +} + +const createNew = async(content) => { + const anecdoteObj = { content, votes:0 } + const response = await axios.post(baseUrl, anecdoteObj) + return response.data +} + +const voteAnecdote = async (id, votes) => { + const requestObject = { votes } + const response = await axios.patch(`${baseUrl}/${id}`, requestObject) + return response.data +} + +export default { + getAll, + createNew, + voteAnecdote +} \ No newline at end of file diff --git a/src/store.js b/src/store.js index e3fe2ea..004464d 100644 --- a/src/store.js +++ b/src/store.js @@ -1,4 +1,5 @@ -import { createStore, combineReducers} from 'redux' +import { createStore, combineReducers, applyMiddleware } from 'redux' +import thunk from 'redux-thunk' import anecdoteReducer from "reducers/anecdoteReducer" import notificationReducer from "reducers/notificationReducer" import filterReducer from 'reducers/filterReducer' @@ -9,6 +10,10 @@ const reducer = combineReducers({ notification: notificationReducer, filter: filterReducer }) -const store = createStore(reducer, composeWithDevTools()) +const store = createStore( + reducer, + composeWithDevTools( + applyMiddleware(thunk) + )) export default store; From 2eb277e54de86df3a678d3e8a47acbcb9a77b853 Mon Sep 17 00:00:00 2001 From: saperez17 Date: Mon, 6 Sep 2021 00:43:11 -0500 Subject: [PATCH 4/5] communicating with server in a redux app excercises completed --- src/App.js | 10 ---------- src/components/AnecdoteForm.js | 15 --------------- src/components/AnecdoteList.js | 1 - src/components/Filter.js | 2 +- src/reducers/anecdoteReducer.js | 29 +---------------------------- src/reducers/notificationReducer.js | 4 ---- 6 files changed, 2 insertions(+), 59 deletions(-) diff --git a/src/App.js b/src/App.js index 319e3f3..18f1a9c 100644 --- a/src/App.js +++ b/src/App.js @@ -5,19 +5,9 @@ import Filter from "components/Filter"; import Notification from "components/Notification"; import { initAnecdotes } from 'reducers/anecdoteReducer' import { useDispatch } from "react-redux"; -import anecdoteService from 'services/anecdotes'; const App = () => { const dispatch = useDispatch() - //Without asynchronous action creators we have to communicate with the server from within our component - // useEffect(()=>{ - // anecdoteService.getAll() - // .then(anecdotes => { - // dispatch(initAnecdotes(anecdotes)) - // }) - // },[]) - - //With async action creators we can simply dispatch an action dispatch(initAnecdotes()) return (
diff --git a/src/components/AnecdoteForm.js b/src/components/AnecdoteForm.js index c0c4253..03d1221 100644 --- a/src/components/AnecdoteForm.js +++ b/src/components/AnecdoteForm.js @@ -1,7 +1,6 @@ import React from "react"; import { useDispatch } from "react-redux"; import { addAnecdote } from "../reducers/anecdoteReducer"; -import anecdoteService from 'services/anecdotes'; import { notificationSet } from 'reducers/notificationReducer'; const AnecdoteForm = (props) => { @@ -10,22 +9,8 @@ const AnecdoteForm = (props) => { const newAnecdote = (event) => { event.preventDefault(); const anecdoteContent = event.target.content.value; - - //Without async action creators - // anecdoteService.createNew(anecdoteContent) - // .then(response => { - // console.log('response: ', response) - // dispatch(addAnecdote(response)) - // event.target.content.value = ""; - // }) - - //With async action creators dispatch(addAnecdote(anecdoteContent)) .then(()=>{ - // dispatch(notificationSet(`New anecdote ${anecdoteContent}`)) - // setTimeout(()=>{ - // dispatch(notificationRemove()) - // }, 5000) dispatch(notificationSet(anecdoteContent, 5000)) event.target.content.value = "" }) diff --git a/src/components/AnecdoteList.js b/src/components/AnecdoteList.js index 256d179..27d88aa 100644 --- a/src/components/AnecdoteList.js +++ b/src/components/AnecdoteList.js @@ -14,7 +14,6 @@ const AnecdoteList = (props) => { const sortedAnecdotes = anecdotes.sort((a, b) => b.votes - a.votes); const vote = (id, votes) => { - // console.log("vote", id); dispatch(voteAnecdote(id, votes+1)); }; diff --git a/src/components/Filter.js b/src/components/Filter.js index 025c970..67344c1 100644 --- a/src/components/Filter.js +++ b/src/components/Filter.js @@ -1,5 +1,5 @@ import React from 'react' -import { useSelector, useDispatch } from 'react-redux' +import { useDispatch } from 'react-redux' const Filter = () => { const dispatch = useDispatch() diff --git a/src/reducers/anecdoteReducer.js b/src/reducers/anecdoteReducer.js index f816262..b6e342a 100644 --- a/src/reducers/anecdoteReducer.js +++ b/src/reducers/anecdoteReducer.js @@ -1,14 +1,5 @@ import anecdoteService from 'services/anecdotes'; -const anecdotesAtStart = [ - "If it hurts, do it more often", - "Adding manpower to a late software project makes it later!", - "The first 90 percent of the code accounts for the first 90 percent of the development time...The remaining 10 percent of the code accounts for the other 90 percent of the development time.", - "Any fool can write code that a computer can understand. Good programmers write code that humans can understand.", - "Premature optimization is the root of all evil.", - "Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." -]; - const getId = () => (100000 * Math.random()).toFixed(0); const asObject = (anecdote) => { @@ -19,19 +10,13 @@ const asObject = (anecdote) => { }; }; -const initialState = anecdotesAtStart.map(asObject); - const reducer = (state=[], action) => { - // console.log("state now: ", state); - // console.log("action", action); - switch (action.type) { case "VOTE_ANECDOTE": const anecdoteToBeVoted = state.filter( (anecdote) => anecdote.id === action.data.id )[0]; anecdoteToBeVoted.votes += 1; - // console.log(anecdoteToBeVoted); return state.map((anecdote) => anecdote.id !== action.data.id ? anecdote : anecdoteToBeVoted ); @@ -47,11 +32,6 @@ const reducer = (state=[], action) => { //Action creators export const addAnecdote = (anecdote) => { - // const noteToAdd = asObject(content); - // return { - // type: "NEW_ANECDOTE", - // data: anecdote - // }; return async (dispatch)=>{ const response = await anecdoteService.createNew(anecdote); dispatch({ @@ -63,10 +43,7 @@ export const addAnecdote = (anecdote) => { }; export const voteAnecdote = (id, votes) => { - // return { - // type: "VOTE_ANECDOTE", - // data: { id } - // }; + return async (dispatch)=>{ const response = await anecdoteService.voteAnecdote(id, votes); dispatch({ @@ -77,10 +54,6 @@ export const voteAnecdote = (id, votes) => { }; export const initAnecdotes = () => { - // return { - // type: "INIT", - // data: anecdotes - // }; return async (dispatch) => { const anecdotes = await anecdoteService.getAll(); dispatch({ diff --git a/src/reducers/notificationReducer.js b/src/reducers/notificationReducer.js index a7456ff..d36c6d9 100644 --- a/src/reducers/notificationReducer.js +++ b/src/reducers/notificationReducer.js @@ -17,10 +17,6 @@ const reducer = (state=notificationInitialState, action) =>{ //action creators export const notificationSet = (message, timeout)=>{ - // return { - // type:"SET", - // data: message - // } return async (dispatch)=>{ dispatch({ type: "SET", From 58588b7c9565766be804e55b67c5f3c189e79149 Mon Sep 17 00:00:00 2001 From: saperez17 Date: Mon, 13 Sep 2021 01:29:34 -0500 Subject: [PATCH 5/5] Connect part6 6.19-6.21 exercises completed --- db.json | 16 +++++++++++++--- src/components/AnecdoteForm.js | 14 +++++++++++--- src/components/AnecdoteList.js | 19 +++++++++++++++---- src/components/Filter.js | 22 ++++++++++++++++------ src/components/Notification.js | 19 ++++++++++++++----- src/reducers/notificationReducer.js | 20 ++++++++++++++------ 6 files changed, 83 insertions(+), 27 deletions(-) diff --git a/db.json b/db.json index d190146..5a59b2d 100644 --- a/db.json +++ b/db.json @@ -3,7 +3,7 @@ { "content": "If it hurts, do it more often", "id": 1, - "votes": 5 + "votes": 50 }, { "content": "Adding manpower to a late software project makes it later!", @@ -52,7 +52,7 @@ }, { "content": "I like to grow silently, and demonstrate when the time is right", - "votes": 0, + "votes": 1, "id": 11 }, { @@ -67,8 +67,18 @@ }, { "content": "Tomorrow is work day, let's go!", - "votes": 6, + "votes": 14, "id": 14 + }, + { + "content": "Building a start-up from the groun-up is hard", + "votes": 0, + "id": 15 + }, + { + "content": "becoming a proficient Frontend engineer is my goal", + "votes": 1, + "id": 16 } ] } \ No newline at end of file diff --git a/src/components/AnecdoteForm.js b/src/components/AnecdoteForm.js index 03d1221..5d42b61 100644 --- a/src/components/AnecdoteForm.js +++ b/src/components/AnecdoteForm.js @@ -1,5 +1,5 @@ import React from "react"; -import { useDispatch } from "react-redux"; +import { useDispatch, connect } from "react-redux"; import { addAnecdote } from "../reducers/anecdoteReducer"; import { notificationSet } from 'reducers/notificationReducer'; @@ -11,7 +11,7 @@ const AnecdoteForm = (props) => { const anecdoteContent = event.target.content.value; dispatch(addAnecdote(anecdoteContent)) .then(()=>{ - dispatch(notificationSet(anecdoteContent, 5000)) + props.notificationSet(anecdoteContent, 5000) event.target.content.value = "" }) }; @@ -29,4 +29,12 @@ const AnecdoteForm = (props) => { ); }; -export default AnecdoteForm; +const mapDispatchToProps = { + notificationSet +} + +const ConnectedAnecdoteForm = connect( + null, + mapDispatchToProps +)(AnecdoteForm) +export default ConnectedAnecdoteForm; diff --git a/src/components/AnecdoteList.js b/src/components/AnecdoteList.js index 27d88aa..2ad8488 100644 --- a/src/components/AnecdoteList.js +++ b/src/components/AnecdoteList.js @@ -1,6 +1,7 @@ import React from "react"; -import { useSelector, useDispatch } from "react-redux"; +import { useSelector, connect } from "react-redux"; import { voteAnecdote } from "../reducers/anecdoteReducer"; +import { notificationSet } from 'reducers/notificationReducer'; const AnecdoteList = (props) => { const anecdotes = useSelector((state) => { @@ -9,12 +10,13 @@ const AnecdoteList = (props) => { } return state.anecdotes }); - const dispatch = useDispatch(); const sortedAnecdotes = anecdotes.sort((a, b) => b.votes - a.votes); const vote = (id, votes) => { - dispatch(voteAnecdote(id, votes+1)); + let votedAnecdote = anecdotes.find(anecdote => anecdote.id === id); + props.voteAnecdote(id, votes+1); + props.notificationSet(`+1 vote for ${votedAnecdote.content}`, 5000); }; return ( @@ -32,4 +34,13 @@ const AnecdoteList = (props) => { ); }; -export default AnecdoteList; +const mapDispatchToProps = { + notificationSet, + voteAnecdote +} + +const ConnectedAnecdoteList = connect( + null, + mapDispatchToProps +)(AnecdoteList) +export default ConnectedAnecdoteList; diff --git a/src/components/Filter.js b/src/components/Filter.js index 67344c1..937abda 100644 --- a/src/components/Filter.js +++ b/src/components/Filter.js @@ -1,12 +1,10 @@ import React from 'react' -import { useDispatch } from 'react-redux' +import { connect } from 'react-redux' -const Filter = () => { - const dispatch = useDispatch() +const Filter = (props) => { const handleChange = (event) => { - // input-field value is in variable event.target.value const filterValue = event.target.value - dispatch({type:'CHANGE', data:filterValue}) + props.changeFilter(filterValue); } const style = { marginBottom: 10 @@ -19,4 +17,16 @@ const Filter = () => { ) } -export default Filter \ No newline at end of file +const mapDispatchToProps = dispatch => { + return { + changeFilter: value => { + dispatch( {type:'CHANGE', data:value}) + } + } +} + +const ConnectedFilter = connect( + null, + mapDispatchToProps +)(Filter) +export default ConnectedFilter \ No newline at end of file diff --git a/src/components/Notification.js b/src/components/Notification.js index 7f9ed82..e2b1d9f 100644 --- a/src/components/Notification.js +++ b/src/components/Notification.js @@ -1,8 +1,7 @@ import React from 'react' -import { useSelector } from 'react-redux' +import { connect } from 'react-redux' -const Notification = () => { - const notification = useSelector(state => state.notification) +const Notification = (props) => { const style = { border: 'solid', padding: 10, @@ -10,9 +9,19 @@ const Notification = () => { } return (
- {notification} + {props.notification.message}
) } -export default Notification \ No newline at end of file +const mapStateToProps = state => { + return { + notification: state.notification + } +} + +const ConnectedNotification = connect( + mapStateToProps, + null +)(Notification) +export default ConnectedNotification \ No newline at end of file diff --git a/src/reducers/notificationReducer.js b/src/reducers/notificationReducer.js index d36c6d9..761098f 100644 --- a/src/reducers/notificationReducer.js +++ b/src/reducers/notificationReducer.js @@ -1,5 +1,8 @@ -const notificationInitialState = "Welcome" +const notificationInitialState = { + message: "Welcome", +} +let timeoutID; const reducer = (state=notificationInitialState, action) =>{ switch(action.type){ @@ -7,7 +10,9 @@ const reducer = (state=notificationInitialState, action) =>{ return action.data } case 'CLEAR':{ - return "" + return { + message: '', + } } default:{ return state @@ -18,13 +23,16 @@ const reducer = (state=notificationInitialState, action) =>{ //action creators export const notificationSet = (message, timeout)=>{ return async (dispatch)=>{ + if (timeoutID !== undefined){ + clearTimeout(timeoutID) + } + timeoutID = setTimeout(()=>{ + dispatch({ type: "CLEAR" }) + }, timeout) dispatch({ type: "SET", - data: message + data: {message} }) - setTimeout(()=>{ - dispatch({ type: "CLEAR" }) - }, timeout) } }