diff --git a/package.json b/package.json
index 6884391..01b38b9 100644
--- a/package.json
+++ b/package.json
@@ -11,7 +11,11 @@
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-markdown": "^4.3.1",
+ "react-redux": "^7.1.3",
"react-scripts": "3.3.0",
+ "redux": "^4.0.5",
+ "redux-logger": "^3.0.6",
+ "redux-thunk": "^2.3.0",
"weedux": "^3.5.3-beta"
},
"scripts": {
diff --git a/src/App.js b/src/App.js
index aa2fe2c..573ebbe 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,6 +1,7 @@
import React, { PureComponent } from "react";
-import { connect, bindActionCreators } from "weedux";
-import { store, actions } from "./state/index.js";
+import { connect } from "react-redux";
+import { bindActionCreators } from "redux";
+import { actions } from "./state/index.js";
import debounce from "lodash.debounce";
import NoteList from "./components/NoteList/NoteList";
@@ -8,10 +9,9 @@ import NoteContent from "./components/NoteContent/NoteContent";
import Omnibar from "./components/Omnibar/Omnibar";
import "./App.css";
-import { getNotes } from "./state/api.js";
class App extends PureComponent {
- defaultProps = { notes: [] };
+ static defaultProps = { selectedNote: null, notes: [], settings: {} };
state = { filter: "" };
@@ -23,9 +23,8 @@ class App extends PureComponent {
handleSearch = text => this.setState({ filter: text.toLocaleLowerCase() });
handleCreate = title => {
- const { createNote } = this.props;
- createNote({ title, content: "", tags: [] });
- getNotes();
+ const { upsertNote } = this.props;
+ upsertNote({ title, content: "", tags: [] });
};
handleSelectNote = title => {
@@ -34,13 +33,13 @@ class App extends PureComponent {
};
handleSaveNoteContent = content => {
- const { updateNote, selectedNote } = this.props;
- updateNote({ ...selectedNote, content });
+ const { upsertNote, selectedNote } = this.props;
+ upsertNote({ ...selectedNote, content });
};
- handleTagUpdate = ({ title, newTags }) => {
- const { updateTags } = this.props;
- updateTags({ title, tags: newTags });
+ handleTagUpdate = ({ title, content, newTags }) => {
+ const { upsertNote } = this.props;
+ upsertNote({ title, content, tags: newTags });
};
applyFilter = (notes = [], filter = "") =>
@@ -52,9 +51,15 @@ class App extends PureComponent {
);
render() {
- const { notes, selectedNote } = this.props;
+ const { notes, selectedNote, settings } = this.props;
+ const { autoSaveDelay } = settings;
+ // const handleSave = debounce(
+ // this.handleSaveNoteContent,
+ // autoSaveDelay || 1000
+ // );
+ const handleSave = this.handleSaveNoteContent;
const { filter } = this.state;
- const handleSave = debounce(this.handleSaveNoteContent, 250);
+ console.log("note", selectedNote);
return (
@@ -72,15 +77,18 @@ class App extends PureComponent {
onTagUpdate={this.handleTagUpdate}
selectedNote={selectedNote}
/>
-
+
);
}
}
-const ms2p = s => s;
+const ms2p = s => {
+ const { notes, settings } = s;
+ return { ...notes, settings: { ...settings.settings } };
+};
const md2p = dispatch => bindActionCreators({ ...actions }, dispatch);
-export default connect(ms2p, md2p, store)(App);
+export default connect(ms2p, md2p)(App);
diff --git a/src/components/EditableNoteArea/EditableNoteArea.css b/src/components/EditableNoteArea/EditableNoteArea.css
index 6b3b7be..2ff3a2c 100644
--- a/src/components/EditableNoteArea/EditableNoteArea.css
+++ b/src/components/EditableNoteArea/EditableNoteArea.css
@@ -18,6 +18,7 @@
border: none;
background-color: black;
color: white;
+ resize: none;
}
.EditableNoteArea .markdownPreview {
diff --git a/src/components/EditableNoteArea/EditableNoteArea.js b/src/components/EditableNoteArea/EditableNoteArea.js
index 88b59db..8cbdd60 100644
--- a/src/components/EditableNoteArea/EditableNoteArea.js
+++ b/src/components/EditableNoteArea/EditableNoteArea.js
@@ -1,21 +1,26 @@
-import React from "react";
+import React, { useState } from "react";
import ReactMarkdown from "react-markdown";
import { noop } from "../../utils";
import "./EditableNoteArea.css";
export default function EditableNoteArea({
- content = null,
+ content = "",
onSave = noop,
preview = false
}) {
- const updateContent = ({ target: { value } }) => onSave(value);
+ // const [newContent, setNewContent] = useState(content);
+ const updateContent = ({ target: { value } }) => {
+ // setNewContent(value);
+ onSave(value);
+ };
return (
{!preview ? (
diff --git a/src/components/NoteContent/NoteContent.js b/src/components/NoteContent/NoteContent.js
index 8922e0d..7dbe716 100644
--- a/src/components/NoteContent/NoteContent.js
+++ b/src/components/NoteContent/NoteContent.js
@@ -4,9 +4,8 @@ import EditableNoteArea from "../EditableNoteArea/EditableNoteArea";
import "./NoteContent.css";
-export default function NoteContent({ note = null, onSave = noop }) {
+export default function NoteContent({ content = "", onSave = noop }) {
const [renderMarkdown, setRenderMarkdown] = useState(false);
- const { content } = note || {};
return (
diff --git a/src/components/NoteList/NoteList.js b/src/components/NoteList/NoteList.js
index 1951675..f858132 100644
--- a/src/components/NoteList/NoteList.js
+++ b/src/components/NoteList/NoteList.js
@@ -17,7 +17,7 @@ export default function NoteList({
{notes.map(n => (
-
, document.getElementById("root"));
+ReactDOM.render(
+
+
+ ,
+ document.getElementById("root")
+);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
diff --git a/src/server/notes/index.js b/src/server/backend/index.js
similarity index 69%
rename from src/server/notes/index.js
rename to src/server/backend/index.js
index 7446db3..974dcea 100755
--- a/src/server/notes/index.js
+++ b/src/server/backend/index.js
@@ -1,4 +1,5 @@
const { loadNotesInDirectory, saveNoteInDirectory } = require("./notes");
+const { loadTags, updateTagsForNote } = require("./tags");
const { defaultSettings } = require("./settings");
function newNoteBackend(initSettings) {
@@ -10,10 +11,14 @@ function newNoteBackend(initSettings) {
switch (type) {
case "getNotes":
const files = await loadNotesInDirectory(directory);
+ const loadedTags = await loadTags(directory);
return {
type: "getNotes",
payload: {
- notes: files.map(f => ({ ...f, tags: [] }))
+ notes: files.map(f => ({
+ ...f,
+ tags: loadedTags[f.title]
+ }))
}
};
case "saveNote":
@@ -23,8 +28,10 @@ function newNoteBackend(initSettings) {
content,
tags
});
- return { type: "saveNote", payload: result };
+ await updateTagsForNote(directory, { title, tags });
+ return { type: "saveNote", payload: { ...result, tags } };
case "getSettings":
+ return { type: "getSettings", payload: { settings } };
case "saveSettings":
default:
return { type: "noop" };
diff --git a/src/server/backend/notes.js b/src/server/backend/notes.js
new file mode 100644
index 0000000..ac050f4
--- /dev/null
+++ b/src/server/backend/notes.js
@@ -0,0 +1,95 @@
+const path = require("path");
+const fs = require("fs");
+
+function listNotesInDirectory(directoryPath = "") {
+ return new Promise(function(resolve, reject) {
+ fs.readdir(directoryPath, function(err, files) {
+ if (err) return reject(err);
+
+ resolve(
+ files
+ .map(fPath => ({
+ ext: path.extname(fPath),
+ name: fPath,
+ title: path.basename(fPath, path.extname(fPath)),
+ path: path.join(directoryPath, fPath)
+ }))
+ .filter(
+ ({ ext }) =>
+ ext === ".md" || ext === ".txt" || ext === ".utf8"
+ )
+ );
+ });
+ });
+}
+
+function getNoteTime(fullPath) {
+ return new Promise((a, r) => {
+ fs.stat(fullPath, (err, stats) => {
+ if (err) return r(err);
+
+ a({
+ lastModified: stats.mtime,
+ created: stats.ctime,
+ size: stats.size
+ });
+ });
+ });
+}
+
+function getNoteContent(fullPath) {
+ return new Promise((a, r) =>
+ fs.readFile(fullPath, "utf8", (err, data) => {
+ if (err) return r(err);
+ a({ content: data });
+ })
+ );
+}
+
+async function loadNotesInDirectory(directoryPath = "") {
+ const files = await listNotesInDirectory(directoryPath);
+
+ return await Promise.all(
+ files.map(async ({ path, title, ext }) => {
+ const [noteTime, noteContent] = await Promise.all([
+ getNoteTime(path),
+ getNoteContent(path)
+ ]);
+
+ return {
+ path,
+ title,
+ ...noteTime,
+ ...noteContent
+ };
+ })
+ );
+}
+
+async function saveNoteInDirectory(
+ { directory, defaultExtension },
+ { title, content }
+) {
+ let fullPath = path.join(directory, `${title}.${defaultExtension}`);
+
+ const match = (await listNotesInDirectory(directory)).filter(
+ f => f.title === title
+ );
+
+ if (match && match.length > 0) fullPath = match[0].path;
+ console.log("tryint to write note: ", fullPath, " - ", match);
+
+ return Promise.all([
+ new Promise((a, r) => {
+ fs.writeFile(fullPath, content, "utf8", err => {
+ if (err) return r(err);
+ a();
+ });
+ })
+ ]);
+}
+
+module.exports = {
+ loadNotesInDirectory,
+ saveNoteInDirectory
+};
diff --git a/src/server/backend/settings.js b/src/server/backend/settings.js
new file mode 100755
index 0000000..eea892e
--- /dev/null
+++ b/src/server/backend/settings.js
@@ -0,0 +1,9 @@
+module.exports = {
+ defaultSettings: {
+ directory: "./",
+ defaultExtension: "txt",
+ autoSave: true, // if notes should save automatically
+ autoSaveDelay: 1000, // ms to wait after last key press to save
+ themes: [{ name: "default" }]
+ }
+};
diff --git a/src/server/backend/tags.js b/src/server/backend/tags.js
new file mode 100755
index 0000000..7d8e111
--- /dev/null
+++ b/src/server/backend/tags.js
@@ -0,0 +1,59 @@
+const path = require("path");
+const fs = require("fs");
+
+function loadTags(directoryPath) {
+ return new Promise((a, r) =>
+ fs.readFile(
+ path.join(directoryPath, "meta.json"),
+ "utf8",
+ async (err, data) => {
+ if (err) {
+ if (!err.message.includes("ENOENT")) return r(err);
+ await saveTags(directoryPath, {});
+ }
+
+ const tagCache = JSON.parse(data);
+ a(tagCache);
+ }
+ )
+ );
+}
+
+function saveTags(directoryPath, tags = {}) {
+ return new Promise((a, r) =>
+ fs.writeFile(
+ path.join(directoryPath, "meta.json"),
+ JSON.stringify(tags),
+ "utf8",
+ err => {
+ if (err) return r(err);
+ a({});
+ }
+ )
+ );
+}
+
+async function updateTagsForNote(directoryPath, { title, tags = [] }) {
+ if (!title || title === "") {
+ return Promise.reject();
+ }
+
+ const allTags = await loadTags(directoryPath);
+ allTags[title] = [...tags];
+ await saveTags(directoryPath, allTags);
+}
+
+async function getTagsForNote(directoryPath, { title }) {
+ if (!title || title === "") {
+ return Promise.reject();
+ }
+
+ const allTags = await loadTags(directoryPath);
+ return allTags[title];
+}
+
+module.exports = {
+ loadTags,
+ getTagsForNote,
+ updateTagsForNote
+};
diff --git a/src/server/index.js b/src/server/index.js
index 15958fc..782cab4 100644
--- a/src/server/index.js
+++ b/src/server/index.js
@@ -1,6 +1,6 @@
const path = require("path");
const { app, shell, BrowserWindow, ipcMain } = require("electron");
-const { createBackend } = require("./notes");
+const { createBackend } = require("./backend");
let win;
function createWindow() {
@@ -21,7 +21,8 @@ function createWindow() {
});
const backend = createBackend({
- directory: "C:/Users/AdamM/Documents/notes"
+ directory: "./.bin"
+ // directory: "C:/Users/AdamM/Documents/notes"
});
ipcMain.on("note_command", async (event, args) => {
diff --git a/src/server/notes/notes.js b/src/server/notes/notes.js
deleted file mode 100644
index 1870c0a..0000000
--- a/src/server/notes/notes.js
+++ /dev/null
@@ -1,175 +0,0 @@
-const path = require("path");
-const fs = require("fs");
-
-const defaultSettings = {
- directory: "./",
- defaultExtension: "txt"
-};
-
-function listNotesInDirectory(directoryPath = "") {
- return new Promise(function(resolve, reject) {
- fs.readdir(directoryPath, function(err, files) {
- if (err) return reject(err);
-
- resolve(
- files
- .map(fPath => ({
- ext: path.extname(fPath),
- name: fPath,
- friendlyName: path.basename(fPath, path.extname(fPath)),
- fullPath: path.join(directoryPath, fPath)
- }))
- .filter(
- ({ ext }) =>
- ext === ".md" || ext === ".txt" || ext === ".utf8"
- )
- );
- });
- });
-}
-
-function getNoteTime(fullPath) {
- return new Promise((a, r) => {
- fs.stat(fullPath, (err, stats) => {
- if (err) return r(err);
-
- a({
- lastModified: stats.mtime,
- created: stats.ctime,
- size: stats.size
- });
- });
- });
-}
-
-function getNoteContent(fullPath) {
- return new Promise((a, r) =>
- fs.readFile(fullPath, "utf8", (err, data) => {
- if (err) return r(err);
- a({ content: data });
- })
- );
-}
-
-function loadTags(directoryPath) {
- return new Promise((a, r) =>
- fs.readFile(
- path.join(directoryPath, "meta.json"),
- "utf8",
- (err, data) => {
- if (err) {
- if (!err.message.includes("ENOENT")) return r(err);
- return a(saveTags(directoryPath, {}));
- }
-
- a(JSON.parse(data));
- }
- )
- );
-}
-
-function saveTags(directoryPath, tags = {}) {
- return new Promise((a, r) =>
- fs.writeFile(
- path.join(directoryPath, "meta.json"),
- JSON.stringify(tags),
- "utf8",
- err => {
- if (err) return r(err);
- a({});
- }
- )
- );
-}
-
-let tagsCache = {};
-function getNoteTags(directoryPath, friendlyName) {
- return Promise.resolve(tagsCache[friendlyName] || []);
-}
-
-async function loadNotesInDirectory(directoryPath = "") {
- const tagCache = await loadTags(directoryPath);
- const files = await listNotesInDirectory(directoryPath);
-
- return await Promise.all(
- files.map(async ({ fullPath, friendlyName, ext }) => {
- const [noteTime, noteContent] = await Promise.all([
- getNoteTime(fullPath),
- getNoteContent(fullPath),
- getNoteTags(directoryPath, friendlyName)
- ]);
-
- return {
- path: fullPath,
- title: friendlyName,
- tags: tagsCache[friendlyName] || [],
- ...noteTime,
- ...noteContent
- };
- })
- );
-}
-
-async function saveNoteInDirectory(
- { directory, defaultExtension },
- { title, content, tags }
-) {
- let fullPath = path.join(directory, `${title}.${defaultExtension}`);
-
- const match = (await listNotesInDirectory(directory)).filter(
- f => f.friendlyName === title
- );
-
- if (match && match.length > 0) fullPath = match[0].path;
-
- tagsCache[title] = tags;
- return Promise.all([
- new Promise((a, r) => {
- fs.writeFile(fullPath, content, "utf8", err => {
- if (err) return r(err);
- a();
- });
- }),
- saveTags(directory, tagsCache)
- ]);
-}
-
-function newNoteBackend(initSettings) {
- let settings = { ...defaultSettings, ...initSettings };
-
- async function onNoteCommand({ type, payload }) {
- console.log(`got command: ${type} -n ${JSON.stringify(payload)}`);
- const { directory } = settings;
- switch (type) {
- case "getNotes":
- const files = await loadNotesInDirectory(directory);
- return {
- type: "getNotes",
- payload: {
- notes: files.map(f => ({ ...f, tags: [] }))
- }
- };
- case "saveNote":
- const { title, content, tags } = payload;
- const result = await saveNoteInDirectory(settings, {
- title,
- content,
- tags
- });
- return { type: "saveNote", payload: result };
- default:
- return { type: "noop" };
- }
- }
-
- function updateSettings({ directory }) {
- settings.directory = directory;
- }
-
- return {
- onNoteCommand,
- updateSettings
- };
-}
-
-module.exports = { createBackend: newNoteBackend };
diff --git a/src/server/notes/settings.js b/src/server/notes/settings.js
deleted file mode 100755
index 2b366b5..0000000
--- a/src/server/notes/settings.js
+++ /dev/null
@@ -1,4 +0,0 @@
-export const defaultSettings = {
- directory: "./",
- defaultExtension: "txt"
-};
diff --git a/src/server/notes/tags.js b/src/server/notes/tags.js
deleted file mode 100755
index e69de29..0000000
diff --git a/src/server/preload.js b/src/server/preload.js
index cb752b3..365c83f 100644
--- a/src/server/preload.js
+++ b/src/server/preload.js
@@ -34,8 +34,13 @@ contextBridge.exposeInMainWorld("api", {
receive,
receiveOnce,
sendLinkNav: target => ipcRenderer.send("element-clicked", target),
+ getSettings: async () => {
+ send("note_command", { type: "getSettings" });
+ const response = await receiveOnce("note_response");
+ return JSON.parse(response);
+ },
getNotes: async () => {
- send("note_command", { type: "getNotes", payload: {} });
+ send("note_command", { type: "getNotes" });
const response = await receiveOnce("note_response");
return JSON.parse(response);
},
diff --git a/src/state/index.js b/src/state/index.js
index 50d6867..0f445f2 100644
--- a/src/state/index.js
+++ b/src/state/index.js
@@ -1,8 +1,18 @@
-import weedux, { middleware } from "weedux";
-import { reducer as noteReducer, actions as acts } from "./actions";
+import { applyMiddleware, createStore, combineReducers } from "redux";
+import thunkMiddleware from "redux-thunk";
+import { createLogger } from "redux-logger";
+import { reducer as noteReducer, actions as acts } from "./notes";
+import { reducer as settingsReducer } from "./settings";
-const { thunk } = middleware;
+const loggerMiddleware = createLogger();
+const reducer = combineReducers({
+ settings: settingsReducer,
+ notes: noteReducer
+});
// note CRUD
-export const store = new weedux({}, noteReducer, [thunk]);
+export const store = createStore(
+ reducer,
+ applyMiddleware(thunkMiddleware, loggerMiddleware)
+);
export const actions = acts;
diff --git a/src/state/actions.js b/src/state/notes.js
similarity index 78%
rename from src/state/actions.js
rename to src/state/notes.js
index 39625c0..ee84989 100644
--- a/src/state/actions.js
+++ b/src/state/notes.js
@@ -12,9 +12,17 @@ const initialState = {
export const reducer = (cs = initialState, { type, payload }) => {
switch (type) {
case "SELECT_NOTE":
- return { ...cs, selectedNote: payload.selectedNote };
+ return { ...cs, selectedNote: { ...payload.selectedNote } };
case "SAVE_NOTE_START":
+ return {
+ ...cs,
+ selectedNote: { ...cs.selectedNote, content: payload.content }
+ };
case "SAVE_NOTE_SUCCESS":
+ return {
+ ...cs,
+ selectedNote: { ...payload.note }
+ };
case "GET_NOTES_START":
return { ...cs, notesLoading: true };
case "GET_NOTES_SUCCESS":
@@ -34,7 +42,7 @@ export const reducer = (cs = initialState, { type, payload }) => {
const selectNote = ({ title }) => (dispatch, getState) => {
const normalizedTitle = title.toLocaleLowerCase();
- const { selectedNote, notes } = getState();
+ const { selectedNote, notes } = getState().notes;
let foundNote = selectedNote;
const notesFilter = notes.filter(
@@ -48,13 +56,14 @@ const selectNote = ({ title }) => (dispatch, getState) => {
const deleteNote = ({ title }) => (dispatch, getState) => {};
-const createNote = ({ title, content, tags = [] }) => async (
+const upsertNote = ({ title, content, tags = [] }) => async (
dispatch,
getState
) => {
- dispatch({ type: "SAVE_NOTE_START" });
+ dispatch({ type: "SAVE_NOTE_START", payload: { title, content, tags } });
try {
const result = await upsertNoteBackend({ title, content, tags });
+ dispatch(getNotes());
dispatch({ type: "SAVE_NOTE_SUCCESS", payload: { ...result } });
} catch (error) {
dispatch({ type: "SAVE_NOTE_FAIL", payload: { error } });
@@ -62,7 +71,7 @@ const createNote = ({ title, content, tags = [] }) => async (
};
const getNotes = () => async (dispatch, getState) => {
- const { notesLoading } = getState();
+ const { notesLoading } = getState().notes;
if (notesLoading) {
console.warn(
"trying to load notes when the operation has already begun."
@@ -73,7 +82,6 @@ const getNotes = () => async (dispatch, getState) => {
dispatch({ type: "GET_NOTES_START" });
try {
const result = await getNotesBackend();
- console.log(result);
dispatch({ type: "GET_NOTES_SUCCESS", payload: result });
} catch (error) {
dispatch({ type: "GET_NOTES_FAIL", payload: { error } });
@@ -87,7 +95,7 @@ const updateTags = ({ title, tags }) => (dispatch, getState) => {
export const actions = {
getNotes,
deleteNote,
- createNote,
+ upsertNote,
selectNote,
updateTags
};
diff --git a/src/state/settings.js b/src/state/settings.js
new file mode 100644
index 0000000..5353dd4
--- /dev/null
+++ b/src/state/settings.js
@@ -0,0 +1,22 @@
+import { getSettings as getSettingsbackend } from "./api";
+
+import { defaultSettings } from "../server/backend/settings";
+
+const initialState = {
+ settings: { ...defaultSettings },
+ loading: false,
+ error: null
+};
+
+export const reducer = (cs = initialState, { type, payload }) => {
+ switch (type) {
+ case "GET_SETTINGS_START":
+ return { ...cs, loading: true };
+ case "GET_SETTINGS_SUCCESS":
+ return { ...cs, settings: { ...payload.settings }, loading: false };
+ case "GET_SETTINGS_FAIL":
+ return { ...cs, error: payload.error, loading: false };
+ default:
+ return { ...cs };
+ }
+};
diff --git a/yarn.lock b/yarn.lock
index 27105cc..6b29742 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -930,6 +930,13 @@
dependencies:
regenerator-runtime "^0.13.2"
+"@babel/runtime@^7.5.5":
+ version "7.8.3"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.3.tgz#0811944f73a6c926bb2ad35e918dcc1bfab279f1"
+ integrity sha512-fVHx1rzEmwB130VTkLnxR+HmxcTjGzH12LYQcFFoBwakMd3aOMD4OsRN7tGG/UOYE2ektgFrS8uACAoRk1CY0w==
+ dependencies:
+ regenerator-runtime "^0.13.2"
+
"@babel/template@^7.4.0", "@babel/template@^7.7.4":
version "7.7.4"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.7.4.tgz#428a7d9eecffe27deac0a98e23bf8e3675d2a77b"
@@ -3488,6 +3495,11 @@ decompress-response@^3.3.0:
dependencies:
mimic-response "^1.0.0"
+deep-diff@^0.3.5:
+ version "0.3.8"
+ resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
+ integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=
+
deep-equal@^1.0.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a"
@@ -5072,6 +5084,13 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
+hoist-non-react-statics@^3.3.0:
+ version "3.3.1"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#101685d3aff3b23ea213163f6e8e12f4f111e19f"
+ integrity sha512-wbg3bpgA/ZqWrZuMOeJi8+SKMhr7X9TesL/rXMjTzh0p0JUBo3II8DHboYbuIXWRlttrUFxwcu/5kygrCw8fJw==
+ dependencies:
+ react-is "^16.7.0"
+
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
@@ -8896,7 +8915,7 @@ react-error-overlay@^6.0.4:
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.4.tgz#0d165d6d27488e660bc08e57bdabaad741366f7a"
integrity sha512-ueZzLmHltszTshDMwyfELDq8zOA803wQ1ZuzCccXa1m57k1PxSHfflPD5W9YIiTXLs0JTLzoj6o1LuM5N6zzNA==
-react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6:
+react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
@@ -8915,6 +8934,18 @@ react-markdown@^4.3.1:
unist-util-visit "^1.3.0"
xtend "^4.0.1"
+react-redux@^7.1.3:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79"
+ integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ hoist-non-react-statics "^3.3.0"
+ invariant "^2.2.4"
+ loose-envify "^1.4.0"
+ prop-types "^15.7.2"
+ react-is "^16.9.0"
+
react-scripts@3.3.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.3.0.tgz#f26a21f208f20bd04770f43e50b5bbc151920c2a"
@@ -9071,6 +9102,26 @@ redent@^3.0.0:
indent-string "^4.0.0"
strip-indent "^3.0.0"
+redux-logger@^3.0.6:
+ version "3.0.6"
+ resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
+ integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8=
+ dependencies:
+ deep-diff "^0.3.5"
+
+redux-thunk@^2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
+ integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
+
+redux@^4.0.5:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
+ integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
+ dependencies:
+ loose-envify "^1.4.0"
+ symbol-observable "^1.2.0"
+
regenerate-unicode-properties@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
@@ -10229,6 +10280,11 @@ svgo@^1.0.0, svgo@^1.2.2:
unquote "~1.1.1"
util.promisify "~1.0.0"
+symbol-observable@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
+ integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
+
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"