1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-18 09:35:20 +02:00
joplin/ReactNativeClient/lib/components/shared/side-menu-shared.js
Vaidotas Šimkus e11e57f1d8
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.
2020-07-12 18:09:07 +01:00

123 lines
3.4 KiB
JavaScript

const BaseItem = require('lib/models/BaseItem');
const BaseModel = require('lib/BaseModel');
const shared = {};
function itemHasChildren_(items, itemId) {
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.parent_id === itemId) return true;
}
return false;
}
function itemIsVisible(items, itemId, collapsedItemIds) {
if (!collapsedItemIds || !collapsedItemIds.length) return true;
while (true) {
const item = BaseModel.byId(items, itemId);
if (!item) throw new Error(`No item with id ${itemId}`);
if (!item.parent_id) return true;
if (collapsedItemIds.indexOf(item.parent_id) >= 0) return false;
itemId = item.parent_id;
}
}
function renderItemsRecursive_(props, renderItem, items, parentId, depth, order, itemType) {
let itemsKey = '';
let notesParentType = '';
let collapsedItemsKey = '';
let selectedItemKey = '';
if (itemType === BaseModel.TYPE_FOLDER) {
itemsKey = 'folders';
notesParentType = 'Folder';
collapsedItemsKey = 'collapsedFolderIds';
selectedItemKey = 'selectedFolderId';
} else if (itemType === BaseModel.TYPE_TAG) {
itemsKey = 'tags';
notesParentType = 'Tag';
collapsedItemsKey = 'collapsedTagIds';
selectedItemKey = 'selectedTagId';
}
const propItems = props[itemsKey];
for (let i = 0; i < propItems.length; i++) {
const item = propItems[i];
if (!BaseItem.getClassByItemType(itemType).idsEqual(item.parent_id, parentId)) continue;
if (!itemIsVisible(props[itemsKey], item.id, props[collapsedItemsKey])) continue;
const hasChildren = itemHasChildren_(propItems, item.id);
order.push(item.id);
items.push(renderItem(item, props[selectedItemKey] == item.id && props.notesParentType == notesParentType, hasChildren, depth));
if (hasChildren) {
const result = renderItemsRecursive_(props, renderItem, items, item.id, depth + 1, order, itemType);
items = result.items;
order = result.order;
}
}
return {
items: items,
order: order,
};
}
shared.renderFolders = function(props, renderItem) {
return renderItemsRecursive_(props, renderItem, [], '', 0, [], BaseModel.TYPE_FOLDER);
};
shared.renderTags = function(props, renderItem) {
return renderItemsRecursive_(props, renderItem, [], '', 0, [], BaseModel.TYPE_TAG);
};
// shared.renderSearches = function(props, renderItem) {
// let searches = props.searches.slice();
// let searchItems = [];
// const order = [];
// for (let i = 0; i < searches.length; i++) {
// const search = searches[i];
// order.push(search.id);
// searchItems.push(renderItem(search, props.selectedSearchId == search.id && props.notesParentType == 'Search'));
// }
// return {
// items: searchItems,
// order: order,
// };
// }
shared.synchronize_press = async function(comp) {
const { reg } = require('lib/registry.js');
const action = comp.props.syncStarted ? 'cancel' : 'start';
if (!(await reg.syncTarget().isAuthenticated())) {
if (reg.syncTarget().authRouteName()) {
comp.props.dispatch({
type: 'NAV_GO',
routeName: reg.syncTarget().authRouteName(),
});
return 'auth';
}
reg.logger().info('Not authentified with sync target - please check your credential.');
return 'error';
}
let sync = null;
try {
sync = await reg.syncTarget().synchronizer();
} catch (error) {
reg.logger().info('Could not acquire synchroniser:');
reg.logger().info(error);
return 'error';
}
if (action == 'cancel') {
sync.cancel();
return 'cancel';
} else {
reg.scheduleSync(0);
return 'sync';
}
};
module.exports = shared;