diff --git a/public/index.html b/public/index.html
index 270a5c9..b234fc1 100644
--- a/public/index.html
+++ b/public/index.html
@@ -494,6 +494,7 @@
+
diff --git a/public/js/notes.js b/public/js/notes.js
index d823f0a..d898e2e 100644
--- a/public/js/notes.js
+++ b/public/js/notes.js
@@ -159,16 +159,8 @@
if (!title) return;
var body = getBody();
var isNew = _activeId == null;
- var url = isNew ? '/api/notes' : '/api/notes/' + _activeId;
- var method = isNew ? 'POST' : 'PUT';
try {
- fetch(url, {
- method: method,
- keepalive: true,
- credentials: 'include',
- headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
- body: JSON.stringify({ title: title, body: body }),
- });
+ window.NotesApi.saveNoteKeepalive(isNew ? null : _activeId, { title: title, body: body });
} catch (e) {}
});
@@ -218,8 +210,8 @@
if (!listEl) return;
listEl.innerHTML = '
Loading…
';
Promise.all([
- fetch('/api/notes', { headers: getAuthHeaders(), credentials: 'include' }).then(function(r) { return r.json(); }),
- fetch('/api/notes/trash', { headers: getAuthHeaders(), credentials: 'include' }).then(function(r) { return r.json(); }),
+ window.NotesApi.listActive(),
+ window.NotesApi.listTrash(),
])
.then(function(results) {
var active = results[0], trash = results[1];
@@ -299,10 +291,7 @@
}
function restoreNote(id) {
- fetch('/api/notes/' + id + '/restore', {
- method: 'POST', credentials: 'include', headers: getAuthHeaders(),
- })
- .then(function(r) { return r.json(); })
+ window.NotesApi.restore(id)
.then(function(data) {
if (!data.success) { if (typeof showToast === 'function') showToast(data.error || 'Restore failed', 'error'); return; }
if (typeof showToast === 'function') showToast('Note restored', 'success');
@@ -316,10 +305,7 @@
function hardDeleteNote(id) {
if (typeof showConfirm !== 'function') return;
showConfirm('Delete forever? This cannot be undone.', function() {
- fetch('/api/notes/' + id + '?hard=1', {
- method: 'DELETE', credentials: 'include', headers: getAuthHeaders(),
- })
- .then(function(r) { return r.json(); })
+ window.NotesApi.hardDelete(id)
.then(function(data) {
if (!data.success) { if (typeof showToast === 'function') showToast(data.error || 'Delete failed', 'error'); return; }
refreshList();
@@ -332,10 +318,7 @@
if (_trash.length === 0) return;
showConfirm('Empty the trash? This will permanently delete ' + _trash.length + ' note' + (_trash.length === 1 ? '' : 's') + '.',
function() {
- fetch('/api/notes/trash/empty', {
- method: 'POST', credentials: 'include', headers: getAuthHeaders(),
- })
- .then(function(r) { return r.json(); })
+ window.NotesApi.emptyTrash()
.then(function(data) {
if (!data.success) { if (typeof showToast === 'function') showToast(data.error || 'Empty trash failed', 'error'); return; }
if (typeof showToast === 'function') showToast('Trash emptied', 'success');
@@ -452,19 +435,11 @@
}
var isNew = _activeId == null;
- var url = isNew ? '/api/notes' : '/api/notes/' + _activeId;
- var method = isNew ? 'POST' : 'PUT';
_autosaveInFlight = true;
updateStatus('Saving…', 'saving');
- fetch(url, {
- method: method,
- headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
- credentials: 'include',
- body: JSON.stringify({ title: title, body: body }),
- })
- .then(function(r) { return r.json(); })
+ window.NotesApi.saveNote(isNew ? null : _activeId, { title: title, body: body })
.then(function(data) {
_autosaveInFlight = false;
if (!data.success) { updateStatus(data.error || 'Save failed', 'err'); return; }
@@ -474,8 +449,7 @@
// Refresh the list so the new/updated note appears with correct
// timestamps + ordering.
- return fetch('/api/notes', { headers: getAuthHeaders(), credentials: 'include' })
- .then(function(r) { return r.json(); })
+ return window.NotesApi.listActive()
.then(function(d) {
if (d.success) _notes = d.notes || [];
var note = _notes.find(function(n) { return n.id === _activeId; });
@@ -512,8 +486,7 @@
// The user can Restore from there or Empty trash to actually erase.
var doMove = function() {
flushAutosave();
- fetch('/api/notes/' + _activeId, { method: 'DELETE', headers: getAuthHeaders(), credentials: 'include' })
- .then(function(r) { return r.json(); })
+ window.NotesApi.moveToTrash(_activeId)
.then(function(data) {
if (!data.success) { updateStatus(data.error || 'Move to trash failed', 'err'); return; }
_notes = _notes.filter(function(n) { return n.id !== _activeId; });
@@ -597,13 +570,7 @@
updateStatus('Generating note…', 'saving');
var modelEl = document.getElementById('notes-model-select');
var selectedModel = modelEl ? modelEl.value : '';
- return fetch('/api/notes/from-voice', {
- method: 'POST',
- credentials: 'include',
- headers: Object.assign({ 'Content-Type': 'application/json' }, getAuthHeaders()),
- body: JSON.stringify({ transcript: resp.text, model: selectedModel || undefined }),
- })
- .then(function(r) { return r.json(); })
+ return window.NotesApi.noteFromVoice({ transcript: resp.text, model: selectedModel || undefined })
.then(function(data) {
setRecUI('idle');
if (!data.success) { updateStatus(data.error || 'Generation failed', 'err'); return; }
@@ -883,8 +850,4 @@
if (sameDay) return d.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: d.getFullYear() === now.getFullYear() ? undefined : 'numeric' });
}
- function getAuthHeaders() {
- if (typeof window.getAuthHeaders === 'function') return window.getAuthHeaders();
- return {};
- }
})();
diff --git a/public/js/notes/api.js b/public/js/notes/api.js
new file mode 100644
index 0000000..2509e98
--- /dev/null
+++ b/public/js/notes/api.js
@@ -0,0 +1,94 @@
+// Shared API helpers for the classic notes script.
+(function() {
+ 'use strict';
+
+ function authHeaders() {
+ return window.getAuthHeaders ? window.getAuthHeaders() : { 'Content-Type': 'application/json' };
+ }
+
+ function json(response) {
+ return response.json().catch(function() { return {}; });
+ }
+
+ function listActive() {
+ return fetch('/api/notes', { headers: authHeaders(), credentials: 'include' }).then(json);
+ }
+
+ function listTrash() {
+ return fetch('/api/notes/trash', { headers: authHeaders(), credentials: 'include' }).then(json);
+ }
+
+ function saveNote(id, payload) {
+ var isNew = id == null;
+ return fetch(isNew ? '/api/notes' : '/api/notes/' + id, {
+ method: isNew ? 'POST' : 'PUT',
+ headers: authHeaders(),
+ credentials: 'include',
+ body: JSON.stringify(payload || {})
+ }).then(json);
+ }
+
+ function saveNoteKeepalive(id, payload) {
+ var isNew = id == null;
+ return fetch(isNew ? '/api/notes' : '/api/notes/' + id, {
+ method: isNew ? 'POST' : 'PUT',
+ keepalive: true,
+ credentials: 'include',
+ headers: authHeaders(),
+ body: JSON.stringify(payload || {})
+ });
+ }
+
+ function restore(id) {
+ return fetch('/api/notes/' + id + '/restore', {
+ method: 'POST',
+ credentials: 'include',
+ headers: authHeaders()
+ }).then(json);
+ }
+
+ function hardDelete(id) {
+ return fetch('/api/notes/' + id + '?hard=1', {
+ method: 'DELETE',
+ credentials: 'include',
+ headers: authHeaders()
+ }).then(json);
+ }
+
+ function emptyTrash() {
+ return fetch('/api/notes/trash/empty', {
+ method: 'POST',
+ credentials: 'include',
+ headers: authHeaders()
+ }).then(json);
+ }
+
+ function moveToTrash(id) {
+ return fetch('/api/notes/' + id, {
+ method: 'DELETE',
+ headers: authHeaders(),
+ credentials: 'include'
+ }).then(json);
+ }
+
+ function noteFromVoice(payload) {
+ return fetch('/api/notes/from-voice', {
+ method: 'POST',
+ credentials: 'include',
+ headers: authHeaders(),
+ body: JSON.stringify(payload || {})
+ }).then(json);
+ }
+
+ window.NotesApi = {
+ listActive: listActive,
+ listTrash: listTrash,
+ saveNote: saveNote,
+ saveNoteKeepalive: saveNoteKeepalive,
+ restore: restore,
+ hardDelete: hardDelete,
+ emptyTrash: emptyTrash,
+ moveToTrash: moveToTrash,
+ noteFromVoice: noteFromVoice
+ };
+})();