blob: 822a364707ece5d8ba7a67e8e4e0058021c2354b [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
var mercury = require('mercury');
var _ = require('lodash');
var EventEmitter = require('events').EventEmitter;
var arraySet = require('../../lib/array-set');
var store = require('../../lib/store');
var freeze = require('../../lib/mercury/freeze');
var sortedPush = require('../../lib/mercury/sorted-push-array');
var namespaceService = require('../namespace/service');
var namespaceItem = require('../namespace/item');
var log = require('../../lib/log')('services:bookmarks:service');
module.exports = {
getAll: getAll,
bookmark: bookmark,
isBookmarked: isBookmarked
};
// Data is loaded from and saved to this key in the store.
var USER_BOOKMARKS_KEY = 'bookmarks-store-key';
// Singleton state for all the bookmarks.
var bookmarksObs = mercury.array([]);
/*
* Gets all the namespace items that are bookmarked
* As new bookmarks become available/removed the observable array will change
* to reflect the changes.
*
* The observable result has an events property which is an EventEmitter
* and emits 'end', 'itemError' events.
*
* @return {Promise.<mercury.array>} Promise of an observable array
* of bookmark items
*/
function getAll() {
// Empty out the array
bookmarksObs.splice(0, bookmarksObs.getLength());
var immutableBookmarksObs = freeze(bookmarksObs);
immutableBookmarksObs.events = new EventEmitter();
return loadKeys().then(getBookmarkItems);
function getBookmarkItems(names) {
var allItems = names.map(function(name) {
return addNamespaceItem(name).catch(function(err) {
immutableBookmarksObs.events.emit('itemError', {
name: name,
error: err
});
log.warn('Failed to create item for "' + name + '"', err);
});
});
Promise.all(allItems).then(function() {
immutableBookmarksObs.events.emit('end');
}).catch(function() {
immutableBookmarksObs.events.emit('end');
});
return immutableBookmarksObs;
}
}
/*
* Gets the namespace items for a name and adds it to the observable array
*/
function addNamespaceItem(name) {
return namespaceService.getNamespaceItem(name)
.then(function(item) {
var sorter = 'objectName';
sortedPush(bookmarksObs, item, sorter);
}).catch(function(err) {
// Add the object, anyway. It will show as inaccessible.
sortedPush(bookmarksObs, namespaceItem.createItem({
objectName: name
}), 'objectName');
throw err;
});
}
/*
* Whether a specific name is bookmarked or not
* @return {Promise.<boolean>} Promise indicating a name is bookmarked
*/
function isBookmarked(name) {
return loadKeys().then(function(keys) {
return (keys && keys.indexOf(name) >= 0);
});
}
/*
* Bookmarks/Unbookmarks a name.
* @return {Promise.<void>} Promise indicating whether operation succeeded.
*/
function bookmark(name, isBookmarked) {
if (isBookmarked) {
// new bookmark, add it to the state
addNamespaceItem(name).catch(function(err) {
log.warn('Bookmarking inaccessible item "' + name + '"', err);
});
} else {
// remove bookmark
arraySet.set(bookmarksObs, null, false, indexOf.bind(null, name));
}
// update store
return loadKeys().then(function(keys) {
keys = keys || []; // Initialize the bookmarks, if none were loaded.
arraySet.set(keys, name, isBookmarked);
return store.setValue(USER_BOOKMARKS_KEY, keys);
});
}
/*
* Check the observe array for the index of the given item. -1 if not present.
*/
function indexOf(name) {
return _.findIndex(bookmarksObs(), function(bookmark) {
// Since bookmarks can be assigned out of order, check for undefined.
return bookmark !== undefined && name === bookmark.objectName;
});
}
/*
* Loads all the bookmarked names from the store
*/
function loadKeys() {
return store.getValue(USER_BOOKMARKS_KEY).then(function(keys) {
keys = keys || [];
return keys.filter(function(key, index, self) {
// only return unique and existing values
return key !== null &&
key !== undefined &&
self.indexOf(key) === index;
});
});
}