works but totally sucks and needs a rewrite
parent
168df6cfbc
commit
31a991cd69
|
|
@ -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": {
|
||||
|
|
|
|||
42
src/App.js
42
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 (
|
||||
<div className="App">
|
||||
|
|
@ -72,15 +77,18 @@ class App extends PureComponent {
|
|||
onTagUpdate={this.handleTagUpdate}
|
||||
selectedNote={selectedNote}
|
||||
/>
|
||||
<NoteContent note={selectedNote} onSave={handleSave} />
|
||||
<NoteContent {...selectedNote} onSave={handleSave} />
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
border: none;
|
||||
background-color: black;
|
||||
color: white;
|
||||
resize: none;
|
||||
}
|
||||
|
||||
.EditableNoteArea .markdownPreview {
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<section className="EditableNoteArea">
|
||||
{!preview ? (
|
||||
<textarea
|
||||
value={content || ""}
|
||||
resizeable={false}
|
||||
value={content}
|
||||
placeholder="This note feels cold and empty inside"
|
||||
onChange={updateContent}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="NoteContent">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default function NoteList({
|
|||
<ul className="NoteList" onKeyDown={handleShortcuts}>
|
||||
{notes.map(n => (
|
||||
<li
|
||||
className={n.title === selectedNoteTitle && "selected"}
|
||||
className={n.title === selectedNoteTitle ? "selected" : ""}
|
||||
key={n.title}
|
||||
>
|
||||
<NoteListItem
|
||||
|
|
|
|||
|
|
@ -1,10 +1,17 @@
|
|||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import { Provider } from "react-redux";
|
||||
import { store } from "./state";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById("root"));
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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" };
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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" }]
|
||||
}
|
||||
};
|
||||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
export const defaultSettings = {
|
||||
directory: "./",
|
||||
defaultExtension: "txt"
|
||||
};
|
||||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
@ -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 };
|
||||
}
|
||||
};
|
||||
58
yarn.lock
58
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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue