~dricottone/noticable

ref: 0476f14a55286bf3ad5e6b0ff3142cc408abd8f6 noticable/preload.js -rw-r--r-- 10.7 KiB
0476f14aDominic Ricottone Bumping versions of this and dependencies 2 years ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
////////////////////////////
// Global state goes here //
////////////////////////////

const { ipcRenderer } = require("electron");
const fs = require("fs");
const os = require("os");
const path = require("path");
const md = require("markdown-it")({ html: true });
var currentFile = "";
var currentNote = "";
var currentNotes = [];
const newFileButton = "+ New Note";

const dirNotes = path.join(os.homedir(), "notes");


///////////////////////
// Functions go here //
///////////////////////

// Push filenames to be relative to the notes directory.
// NOTE: In this module, we need to concatenate the notes directory and the
//       relative path. Different modules have different needs.
function relativeNotePath(filename) {
  return path.join(dirNotes, filename);
};

// Cache the new file name and ask renderer to send editor content to be saved.
function saveFile(filename) {
  currentFile = filename;
  rendererSendContentForSave();
};

// If the file name is cached, ask renderer to send editor content to be saved.
// Otherwise ask main to prompt for a new file name and proceed through
// `saveFile` logic.
function trySaveFile() {
  if (currentFile == "") {
    mainPromptSave();
  } else {
    rendererSendContentForSave();
  }
};

// Cache the new file name and ask renderer to send editor content to be saved
// then reset the editor.
function saveFileThenNewFile(filename) {
  currentFile = filename;
  rendererSendContentForSaveThenNew();
};

// If the file name is cached, ask renderer to send editor content to be saved.
// Otherwise ask main to prompt for a new file name and proceed through
// `saveFileThenNewFile` logic.
function trySaveFileThenNewFile() {
  if (currentFile == "") {
    mainPromptSaveDiscardableThenNew();
  } else {
    rendererSendContentForSaveThenNew();
  }
};

// If the file name is cached, save the cached content to it. Otherwise ask
// main to prompt for a new file name and proceed through
// `saveFileThenReadFile` logic.
function trySaveFileThenReadFile(filename) {
  if (currentFile == "") {
    mainPromptSaveDiscardableThenRead(filename);
  } else {
    writeFileThenReadFile(currentFile, currentNote, filename);
  }
};

// Read a directory and return a sorted array of all file names.
function readNotesDirectory(directory) {
  let files = fs.readdirSync(directory);
  return files.sort((a,b) => a.localeCompare(b));
};

// Read a file and send the content and title to the renderer.
function readNoteFromFile(filename) {
  fs.readFile(relativeNotePath(filename), "utf8", (err, content) => {
    if (err) {
      announceFileUnreadable();
    } else {
      currentFile = filename;
      currentNote = content;
      rendererNewTitle(filename);
      rendererNewContent(content);
      rendererNewHTML(md.render(content));
    }
  });
};

// Reset local cache and update renderer's state.
// NOTE: This is a destructive operation. Changes should have been saved or
//       discarded with user permission *first*.
function newNote() {
  currentFile = "";
  currentNote = "";
  rendererNewTitle("");
  rendererNewContent("");
  rendererNewHTML("");
};

// Write a note to a file.
function writeFile(filename, content) {
  console.log("trying to save " + filename);
  fs.writeFile(relativeNotePath(filename), content, (err) => {
    if (err) {
      announceFileUnwritable();
      mainRePromptSave();
    } else {
      currentFile = filename;
      currentNote = content;
      rendererNewTitle(filename);
    }
  });
};

// Write a note to a file and then open a new file.
function writeFileThenNewFile(filename, content) {
  console.log("trying to save " + filename);
  fs.writeFile(relativeNotePath(filename), content, (err) => {
    if (err) {
      announceFileUnwritable();
      mainRePromptSaveThenNew();
    } else {
      rendererAddTitle(filename);
      newNote()
    }
  });
};

// Write a note to a file and then read another file.
function writeFileThenReadFile(toWriteFilename, content, toReadFilename) {
  console.log("trying to save " + toWriteFilename);
  fs.writeFile(relativeNotePath(toWriteFilename), content, (err) => {
    if (err) {
      announceFileUnwritable();
      mainRePromptSaveThenRead();
    } else {
      rendererAddTitle(toWriteFilename);
      readNoteFromFile(toReadFilename);
    }
  });
};

// Ask renderer to show the editor.
function rendererShowEditor() {
  window.postMessage({ type: "showEditor" }, "*")
};

// Ask renderer to show the viewer.
function rendererShowViewer() {
  window.postMessage({ type: "showViewer" }, "*")
};

// Ask renderer to send editor content so that it can be saved.
function rendererSendContentForSave() {
  window.postMessage({ type: "sendContentForSave" }, "*")
};

// Ask renderer to send editor content so that it can be saved and then
// reset.
function rendererSendContentForSaveThenNew() {
  window.postMessage({ type: "sendContentForSaveThenNew" }, "*")
};

// Ask renderer to send editor content so that it can be checked and then
// conditionally reset.
function rendererSendContentForCheckThenNew() {
  window.postMessage({ type: "sendContentForCheckThenNew" }, "*")
};

// Ask renderer to send editor content so that it can be rendered.
function rendererSendContentForRender() {
  window.postMessage({ type: "sendContentForRender" }, "*")
};

// Ask renderer to add a note title to the sidebar.
function rendererAddTitle(filename) {
    if (currentNotes.indexOf(filename)==-1) {
      currentNotes.push(filename);
      window.postMessage({ type: "addTitle", text: filename }, "*")
    }
};

