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 + }; +})();