parent
168df6cfbc
commit
31a991cd69
@ -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,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"
|
|
||||||
};
|
|
@ -1,8 +1,18 @@
|
|||||||
import weedux, { middleware } from "weedux";
|
import { applyMiddleware, createStore, combineReducers } from "redux";
|
||||||
import { reducer as noteReducer, actions as acts } from "./actions";
|
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
|
// note CRUD
|
||||||
export const store = new weedux({}, noteReducer, [thunk]);
|
export const store = createStore(
|
||||||
|
reducer,
|
||||||
|
applyMiddleware(thunkMiddleware, loggerMiddleware)
|
||||||
|
);
|
||||||
export const actions = acts;
|
export const actions = acts;
|
||||||
|
@ -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 };
|
||||||
|
}
|
||||||
|
};
|
Loading…
Reference in new issue