From 0469fe76d76163ae5d2385accb469ca1397e717d Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Wed, 13 Feb 2019 23:33:07 +0000 Subject: [PATCH] Desktop: Do not display tags that are not associated with any note --- CliClient/tests/models_Tag.js | 15 +++++++++++++++ ElectronClient/app/gui/NoteList.jsx | 2 +- ReactNativeClient/lib/BaseApplication.js | 12 ++++++++++++ ReactNativeClient/lib/models/Tag.js | 3 ++- 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CliClient/tests/models_Tag.js b/CliClient/tests/models_Tag.js index 214d154f20..cf136e631f 100644 --- a/CliClient/tests/models_Tag.js +++ b/CliClient/tests/models_Tag.js @@ -4,6 +4,7 @@ const { time } = require('lib/time-utils.js'); const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js'); const Folder = require('lib/models/Folder.js'); const Note = require('lib/models/Note.js'); +const NoteTag = require('lib/models/NoteTag.js'); const Tag = require('lib/models/Tag.js'); const BaseModel = require('lib/BaseModel.js'); const { shim } = require('lib/shim'); @@ -42,4 +43,18 @@ describe('models_Tag', function() { expect(hasThrown).toBe(true); })); + it('should not return tags without notes', asyncTest(async () => { + let folder1 = await Folder.save({ title: "folder1" }); + let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id }); + await Tag.setNoteTagsByTitles(note1.id, ['un']); + + let tags = await Tag.allWithNotes(); + expect(tags.length).toBe(1); + + await Note.delete(note1.id); + + tags = await Tag.allWithNotes(); + expect(tags.length).toBe(0); + })); + }); \ No newline at end of file diff --git a/ElectronClient/app/gui/NoteList.jsx b/ElectronClient/app/gui/NoteList.jsx index c5111f418a..654cc1e73d 100644 --- a/ElectronClient/app/gui/NoteList.jsx +++ b/ElectronClient/app/gui/NoteList.jsx @@ -312,7 +312,7 @@ class NoteListComponent extends React.Component { event.preventDefault(); } - if (noteIds.length && keyCode === 46) { // DELETE + if (noteIds.length && (keyCode === 46 || (keyCode === 8 && event.metaKey))) { // DELETE / CMD+Backspace event.preventDefault(); await NoteListUtils.confirmDeleteNotes(noteIds); } diff --git a/ReactNativeClient/lib/BaseApplication.js b/ReactNativeClient/lib/BaseApplication.js index 6f629a600f..40ce7f1aa2 100644 --- a/ReactNativeClient/lib/BaseApplication.js +++ b/ReactNativeClient/lib/BaseApplication.js @@ -299,6 +299,7 @@ class BaseApplication { const result = next(action); const newState = store.getState(); let refreshNotes = false; + let refreshTags = false; let refreshNotesUseSelectedNoteId = false; reduxSharedMiddleware(store, next, action); @@ -336,10 +337,21 @@ class BaseApplication { refreshNotes = true; } + if (action.type == 'NOTE_DELETE') { + refreshTags = true; + } + if (refreshNotes) { await this.refreshNotes(newState, refreshNotesUseSelectedNoteId); } + if (refreshTags) { + this.dispatch({ + type: 'TAG_UPDATE_ALL', + items: await Tag.allWithNotes(), + }); + } + if ((action.type == 'SETTING_UPDATE_ONE' && (action.key == 'dateFormat' || action.key == 'timeFormat')) || (action.type == 'SETTING_UPDATE_ALL')) { time.setDateFormat(Setting.value('dateFormat')); time.setTimeFormat(Setting.value('timeFormat')); diff --git a/ReactNativeClient/lib/models/Tag.js b/ReactNativeClient/lib/models/Tag.js index 4f05003147..07d08e9f88 100644 --- a/ReactNativeClient/lib/models/Tag.js +++ b/ReactNativeClient/lib/models/Tag.js @@ -91,7 +91,8 @@ class Tag extends BaseItem { } static async allWithNotes() { - return await Tag.modelSelectAll('SELECT * FROM tags WHERE id IN (SELECT DISTINCT tag_id FROM note_tags)'); + const tagIdSql = 'select distinct tags.id from tags left join note_tags nt on nt.tag_id = tags.id left join notes on notes.id = nt.note_id where notes.id IS NOT NULL'; + return await Tag.modelSelectAll('SELECT * FROM tags WHERE id IN (' + tagIdSql + ')'); } static async tagsByNoteId(noteId) {