// Ask renderer to add a note title to the sidebar. Does not need to trigger a
// re-sort. Should only be used on initialization.
function rendererAddTitleOrdered(filename) {
  window.postMessage({ type: "addTitleOrdered", text: filename }, "*")
};

// Ask renderer to highlight a new note title in the sidebar.
function rendererNewTitle(filename) {
  if (filename!="") {
    rendererAddTitle(filename);
    window.postMessage({ type: "newTitle", text: filename }, "*")
  }
};

// Ask renderer to show new content in the editor.
function rendererNewContent(content) {
  window.postMessage({ type: "newContent", text: content }, "*")
};

// Ask renderer to show new HTML in the viewer.
function rendererNewHTML(html) {
  window.postMessage({ type: "newHTML", text: html }, "*")
};

// Ask main to prompt for a new file name.
function mainPromptSave() {
  ipcRenderer.send("promptSave", "");
};

// Ask main to prompt for either permission to discard changes or a new file
// name then open a new file.
function mainPromptSaveDiscardableThenNew() {
  ipcRenderer.send("promptSaveDiscardableThenNew", "");
};

// Ask main to prompt for either permission to discard changes or a new file
// name then read a new file.
function mainPromptSaveDiscardableThenRead(filename) {
  ipcRenderer.send("promptSaveDiscardableThenRead", filename);
};

// Ask main to prompt for a new file after the previous file name failed to
// work.
function mainRePromptSave() {
  ipcRenderer.send("rePromptSave", "");
};

// Ask main to prompt for a new file after the previous file name failed to
// write then open a new file.
function mainRePromptSaveThenNew() {
  ipcRenderer.send("rePromptSaveThenNew", "");
}

// Ask main to prompt for a new file after the previous file name failed to
// write then read another file.
function mainRePromptSaveThenRead(filename) {
  ipcRenderer.send("rePromptSaveThenRead", filename);
}

// Announce that a file is unreadable.
function announceFileUnreadable() {
  ipcRenderer.send("fileUnreadable", "");
  window.postMessage({ type: "fileUnreadable" }, "*")
};

// Announce that a file is unwritable.
function announceFileUnwritable() {
  ipcRenderer.send("fileUnwritable", "");
  window.postMessage({ type: "fileUnwritable" }, "*")
};

// Main announced that a file was not saved. Broadcast this announcement.
function broadcastFileNotSaved() {
  window.postMessage({ type: "fileNotSaved" }, "*")
};

// Main announced that a file was discarded. Broadcast this announcement.
function broadcastFileDiscarded() {
  window.postMessage({ type: "fileDiscarded" }, "*")
};


////////////////////////////
// Listen for events here //
////////////////////////////
ipcRenderer.on("saveFile", (_, filename) => saveFile(filename));
ipcRenderer.on("saveFileThenNewFile", (_, filename) => saveFileThenNewFile(filename));
ipcRenderer.on("saveFileThenReadFile", (_, filenames) => writeFileThenReadFile(filenames.toSave, currentNote, filenames.toRead));
ipcRenderer.on("trySaveFile", () => trySaveFile());
ipcRenderer.on("trySaveFileThenNewFile", () => trySaveFileThenNewFile());
ipcRenderer.on("trySaveFileThenReadFile", (_, filename) => trySaveFileThenReadFile(filename));
ipcRenderer.on("reSaveFile", (_, filename) => writeFile(filename, currentNote));
ipcRenderer.on("reSaveFileThenNewFile", (_, filename) => writeFileThenNewFile(filename, currentNote));
ipcRenderer.on("reSaveFileThenReadFile", (_, filenames) => writeFileThenReadFile(filenames.toSave, currentNote, filenames.toRead));
ipcRenderer.on("fileNotSaved", () => broadcastFileNotSaved());
ipcRenderer.on("fileDiscardedForNewFile", () => {
  broadcastFileDiscarded();
  newNote();
});
ipcRenderer.on("fileDiscardedForReadFile", (_, filename) => {
  broadcastFileDiscarded();
  readNoteFromFile(filename);
});
ipcRenderer.on("rendererShowViewer", () => rendererShowViewer());
ipcRenderer.on("rendererShowEditor", () => rendererShowEditor());
ipcRenderer.on("rendererSendContentForCheckThenNew", () => rendererSendContentForCheckThenNew());
ipcRenderer.on("rendererSendContentForRender", () => rendererSendContentForRender());

window.addEventListener("message", (event) => {
  if (event.source != window) return;
  if (event.data.type) {
    let parameter = event.data.text;
    switch(event.data.type) {
      case "contentForSave":
        writeFile(currentFile, parameter);
        break;
      case "contentForSaveThenNew":
        writeFileThenNewFile(currentFile, parameter);
        break;
      case "contentForSaveThenRead":
        writeFileThenReadFile(currentFile, parameter.note, parameter.title);
        break;
      case "contentForCheckThenNew":
        if (currentNote!=parameter) {
          currentNote = parameter.note;
          mainPromptSaveDiscardableThenNew();
        } else {
          newNote();
        }
        break;
      case "contentForCheckThenRead":
        if (currentNote!=parameter.note) {
          currentNote = parameter.note;
          mainPromptSaveDiscardableThenRead(parameter.title);
        } else {
          readNoteFromFile(parameter.title)
        }
        break;
      case "contentForRender":
        rendererNewHTML(md.render(parameter));
        rendererShowViewer();
        break;
    }
  }
}, false);

window.addEventListener("load", () => {
  currentNotes = readNotesDirectory(dirNotes);
  currentNotes.forEach(filename => {
    rendererAddTitleOrdered(filename);
  });
});