You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-06-15 23:00:36 +02:00
All: Added support for hierarchical/nested tags (#2572)
The implementation uses / symbol as a nesting separator. I.e. tag/subtag is a nested tag, where tag is the parent tag and subtag is its child. Creating a tag named tag/subtag/subsubtag creates three tags, one for each level. The tags are associated using parent_id field. In the app, viewing notes with a tag will also show all notes that are associated with any of the tag's descendant tags (same for the note count). Deleting a tag will also delete all its descendant tags. In the desktop app the tags are shown nested just like the notebooks.
This commit is contained in:
@ -38,6 +38,7 @@ const defaultState = {
|
||||
customCss: '',
|
||||
templates: [],
|
||||
collapsedFolderIds: [],
|
||||
collapsedTagIds: [],
|
||||
clipperServer: {
|
||||
startState: 'idle',
|
||||
port: null,
|
||||
@ -172,20 +173,24 @@ function stateHasEncryptedItems(state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
function folderSetCollapsed(state, action) {
|
||||
const collapsedFolderIds = state.collapsedFolderIds.slice();
|
||||
const idx = collapsedFolderIds.indexOf(action.id);
|
||||
function itemSetCollapsed(state, action) {
|
||||
let collapsedItemsKey = null;
|
||||
if (action.type.indexOf('TAG_') !== -1) collapsedItemsKey = 'collapsedTagIds';
|
||||
else if (action.type.indexOf('FOLDER_') !== -1) collapsedItemsKey = 'collapsedFolderIds';
|
||||
|
||||
const collapsedItemIds = state[collapsedItemsKey].slice();
|
||||
const idx = collapsedItemIds.indexOf(action.id);
|
||||
|
||||
if (action.collapsed) {
|
||||
if (idx >= 0) return state;
|
||||
collapsedFolderIds.push(action.id);
|
||||
collapsedItemIds.push(action.id);
|
||||
} else {
|
||||
if (idx < 0) return state;
|
||||
collapsedFolderIds.splice(idx, 1);
|
||||
collapsedItemIds.splice(idx, 1);
|
||||
}
|
||||
|
||||
const newState = Object.assign({}, state);
|
||||
newState.collapsedFolderIds = collapsedFolderIds;
|
||||
newState[collapsedItemsKey] = collapsedItemIds;
|
||||
return newState;
|
||||
}
|
||||
|
||||
@ -768,14 +773,14 @@ const reducer = (state = defaultState, action) => {
|
||||
break;
|
||||
|
||||
case 'FOLDER_SET_COLLAPSED':
|
||||
newState = folderSetCollapsed(state, action);
|
||||
newState = itemSetCollapsed(state, action);
|
||||
break;
|
||||
|
||||
case 'FOLDER_TOGGLE':
|
||||
if (state.collapsedFolderIds.indexOf(action.id) >= 0) {
|
||||
newState = folderSetCollapsed(state, Object.assign({ collapsed: false }, action));
|
||||
newState = itemSetCollapsed(state, Object.assign({ collapsed: false }, action));
|
||||
} else {
|
||||
newState = folderSetCollapsed(state, Object.assign({ collapsed: true }, action));
|
||||
newState = itemSetCollapsed(state, Object.assign({ collapsed: true }, action));
|
||||
}
|
||||
break;
|
||||
|
||||
@ -784,6 +789,23 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.collapsedFolderIds = action.ids.slice();
|
||||
break;
|
||||
|
||||
case 'TAG_SET_COLLAPSED':
|
||||
newState = itemSetCollapsed(state, action);
|
||||
break;
|
||||
|
||||
case 'TAG_TOGGLE':
|
||||
if (state.collapsedTagIds.indexOf(action.id) >= 0) {
|
||||
newState = itemSetCollapsed(state, Object.assign({ collapsed: false }, action));
|
||||
} else {
|
||||
newState = itemSetCollapsed(state, Object.assign({ collapsed: true }, action));
|
||||
}
|
||||
break;
|
||||
|
||||
case 'TAG_SET_COLLAPSED_ALL':
|
||||
newState = Object.assign({}, state);
|
||||
newState.collapsedTagIds = action.ids.slice();
|
||||
break;
|
||||
|
||||
case 'TAG_UPDATE_ALL':
|
||||
newState = Object.assign({}, state);
|
||||
newState.tags = action.items;
|
||||
|
Reference in New Issue
Block a user