From 2d24bf81dd8aa6ade9846312406a6e4c06c4f754 Mon Sep 17 00:00:00 2001 From: azivner Date: Wed, 1 Aug 2018 09:26:02 +0200 Subject: [PATCH] added new label "sorted" which will keep children notes alphabetically sorted, fixes #82 --- src/app.js | 1 + src/entities/note.js | 4 +++ src/public/javascripts/services/messaging.js | 16 ++++++++--- .../javascripts/services/note_detail.js | 2 +- src/public/javascripts/services/tree.js | 8 +++++- src/services/date_notes.js | 3 +++ src/services/events.js | 13 +++++++++ src/services/handlers.js | 27 +++++++++++++++++++ src/services/notes.js | 15 +++++++++++ src/services/relations.js | 14 +++++++--- src/services/script.js | 4 +-- src/services/script_context.js | 3 +++ 12 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 src/services/handlers.js diff --git a/src/app.js b/src/app.js index da3e27d1b..b8a532e51 100644 --- a/src/app.js +++ b/src/app.js @@ -11,6 +11,7 @@ const os = require('os'); const sessionSecret = require('./services/session_secret'); const cls = require('./services/cls'); require('./entities/entity_constructor'); +require('./services/handlers'); const app = express(); diff --git a/src/entities/note.js b/src/entities/note.js index fc4983d6a..0aaa094bc 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -30,6 +30,10 @@ class Note extends Entity { catch(e) {} } + isRoot() { + return this.noteId === 'root'; + } + isJson() { return this.mime === "application/json"; } diff --git a/src/public/javascripts/services/messaging.js b/src/public/javascripts/services/messaging.js index cc65df357..8177b0d83 100644 --- a/src/public/javascripts/services/messaging.js +++ b/src/public/javascripts/services/messaging.js @@ -3,6 +3,7 @@ import infoService from "./info.js"; const $outstandingSyncsCount = $("#outstanding-syncs-count"); +const syncMessageHandlers = []; const messageHandlers = []; let ws; @@ -25,9 +26,17 @@ function subscribeToMessages(messageHandler) { messageHandlers.push(messageHandler); } +function subscribeToSyncMessages(messageHandler) { + syncMessageHandlers.push(messageHandler); +} + function handleMessage(event) { const message = JSON.parse(event.data); + for (const messageHandler of messageHandlers) { + messageHandler(message); + } + if (message.type === 'sync') { lastPingTs = new Date().getTime(); @@ -39,8 +48,8 @@ function handleMessage(event) { const syncData = message.data.filter(sync => sync.sourceId !== glob.sourceId); - for (const messageHandler of messageHandlers) { - messageHandler(syncData); + for (const syncMessageHandler of syncMessageHandlers) { + syncMessageHandler(syncData); } $outstandingSyncsCount.html(message.outstandingSyncs); @@ -104,5 +113,6 @@ setTimeout(() => { export default { logError, - subscribeToMessages + subscribeToMessages, + subscribeToSyncMessages }; \ No newline at end of file diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index fbe863243..9558adbdc 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -276,7 +276,7 @@ function focus() { getComponent(note.type).focus(); } -messagingService.subscribeToMessages(syncData => { +messagingService.subscribeToSyncMessages(syncData => { if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) { infoService.showMessage('Reloading note because of background changes'); diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 71efe1c21..a6dcf24bf 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -505,7 +505,13 @@ async function showTree() { initFancyTree(tree); } -messagingService.subscribeToMessages(syncData => { +messagingService.subscribeToMessages(message => { + if (message.type === 'refresh-tree') { + reload(); + } +}); + +messagingService.subscribeToSyncMessages(syncData => { if (syncData.some(sync => sync.entityName === 'branches') || syncData.some(sync => sync.entityName === 'notes')) { diff --git a/src/services/date_notes.js b/src/services/date_notes.js index c842d6488..7e28efbfd 100644 --- a/src/services/date_notes.js +++ b/src/services/date_notes.js @@ -42,6 +42,7 @@ async function getRootCalendarNote() { })).note; await labelService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); + await labelService.createLabel(rootNote.noteId, 'sorted'); } return rootNote; @@ -60,6 +61,7 @@ async function getYearNote(dateTimeStr, rootNote) { } await labelService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr); + await labelService.createLabel(yearNote.noteId, 'sorted'); } return yearNote; @@ -85,6 +87,7 @@ async function getMonthNote(dateTimeStr, rootNote) { } await labelService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr); + await labelService.createLabel(monthNote.noteId, 'sorted'); } return monthNote; diff --git a/src/services/events.js b/src/services/events.js index 1e7c78b57..e9e84e5a5 100644 --- a/src/services/events.js +++ b/src/services/events.js @@ -1,3 +1,4 @@ +const NOTE_TITLE_CHANGED = "NOTE_TITLE_CHANGED"; const ENTER_PROTECTED_SESSION = "ENTER_PROTECTED_SESSION"; const ENTITY_CHANGED = "ENTITY_CHANGED"; @@ -19,10 +20,22 @@ function emit(eventType, data) { } } +async function syncEmit(eventType, data) { + const listeners = eventListeners[eventType]; + + if (listeners) { + for (const listener of listeners) { + await listener(data); + } + } +} + module.exports = { subscribe, emit, + syncEmit, // event types: + NOTE_TITLE_CHANGED, ENTER_PROTECTED_SESSION, ENTITY_CHANGED }; \ No newline at end of file diff --git a/src/services/handlers.js b/src/services/handlers.js new file mode 100644 index 000000000..32b17c3a9 --- /dev/null +++ b/src/services/handlers.js @@ -0,0 +1,27 @@ +const eventService = require('./events'); +const scriptService = require('./script'); +const relationService = require('./relations'); +const treeService = require('./tree'); +const messagingService = require('./messaging'); + +eventService.subscribe(eventService.NOTE_TITLE_CHANGED, async note => { + const relations = await relationService.getEffectiveRelations(note.noteId, 'runOnNoteTitleChange'); + + for (const relation of relations) { + const scriptNote = await relation.getTargetNote(); + + await scriptService.executeNote(scriptNote, scriptNote, note); + } + + if (!note.isRoot()) { + const parents = await note.getParentNotes(); + + for (const parent of parents) { + if (await parent.hasLabel("sorted")) { + await treeService.sortNotesAlphabetically(parent.noteId); + + messagingService.sendMessageToAllClients({ type: 'refresh-tree' }); + } + } + } +}); \ No newline at end of file diff --git a/src/services/notes.js b/src/services/notes.js index ed5be7bc8..b32b658cb 100644 --- a/src/services/notes.js +++ b/src/services/notes.js @@ -3,6 +3,7 @@ const optionService = require('./options'); const dateUtils = require('./date_utils'); const syncTableService = require('./sync_table'); const labelService = require('./labels'); +const eventService = require('./events'); const repository = require('./repository'); const Note = require('../entities/note'); const NoteImage = require('../entities/note_image'); @@ -34,6 +35,10 @@ async function getNewNotePosition(parentNoteId, noteData) { return newNotePos; } +async function triggerNoteTitleChanged(note) { + await eventService.emit(eventService.NOTE_TITLE_CHANGED, note); +} + async function createNewNote(parentNoteId, noteData) { const newNotePos = await getNewNotePosition(parentNoteId, noteData); @@ -60,6 +65,8 @@ async function createNewNote(parentNoteId, noteData) { isExpanded: 0 }).save(); + await triggerNoteTitleChanged(note); + return { note, branch @@ -92,6 +99,8 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {}) } } + await triggerNoteTitleChanged(note); + return {note, branch}; } @@ -200,11 +209,17 @@ async function updateNote(noteId, noteUpdates) { await saveNoteRevision(note); + const noteTitleChanged = note.title !== noteUpdates.title; + note.title = noteUpdates.title; note.setContent(noteUpdates.content); note.isProtected = noteUpdates.isProtected; await note.save(); + if (noteTitleChanged) { + await triggerNoteTitleChanged(note); + } + await saveNoteImages(note); await protectNoteRevisions(note); diff --git a/src/services/relations.js b/src/services/relations.js index 5c273871f..426683f78 100644 --- a/src/services/relations.js +++ b/src/services/relations.js @@ -4,7 +4,8 @@ const repository = require('./repository'); const Relation = require('../entities/relation'); const BUILTIN_RELATIONS = [ - 'runOnNoteView' + 'runOnNoteView', + 'runOnNoteTitleChange' ]; async function getNotesWithRelation(name, targetNoteId) { @@ -36,8 +37,8 @@ async function createRelation(sourceNoteId, name, targetNoteId) { }).save(); } -async function getEffectiveRelations(noteId) { - return await repository.getEntities(` +async function getEffectiveRelations(noteId, relationName) { + const relations = await repository.getEntities(` WITH RECURSIVE tree(noteId) AS ( SELECT ? UNION @@ -48,6 +49,13 @@ async function getEffectiveRelations(noteId) { ) SELECT relations.* FROM relations JOIN tree ON relations.sourceNoteId = tree.noteId WHERE relations.isDeleted = 0 AND (relations.isInheritable = 1 OR relations.sourceNoteId = ?)`, [noteId, noteId]); + + if (relationName) { + return relations.filter(relation => relation.name === relationName); + } + else { + return relations; + } } module.exports = { diff --git a/src/services/script.js b/src/services/script.js index d2888a4c2..2a5939e92 100644 --- a/src/services/script.js +++ b/src/services/script.js @@ -4,14 +4,14 @@ const repository = require('./repository'); const cls = require('./cls'); const sourceIdService = require('./source_id'); -async function executeNote(note) { +async function executeNote(note, targetNote) { if (!note.isJavaScript()) { return; } const bundle = await getScriptBundle(note); - await executeBundle(bundle); + await executeBundle(bundle, note, targetNote); } async function executeBundle(bundle, startNote, targetNote = null) { diff --git a/src/services/script_context.js b/src/services/script_context.js index 68cef3a35..6ab8c258a 100644 --- a/src/services/script_context.js +++ b/src/services/script_context.js @@ -5,6 +5,7 @@ const utils = require('./utils'); const dateUtils = require('./date_utils'); const labelService = require('./labels'); const dateNoteService = require('./date_notes'); +const treeService = require('./tree'); const config = require('./config'); const repository = require('./repository'); const axios = require('axios'); @@ -61,6 +62,8 @@ function ScriptApi(startNote, currentNote, targetNote) { this.getRootCalendarNote = dateNoteService.getRootCalendarNote; this.getDateNote = dateNoteService.getDateNote; + this.sortNotesAlphabetically = treeService.sortNotesAlphabetically; + this.transactional = sql.transactional; }