Commit 6585c54e authored by Robert Knight's avatar Robert Knight

Update groups service to use groups Redux module

Update the groups service to use the Redux store for storing and reading
groups state instead of storing it internally in private variables.
parent e61711b9
...@@ -20,12 +20,6 @@ var serviceConfig = require('../service-config'); ...@@ -20,12 +20,6 @@ var serviceConfig = require('../service-config');
// @ngInject // @ngInject
function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, session, function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, session,
settings) { settings) {
// The currently focused group. This is the group that's shown as selected in
// the groups dropdown, the annotations displayed are filtered to only ones
// that belong to this group, and any new annotations that the user creates
// will be created in this group.
var focusedGroupId;
var groups = [];
var documentUri; var documentUri;
var svc = serviceConfig(settings); var svc = serviceConfig(settings);
...@@ -69,33 +63,25 @@ function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, ses ...@@ -69,33 +63,25 @@ function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, ses
} }
return api.groups.list(params); return api.groups.list(params);
}).then(gs => { }).then(gs => {
$rootScope.$apply(() => { var isFirstLoad = store.allGroups().length === 0;
var focGroup = focused(); var prevFocusedGroup = localStorage.getItem(STORAGE_KEY);
if (focGroup) {
var focusedGroupInFetchedList = gs.some(g => g.id === focGroup.id); store.loadGroups(gs);
if (!focusedGroupInFetchedList) { if (isFirstLoad) {
focus(gs[0].id); store.focusGroup(prevFocusedGroup);
} }
}
groups = gs; return store.allGroups();
});
return gs;
}); });
} }
function all() { function all() {
return groups; return store.allGroups();
} }
// Return the full object for the group with the given id. // Return the full object for the group with the given id.
function get(id) { function get(id) {
var gs = all(); return store.getGroup(id);
for (var i = 0, max = gs.length; i < max; i++) {
if (gs[i].id === id) {
return gs[i];
}
}
return null;
} }
/** /**
...@@ -118,32 +104,26 @@ function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, ses ...@@ -118,32 +104,26 @@ function groups($rootScope, store, api, isSidebar, localStorage, serviceUrl, ses
* a previous session. Lastly, we fall back to the first group available. * a previous session. Lastly, we fall back to the first group available.
*/ */
function focused() { function focused() {
if (focusedGroupId) { return store.focusedGroup();
return get(focusedGroupId);
}
var fromStorage = get(localStorage.getItem(STORAGE_KEY));
if (fromStorage) {
focusedGroupId = fromStorage.id;
return fromStorage;
}
return all()[0];
} }
/** Set the group with the passed id as the currently focused group. */ /** Set the group with the passed id as the currently focused group. */
function focus(id) { function focus(id) {
var prevFocused = focused(); store.focusGroup(id);
var g = get(id);
if (g) {
focusedGroupId = g.id;
localStorage.setItem(STORAGE_KEY, g.id);
if (prevFocused.id !== g.id) {
$rootScope.$broadcast(events.GROUP_FOCUSED, g.id);
}
}
} }
// Persist the focused group to storage when it changes.
var prevFocused = store.focusedGroup();
store.subscribe(() => {
var focused = store.focusedGroup();
if (focused && focused !== prevFocused) {
localStorage.setItem(STORAGE_KEY, focused.id);
// Emit the `GROUP_FOCUSED` event for code that still relies on it.
$rootScope.$broadcast(events.GROUP_FOCUSED, focused.id);
}
});
// reset the focused group if the user leaves it // reset the focused group if the user leaves it
$rootScope.$on(events.GROUPS_CHANGED, function () { $rootScope.$on(events.GROUPS_CHANGED, function () {
// return for use in test // return for use in test
......
...@@ -12,6 +12,12 @@ var sessionWithThreeGroups = function() { ...@@ -12,6 +12,12 @@ var sessionWithThreeGroups = function() {
}; };
}; };
var dummyGroups = [
{ name: 'Group 1', id: 'id1'},
{ name: 'Group 2', id: 'id2'},
{ name: 'Group 3', id: 'id3'},
];
describe('groups', function() { describe('groups', function() {
var fakeStore; var fakeStore;
var fakeIsSidebar; var fakeIsSidebar;
...@@ -28,7 +34,18 @@ describe('groups', function() { ...@@ -28,7 +34,18 @@ describe('groups', function() {
fakeStore = fakeReduxStore({ fakeStore = fakeReduxStore({
searchUris: ['http://example.org'], searchUris: ['http://example.org'],
focusedGroup: null,
groups: [],
},{ },{
focusGroup: sinon.stub(),
getGroup: sinon.stub(),
loadGroups: sinon.stub(),
allGroups() {
return this.getState().groups;
},
focusedGroup() {
return this.getState().focusedGroup;
},
searchUris() { searchUris() {
return this.getState().searchUris; return this.getState().searchUris;
}, },
...@@ -61,11 +78,7 @@ describe('groups', function() { ...@@ -61,11 +78,7 @@ describe('groups', function() {
}, },
}, },
groups: { groups: {
list: sandbox.stub().returns(Promise.resolve([ list: sandbox.stub().returns(Promise.resolve(dummyGroups)),
{name: 'Group 1', id: 'id1'},
{name: 'Group 2', id: 'id2'},
{name: 'Group 3', id: 'id3'},
])),
}, },
}; };
fakeServiceUrl = sandbox.stub(); fakeServiceUrl = sandbox.stub();
...@@ -81,36 +94,20 @@ describe('groups', function() { ...@@ -81,36 +94,20 @@ describe('groups', function() {
fakeSession, fakeSettings); fakeSession, fakeSettings);
} }
describe('#all()', function() { describe('#all', function() {
it('returns no groups if there are none in the session', function() { it('returns all groups', function() {
fakeSession = {state: {}};
var groups = service().all();
assert.equal(groups.length, 0);
});
it('returns the groups when there are some', function() {
var svc = service(); var svc = service();
fakeStore.setState({ groups: dummyGroups });
return svc.load().then(() => { assert.deepEqual(svc.all(), dummyGroups);
var groups = svc.all();
assert.equal(groups.length, 3);
assert.deepEqual(groups, [
{name: 'Group 1', id: 'id1'},
{name: 'Group 2', id: 'id2'},
{name: 'Group 3', id: 'id3'},
]);
});
}); });
}); });
describe('#load() method', function() { describe('#load', function() {
it('loads all available groups', function() { it('loads all available groups', function() {
var svc = service(); var svc = service();
return svc.load().then(() => { return svc.load().then(() => {
assert.equal(svc.all().length, 3); assert.calledWith(fakeStore.loadGroups, dummyGroups);
}); });
}); });
...@@ -123,19 +120,11 @@ describe('groups', function() { ...@@ -123,19 +120,11 @@ describe('groups', function() {
}); });
}); });
it('focuses on the first in the list of groups if user leaves the focused group', function () { it('sets the focused group from the value saved in local storage', () => {
var svc = service(); var svc = service();
fakeLocalStorage.getItem.returns(dummyGroups[1].id);
return svc.load().then(() => { return svc.load().then(() => {
svc.focus('id2'); assert.calledWith(fakeStore.focusGroup, dummyGroups[1].id);
}).then(() => {
fakeApi.groups.list = sandbox.stub().returns(Promise.resolve([
{name: 'Group 3', id: 'id3'},
{name: 'Group 1', id: 'id1'},
]));
return svc.load();
}).then(() => {
assert.equal(svc.focused().id, 'id3');
}); });
}); });
...@@ -181,106 +170,50 @@ describe('groups', function() { ...@@ -181,106 +170,50 @@ describe('groups', function() {
}); });
}); });
describe('#get() method', function() { describe('#get', function() {
it('returns the requested group', function() { it('returns the requested group', function() {
var svc = service(); var svc = service();
fakeStore.getGroup.withArgs('foo').returns(dummyGroups[1]);
return svc.load().then(() => { assert.equal(svc.get('foo'), dummyGroups[1]);
var group = svc.get('id2');
assert.equal(group.id, 'id2');
});
});
it("returns null if the group doesn't exist", function() {
var svc = service();
return svc.load().then(() => {
var group = svc.get('foobar');
assert.isNull(group);
});
}); });
}); });
describe('#focused() method', function() { describe('#focused', function() {
it('returns the focused group', function() { it('returns the focused group', function() {
var svc = service(); var svc = service();
fakeStore.setState({ groups: dummyGroups, focusedGroup: dummyGroups[2] });
return svc.load().then(() => { assert.equal(svc.focused(), dummyGroups[2]);
svc.focus('id2');
assert.equal(svc.focused().id, 'id2');
});
});
it('returns the first group initially', function() {
var svc = service();
return svc.load().then(() => {
assert.equal(svc.focused().id, 'id1');
});
});
it('returns the group selected in localStorage if available', function() {
fakeLocalStorage.getItem.returns('id3');
var svc = service();
return svc.load().then(() => {
assert.equal(svc.focused().id, 'id3');
});
}); });
}); });
describe('#focus()', function() { describe('#focus', function() {
it('sets the focused group to the named group', function() { it('sets the focused group to the named group', function() {
var svc = service(); var svc = service();
svc.focus('foo');
return svc.load().then(() => { assert.calledWith(fakeStore.focusGroup, 'foo');
svc.focus('id2');
assert.equal(svc.focused().id, 'id2');
});
});
it('does nothing if the named group isn\'t recognised', function() {
var svc = service();
return svc.load().then(() => {
svc.focus('foobar');
assert.equal(svc.focused().id, 'id1');
});
}); });
});
context('when the focused group changes', () => {
it('stores the focused group id in localStorage', function() { it('stores the focused group id in localStorage', function() {
var svc = service(); service();
return svc.load().then(() => { fakeStore.setState({ groups: dummyGroups, focusedGroup: dummyGroups[1] });
svc.focus('id3');
assert.calledWithMatch(fakeLocalStorage.setItem, sinon.match.any, 'id3'); assert.calledWithMatch(fakeLocalStorage.setItem, sinon.match.any, dummyGroups[1].id);
});
}); });
it('emits the GROUP_FOCUSED event if the focused group changed', function () { it('emits the GROUP_FOCUSED event if the focused group changed', function () {
var svc = service(); service();
return svc.load().then(() => { fakeStore.setState({ groups: dummyGroups, focusedGroup: dummyGroups[1] });
svc.focus('id3');
assert.calledWith(fakeRootScope.$broadcast, events.GROUP_FOCUSED, 'id3');
});
});
it('does not emit GROUP_FOCUSED if the focused group did not change', function () { assert.calledWith(fakeRootScope.$broadcast, events.GROUP_FOCUSED, dummyGroups[1].id);
var svc = service();
return svc.load().then(() => {
svc.focus('id3');
fakeRootScope.$broadcast = sinon.stub();
svc.focus('id3');
assert.notCalled(fakeRootScope.$broadcast);
});
}); });
}); });
describe('#leave()', function () { describe('#leave', function () {
it('should call the group leave API', function () { it('should call the group leave API', function () {
var s = service(); var s = service();
return s.leave('id2').then(() => { return s.leave('id2').then(() => {
......
...@@ -16,7 +16,7 @@ var redux = require('redux'); ...@@ -16,7 +16,7 @@ var redux = require('redux');
function fakeStore(initialState, methods) { function fakeStore(initialState, methods) {
function update(state, action) { function update(state, action) {
if (action.state) { if (action.state) {
return action.state; return Object.assign({}, state, action.state);
} else { } else {
return state; return state;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment