mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-18 09:35:20 +02:00
e11e57f1d8
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.
123 lines
3.4 KiB
JavaScript
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;
|