Files
habitica/public/js/services/userServices.js
Tyler Renelle f99b496a29 [#1444] add filters back to use schema. Allow editing of top-level tags
object in PUT /user, WARNING! We want to get away from that ASAP and
instead have a nested resource for managing tags. Set default task tags
if not present, will revisit after tasks are a subdoc too
2013-09-08 23:28:34 -04:00

213 lines
8.4 KiB
JavaScript

'use strict';
/**
* Services that persists and retrieves user from localStorage.
*/
angular.module('userServices', []).
factory('User', ['$http', '$location', 'API_URL', 'STORAGE_USER_ID', 'STORAGE_SETTINGS_ID',
function($http, $location, API_URL, STORAGE_USER_ID, STORAGE_SETTINGS_ID) {
var authenticated = false,
defaultSettings = {
auth: { apiId: '', apiToken: ''},
sync: {
queue: [], //here OT will be queued up, this is NOT call-back queue!
sent: [] //here will be OT which have been sent, but we have not got reply from server yet.
},
fetching: false, // whether fetch() was called or no. this is to avoid race conditions
online: false
},
settings = {}, //habit mobile settings (like auth etc.) to be stored here
user = {}; // this is stored as a reference accessible to all controllers, that way updates propagate
//first we populate user with schema
_.extend(user, window.habitrpgShared.helpers.newUser());
user.apiToken = user._id = ''; // we use id / apitoken to determine if registered
//than we try to load localStorage
if (localStorage.getItem(STORAGE_USER_ID)) {
_.extend(user, JSON.parse(localStorage.getItem(STORAGE_USER_ID)));
}
var syncQueue = function (cb) {
if (!authenticated) {
alert("Not authenticated, can't sync, go to settings first.");
return;
}
var queue = settings.sync.queue;
var sent = settings.sync.sent;
if (queue.length === 0) {
console.log('Sync: Queue is empty');
return;
}
if (settings.fetching) {
console.log('Sync: Already fetching');
return;
}
if (settings.online!==true) {
console.log('Sync: Not online');
return;
}
settings.fetching = true;
// move all actions from queue array to sent array
_.times(queue.length, function () {
sent.push(queue.shift());
});
$http.post(API_URL + '/api/v1/user/batch-update', sent, {params: {data:+new Date, _v:user._v}})
.success(function (data, status, heacreatingders, config) {
data.tasks = _.toArray(data.tasks);
//make sure there are no pending actions to sync. If there are any it is not safe to apply model from server as we may overwrite user data.
if (!queue.length) {
//we can't do user=data as it will not update user references in all other angular controllers.
// the user has been modified from another application, sync up
if(data.wasModified) {
delete data.wasModified;
_.extend(user, data);
}
user._v = data._v;
// FIXME handle this somewhere else, we don't need to check every single time
var offset = moment().zone(); // eg, 240 - this will be converted on server as -(offset/60)
if (!user.preferences.timezoneOffset || user.preferences.timezoneOffset !== offset) {
userServices.log({op:'set', data: {'preferences.timezoneOffset': offset}});
}
}
sent.length = 0;
settings.fetching = false;
save();
if (cb) {
cb(false)
}
syncQueue(); // call syncQueue to check if anyone pushed more actions to the queue while we were talking to server.
})
.error(function (data, status, headers, config) {
//move sent actions back to queue
_.times(sent.length, function () {
queue.push(sent.shift())
});
settings.fetching = false;
//Notification.push({type:'text', text:"We're offline"})
});
}
var save = function () {
localStorage.setItem(STORAGE_USER_ID, JSON.stringify(user));
localStorage.setItem(STORAGE_SETTINGS_ID, JSON.stringify(settings));
};
var userServices = {
user: user,
online: function (status) {
if (status===true) {
settings.online = true;
syncQueue();
} else {
settings.online = false;
};
},
authenticate: function (uuid, token, cb) {
if (!!uuid && !!token) {
$http.defaults.headers.common['x-api-user'] = uuid;
$http.defaults.headers.common['x-api-key'] = token;
authenticated = true;
settings.auth.apiId = uuid;
settings.auth.apiToken = token;
settings.online = true;
if (user && user._v) user._v--; // shortcut to always fetch new updates on page reload
this.log({}, cb);
} else {
alert('Please enter your ID and Token in settings.')
}
},
authenticated: function(){
return this.settings.auth.apiId !== "";
},
log: function (action, cb) {
//push by one buy one if an array passed in.
if (_.isArray(action)) {
action.forEach(function (a) {
settings.sync.queue.push(a);
});
} else {
settings.sync.queue.push(action);
}
save();
syncQueue(cb);
},
/*
Very simple path-set. `set('preferences.gender','m')` for example. We'll deprecate this once we have a complete API
*/
set: function(k, v) {
var self = userServices;
var log = { op: 'set', data: {} };
window.habitrpgShared.helpers.dotSet(k, v, userServices.user);
log.data[k] = v;
userServices.log(log);
},
setMultiple: function(obj){
var self = this;
var log = { op: 'set', data: {} };
_.each(obj, function(v,k){
window.habitrpgShared.helpers.dotSet(k, v, userServices.user);
log.data[k] = v;
})
userServices.log(log);
},
save: save,
settings: settings
};
//load settings if we have them
if (localStorage.getItem(STORAGE_SETTINGS_ID)) {
//use extend here to make sure we keep object reference in other angular controllers
_.extend(settings, JSON.parse(localStorage.getItem(STORAGE_SETTINGS_ID)));
//if settings were saved while fetch was in process reset the flag.
settings.fetching = false;
//create and load if not
} else {
localStorage.setItem(STORAGE_SETTINGS_ID, JSON.stringify(defaultSettings));
_.extend(settings, defaultSettings);
}
//If user does not have ApiID that forward him to settings.
if (!settings.auth.apiId || !settings.auth.apiToken) {
//var search = $location.search(); // FIXME this should be working, but it's returning an empty object when at a root url /?_id=...
var search = $location.search(window.location.search.substring(1)).$$search; // so we use this fugly hack instead
if (search.err) return alert(search.err);
if (search._id && search.apiToken) {
userServices.authenticate(search._id, search.apiToken, function(){
window.location.href='/';
});
} else {
if (window.location.pathname.indexOf('/static') !== 0){
localStorage.clear();
window.location.href = '/logout';
}
}
} else {
userServices.authenticate(settings.auth.apiId, settings.auth.apiToken)
}
return userServices;
}
]);