| // 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/arraySet'); |
| 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; |
| }); |
| }); |
| } |