Skip to content

Commit

Permalink
feat(bookmarks) Added bookmark APIs
Browse files Browse the repository at this point in the history
- Added a new service (with basic unit tests) that wraps ngResoruce. Intended to replace all access to to ngResource.
- Add Bookmark service. Configured so it automatically downloads application bookmarks for user on load.
- Enhanced UserProfile service so that it utilises a promise chain that is able to be hooked into from other services (like Bookmark)
  • Loading branch information
atruskie committed Jul 5, 2014
1 parent fda2435 commit c1598ec
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 133 deletions.
1 change: 1 addition & 0 deletions src/app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ var app = angular.module('baw',
'templates-app', /* these are the precompiled templates */
'templates-common',

'bawApp.services.resource', // a custom wrapped around ngResource
'bawApp.directives', /* our directives.js */
'bawApp.directives.ngAudio', /* our directives.js */
'bawApp.filters', /* our filters.js */
Expand Down
6 changes: 4 additions & 2 deletions src/app/listen/listen.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead'])
'Site',
'Project',
'UserProfile',
'UserProfileEvents',
'Bookmark',
/**
* The listen controller.
* @param $scope
Expand All @@ -42,7 +44,7 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead'])
*/
function ListenCtrl(
$scope, $resource, $location, $routeParams, $route, $q, paths, constants, $url, ngAudioEvents,
AudioRecording, Media, AudioEvent, Tag, Taggings, Site, Project, UserProfile) {
AudioRecording, Media, AudioEvent, Tag, Taggings, Site, Project, UserProfile, UserProfileEvents, Bookmark) {

var CHUNK_DURATION_SECONDS = constants.listen.chunkDurationSeconds;

Expand Down Expand Up @@ -96,7 +98,7 @@ angular.module('bawApp.listen', ['decipher.tags', 'ui.bootstrap.typeahead'])
$scope.model.audioElement.volume = UserProfile.profile.preferences.volume;
$scope.model.audioElement.muted = UserProfile.profile.preferences.muted;
};
$scope.$on(UserProfile.eventKeys.loaded, profileLoaded);
$scope.$on(UserProfileEvents.loaded, profileLoaded);
if (UserProfile.profile && UserProfile.profile.preferences) {
profileLoaded(null, UserProfile);
}
Expand Down
102 changes: 55 additions & 47 deletions src/baw.configuration.tpl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,10 @@ angular.module('bawApp.configuration', ['url'])
* This module contains static paths that are stored centrally for easy configuration.
* App dependent.
*
* The root properties changed when the app is built with grunt.
* The root properties are changed when the app is built with grunt.
*/

.constant("conf.paths", (function () {
/**
* Joins path fragments together.
* @param {...[string]} fragments
* @returns {*}
*/
function joinPathFragments(fragments) {
fragments = Array.prototype.slice.call(arguments, 0);

if (fragments.length === 0) {
return undefined;
}
else if (fragments.length === 1) {
return fragments[0];
}
else {
var path = fragments[0];

if (path.slice(-1) === "/") {
path = path.slice(0, -1);
}

for (var i = 1; i < fragments.length; i++) {
var f = fragments[i];

if ((typeof f) !== "string") {
throw "joinPathFragments: Path fragment " + f + " is not a string";
}

var hasFirst = f[0] === "/";
var hasLast = (f.slice(-1))[0] === "/";

if (!hasFirst) {
f = "/" + f;
}

if (hasLast && i !== (fragments.length - 1)) {
f = f.slice(0, -1);
}

path += f;
}

return path;
}
}

var paths = {
api: {
Expand Down Expand Up @@ -92,6 +47,9 @@ angular.module('bawApp.configuration', ['url'])
user: {
profile: "/my_account",
settings: "/my_account/prefs"
},
bookmark: {
show: "user_accounts/{userId}/bookmarks/{bookmarkId}"
}
},
links: {
Expand Down Expand Up @@ -139,6 +97,52 @@ angular.module('bawApp.configuration', ['url'])
};


/**
* Joins path fragments together.
* @param {...[string]} fragments
* @returns {*}
*/
function joinPathFragments(fragments) {
fragments = Array.prototype.slice.call(arguments, 0);

if (fragments.length === 0) {
return undefined;
}
else if (fragments.length === 1) {
return fragments[0];
}
else {
var path = fragments[0];

if (path.slice(-1) === "/") {
path = path.slice(0, -1);
}

for (var i = 1; i < fragments.length; i++) {
var f = fragments[i];

if ((typeof f) !== "string") {
throw "joinPathFragments: Path fragment " + f + " is not a string";
}

var hasFirst = f[0] === "/";
var hasLast = (f.slice(-1))[0] === "/";

if (!hasFirst) {
f = "/" + f;
}

if (hasLast && i !== (fragments.length - 1)) {
f = f.slice(0, -1);
}

path += f;
}

return path;
}
}

// add helper paths
function recursivePath(source, root) {
for (var key in source) {
Expand All @@ -164,7 +168,7 @@ angular.module('bawApp.configuration', ['url'])

return paths;
})()
)
)
.constant("conf.constants", {
listen: {
chunkDurationSeconds: 30.0,
Expand All @@ -186,5 +190,9 @@ angular.module('bawApp.configuration', ['url'])
},
annotationLibrary: {
paddingSeconds: 1.0
},
bookmark: {
lastPlaybackPositionName: "Last playback position",
appCategory: "<<application>>"
}
});
36 changes: 36 additions & 0 deletions src/components/services/bawResource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
angular.module("bawApp.services.resource", ["ngResource"])
.factory("bawResource", ["$resource", function ($resource) {

/**
*
* @param uri
* @returns {*}
*/
function uriConvert(uri) {
// find all place holders in this form: '{identifier}'
// replace with placeholder in this form: ':identifier'
return uri.replace(/(\{([^{}]*)\})/g, ":$2");
}

/**
* @name bawResource
* Helper method for adding a put request onto the standard angular resource service
* @param {string} path - the web server path
* @param {Object} paramDefaults - the default parameters
* @param {Object} [actions] - a set of actions to also add (extend)
* @return {*}
*/
var bawResource = function resourcePut(path, paramDefaults, actions) {
path = uriConvert(path);

var a = actions || {};
a.update = a.update || { method: 'PUT' };
var resource = $resource(path, paramDefaults, a);

resource.modifiedPath = path;

return resource;
};

return bawResource;
}]);
24 changes: 24 additions & 0 deletions src/components/services/bawResource.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
describe("The bawResource service", function () {

var Bookmark;

beforeEach(module('bawApp.services'));

beforeEach(inject(["Bookmark", function (providedBookmark) {
Bookmark = providedBookmark;
}]));


it("should return a resource constructor that includes update/put", function () {

expect(Bookmark).toImplement({
"get": null,
"save": null,
"query": null,
"remove": null,
"delete": null,
"update": null,
"modifiedPath": null
});
});
});
79 changes: 79 additions & 0 deletions src/components/services/bookmark.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
var bawss = bawss || angular.module("bawApp.services", ['bawApp.services.resource', 'bawApp.configuration']);


bawss.factory('Bookmark', [
'bawResource',
'conf.paths',
'conf.constants',
'UserProfile',
'$q',
function (bawResource, paths, constants, UserProfile, $q) {
var bc = constants.bookmark;

// valid query options: category
// required parameters: userId
// optional parameters: bookmarkId

// at the moment we only support bookmark modification for users (not for recordings)
var resource = bawResource(paths.api.routes.bookmark.showAbsolute, {});

// retrieve or set the playback bookmark
resource.applicationBookmarks = {};
function getApplicationBookmarks(userProfile) {
console.info("User profile hook success, retrieving app bookmarks", arguments);
var deferred = $q.defer();

resource.query({
category: bc.appCategory,
userId: userProfile.id
},
function appBookmarksQuerySuccess(values, headers) {
console.info("Application bookmarks received", values);

// transform into associative hash
values.forEach(function (value, index) {
resource.applicationBookmarks[value.name] = value;
});

deferred.resolve(values);
},
function appBookmarksQueryFailure() {
console.error("Retrieving application bookmarks failed");

deferred.reject();
});

return deferred.promise;
}

resource.applicationBookmarksPromise = UserProfile.get.then(
getApplicationBookmarks,
function () {
console.error("user profile hook failure", arguments);
});


resource.savePlaybackPosition = function savePlaybackPosition(recordingId, offset) {
var bookmark = resource.applicationBookmarks[bc.lastPlaybackPositionName];
if (bookmark) {
// update
bookmark.offsetSeconds = offset;
bookmark.audioRecordingId = recordingId;

}
else {
// create
bookmark = {
name: bc.lastPlaybackPositionName,
category: bc.appCategory,
offsetSeconds: offset,
audioRecordingId: recordingId
};


}
};


return resource;
}]);
20 changes: 20 additions & 0 deletions src/components/services/bookmark.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
describe("The bookmark service", function () {

var bawResource;

beforeEach(module('bawApp.services'));

beforeEach(inject(["Bookmark", function (providedBawResource) {
bawResource = providedBawResource;
}]));


it("will return a promise for retrieving application bookmarks", function() {

expect(bawResource.applicationBookmarksPromise).toImplement({
catch: null,
finally: null,
then: null
});
});
});
2 changes: 1 addition & 1 deletion src/components/services/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
return uri.replace(/(\{([^{}]*)\})/g, ":$2");
}

var bawss = angular.module("bawApp.services", ['ngResource', 'bawApp.configuration']);
var bawss = bawss || angular.module("bawApp.services", ['ngResource', 'bawApp.configuration']);

bawss.factory('Project', [ '$resource', 'conf.paths', function ($resource, paths) {
return resourcePut($resource, uriConvert(paths.api.routes.projectAbsolute), {projectId: "@projectId"});
Expand Down
Loading

0 comments on commit c1598ec

Please sign in to comment.