diff --git a/common/locales/en/front.json b/common/locales/en/front.json index 7982944940..8207a9dc7f 100644 --- a/common/locales/en/front.json +++ b/common/locales/en/front.json @@ -179,5 +179,8 @@ "businessInquiries": "Business Inquiries", "merchandiseInquiries": "Merchandise Inquiries", "marketingInquiries": "Marketing/Social Media Inquiries", - "tweet": "Tweet" + "tweet": "Tweet", + "apps": "Apps", + "notifyAndroidApp": "Want us to notify you when the Android app is ready? Sign up for this mailing list!", + "checkOutIOSApp": "Check out our new iOS App!" } diff --git a/test/api/v2/register/POST-register.test.js b/test/api/v2/register/POST-register.test.js index 19c9b3e341..e40963830e 100644 --- a/test/api/v2/register/POST-register.test.js +++ b/test/api/v2/register/POST-register.test.js @@ -3,7 +3,8 @@ import { requester, translate as t, } from '../../../helpers/api-integration.helper'; -import {v4 as generateRandomUserName} from 'uuid'; +import { v4 as generateRandomUserName } from 'uuid'; +import { each } from 'lodash'; describe('POST /register', () => { @@ -137,4 +138,106 @@ describe('POST /register', () => { }); }); }); + + context('successful login via api', () => { + let api, username, email, password; + + beforeEach(() => { + api = requester(); + username = generateRandomUserName(); + email = `${username}@example.com`; + password = 'password'; + }); + + it('sets all site tour values to -2 (already seen)', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.flags.tour).to.not.be.empty; + + each(user.flags.tour, (value, attribute) => { + expect(value).to.eql(-2); + }); + }); + }); + + it('populates user with default todos, not no other task types', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.todos).to.not.be.empty; + expect(user.dailys).to.be.empty; + expect(user.habits).to.be.empty; + expect(user.rewards).to.be.empty; + }); + }); + + it('populates user with default tags', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.tags).to.not.be.empty; + }); + }); + }); + + context('successful login with habitica-web header', () => { + let api, username, email, password; + + beforeEach(() => { + api = requester({}, {'x-client': 'habitica-web'}); + username = generateRandomUserName(); + email = `${username}@example.com`; + password = 'password'; + }); + + it('sets all common tutorial flags to true', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.flags.tour).to.not.be.empty; + + each(user.flags.tutorial.common, (value, attribute) => { + expect(value).to.eql(true); + }); + }); + }); + + it('populates user with default todos, habits, and rewards', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.todos).to.not.be.empty; + expect(user.dailys).to.be.empty; + expect(user.habits).to.not.be.empty; + expect(user.rewards).to.not.be.empty; + }); + }); + + it('populates user with default tags', () => { + return api.post('/register', { + username: username, + email: email, + password: password, + confirmPassword: password, + }).then((user) => { + expect(user.tags).to.not.be.empty; + }); + }); + }); }); diff --git a/test/api/v2/user/tasks/GET-tasks.test.js b/test/api/v2/user/tasks/GET-tasks.test.js index 6c70bdd554..89492b717b 100644 --- a/test/api/v2/user/tasks/GET-tasks.test.js +++ b/test/api/v2/user/tasks/GET-tasks.test.js @@ -8,7 +8,14 @@ describe('GET /user/tasks/', () => { let api, user; beforeEach(() => { - return generateUser().then((_user) => { + return generateUser({ + dailys: [ + {text: 'daily', type: 'daily'}, + {text: 'daily', type: 'daily'}, + {text: 'daily', type: 'daily'}, + {text: 'daily', type: 'daily'}, + ], + }).then((_user) => { user = _user; api = requester(user); }); @@ -17,7 +24,7 @@ describe('GET /user/tasks/', () => { it('gets all tasks', () => { return api.get(`/user/tasks/`).then((tasks) => { expect(tasks).to.be.an('array'); - expect(tasks.length).to.be.greaterThan(4); + expect(tasks.length).to.be.greaterThan(3); let task = tasks[0]; expect(task.id).to.exist; diff --git a/test/helpers/api-integration.helper.js b/test/helpers/api-integration.helper.js index f34c0d1384..b2b0ac99c6 100644 --- a/test/helpers/api-integration.helper.js +++ b/test/helpers/api-integration.helper.js @@ -16,12 +16,12 @@ const API_TEST_SERVER_PORT = 3003; // Sets up an abject that can make all REST requests // If a user is passed in, the uuid and api token of // the user are used to make the requests -export function requester(user={}) { +export function requester(user={}, additionalSets) { return { - get: _requestMaker(user, 'get'), - post: _requestMaker(user, 'post'), - put: _requestMaker(user, 'put'), - del: _requestMaker(user, 'del'), + get: _requestMaker(user, 'get', additionalSets), + post: _requestMaker(user, 'post', additionalSets), + put: _requestMaker(user, 'put', additionalSets), + del: _requestMaker(user, 'del', additionalSets), } }; @@ -207,18 +207,22 @@ export function resetHabiticaDB() { }); } -function _requestMaker(user, method) { +function _requestMaker(user, method, additionalSets) { return (route, send, query) => { return new Promise((resolve, reject) => { let request = superagent[method](`http://localhost:${API_TEST_SERVER_PORT}/api/v2${route}`) .accept('application/json'); - if (user._id && user.apiToken) { + if (user && user._id && user.apiToken) { request .set('x-api-user', user._id) .set('x-api-key', user.apiToken); } + if (additionalSets) { + request.set(additionalSets); + } + request .query(query) .send(send) diff --git a/website/public/js/controllers/authCtrl.js b/website/public/js/controllers/authCtrl.js index 7dced7ce00..0a262fc141 100644 --- a/website/public/js/controllers/authCtrl.js +++ b/website/public/js/controllers/authCtrl.js @@ -9,6 +9,8 @@ angular.module('habitrpg') function($scope, $rootScope, User, $http, $location, $window, ApiUrl, $modal, Analytics) { $scope.Analytics = Analytics; + $http.defaults.headers.common['x-client'] = 'habitica-web'; + $scope.logout = function() { localStorage.clear(); window.location.href = '/logout'; diff --git a/website/public/js/controllers/rootCtrl.js b/website/public/js/controllers/rootCtrl.js index 0e6fd743c7..506a779444 100644 --- a/website/public/js/controllers/rootCtrl.js +++ b/website/public/js/controllers/rootCtrl.js @@ -36,6 +36,8 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ $rootScope.toJson = angular.toJson; $rootScope.Payments = Payments; + $http.defaults.headers.common['x-client'] = 'habitica-web'; + // Angular UI Router $rootScope.$state = $state; $rootScope.$stateParams = $stateParams; diff --git a/website/src/controllers/api-v2/auth.js b/website/src/controllers/api-v2/auth.js index 7f6ae19bd3..b3a552721a 100644 --- a/website/src/controllers/api-v2/auth.js +++ b/website/src/controllers/api-v2/auth.js @@ -123,6 +123,8 @@ api.registerUser = function(req, res, next) { }; analytics.track('register', analyticsData) + user.registeredThrough = req.headers['x-client'] + user.save(function(err, savedUser){ // Clean previous email preferences // TODO when emails added to EmailUnsubcription they should use lowercase version diff --git a/website/src/models/user.js b/website/src/models/user.js index d55a06c2d1..61fe6bd140 100644 --- a/website/src/models/user.js +++ b/website/src/models/user.js @@ -481,34 +481,7 @@ UserSchema.pre('save', function(next) { // Populate new users with default content if (this.isNew){ - //TODO for some reason this doesn't work here: `_.merge(this, shared.content.userDefaults);` - var self = this; - _.each(['habits', 'dailys', 'todos', 'rewards', 'tags'], function(taskType){ - self[taskType] = _.map(shared.content.userDefaults[taskType], function(task){ - var newTask = _.cloneDeep(task); - - // Render task's text and notes in user's language - if(taskType === 'tags'){ - // tasks automatically get id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here - newTask.id = shared.uuid(); - newTask.name = newTask.name(self.preferences.language); - }else{ - newTask.text = newTask.text(self.preferences.language); - if(newTask.notes) { - newTask.notes = newTask.notes(self.preferences.language); - } - - if(newTask.checklist){ - newTask.checklist = _.map(newTask.checklist, function(checklistItem){ - checklistItem.text = checklistItem.text(self.preferences.language); - return checklistItem; - }); - } - } - - return newTask; - }); - }); + _populateDefaultsForNewUser(this); } //this.markModified('tasks'); @@ -601,6 +574,57 @@ UserSchema.methods.unlink = function(options, cb) { self.save(cb); } +function _populateDefaultsForNewUser(user) { + var taskTypes; + + if (user.registeredThrough === "habitica-web") { + taskTypes = ['habits', 'dailys', 'todos', 'rewards', 'tags']; + + _.each(user.flags.tutorial.common, function(value, section) { + user.flags.tutorial.common[section] = true; + }); + } else { + taskTypes = ['todos', 'tags'] + + user.flags.showTour = false; + + _.each(user.flags.tour, function(value, section) { + user.flags.tour[section] = -2; + }); + } + + _populateDefaultTasks(user, taskTypes); +} + +function _populateDefaultTasks (user, taskTypes) { + _.each(taskTypes, function(taskType){ + user[taskType] = _.map(shared.content.userDefaults[taskType], function(task){ + var newTask = _.cloneDeep(task); + + // Render task's text and notes in user's language + if(taskType === 'tags'){ + // tasks automatically get id=helpers.uuid() from TaskSchema id.default, but tags are Schema.Types.Mixed - so we need to manually invoke here + newTask.id = shared.uuid(); + newTask.name = newTask.name(user.preferences.language); + }else{ + newTask.text = newTask.text(user.preferences.language); + if(newTask.notes) { + newTask.notes = newTask.notes(user.preferences.language); + } + + if(newTask.checklist){ + newTask.checklist = _.map(newTask.checklist, function(checklistItem){ + checklistItem.text = checklistItem.text(user.preferences.language); + return checklistItem; + }); + } + } + + return newTask; + }); + }); +} + module.exports.schema = UserSchema; module.exports.model = mongoose.model("User", UserSchema); // Initially export an empty object so external requires will get diff --git a/website/src/routes/pages.js b/website/src/routes/pages.js index b88393301b..edeedc04c8 100644 --- a/website/src/routes/pages.js +++ b/website/src/routes/pages.js @@ -18,7 +18,7 @@ router.get('/', i18n.getUserLanguage, locals, function(req, res) { // -------- Marketing -------- -var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit', 'faq']; +var pages = ['front', 'privacy', 'terms', 'api', 'features', 'videos', 'contact', 'plans', 'new-stuff', 'community-guidelines', 'old-news', 'press-kit', 'faq', 'apps']; _.each(pages, function(name){ router.get('/static/' + name, i18n.getUserLanguage, locals, function(req, res) { diff --git a/website/views/shared/footer.jade b/website/views/shared/footer.jade index d3a4edb1cc..4da505f3ee 100644 --- a/website/views/shared/footer.jade +++ b/website/views/shared/footer.jade @@ -8,7 +8,7 @@ footer.footer(ng-controller='FooterCtrl') li a(href='https://itunes.apple.com/us/app/habitica/id994882113?ls=1&mt=8', target='_blank')=env.t('mobileIOS') li - a(href='https://play.google.com/store/apps/details?id=com.ocdevel.habitrpg', target='_blank')=env.t('mobileAndroid') + a(href='/static/apps')=env.t('mobileAndroid') if env.isStaticPage h4=env.t('language') select(ng-change='changeLang()', ng-model='selectedLanguage', ng-options='language.name for language in languages') diff --git a/website/views/static/apps.jade b/website/views/static/apps.jade new file mode 100644 index 0000000000..c4dce70b62 --- /dev/null +++ b/website/views/static/apps.jade @@ -0,0 +1,23 @@ +extends ./layout + +block vars + - var layoutEnv = env + +block title + title Habitica |  + =env.t('apps') + +block content + .lead + =env.t('checkOutIOSApp') + .row.text-center + #ibb-widget-root-994882113 + script. + (function(t,e,i,d){var o=t.getElementById(i),n=t.createElement(e);o.style.height=250;o.style.width=300;o.style.display='inline-block';n.id='ibb-widget',n.setAttribute('src',('https:'===t.location.protocol?'https://':'http://')+d),n.setAttribute('width','300'),n.setAttribute('height','250'),n.setAttribute('frameborder','0'),n.setAttribute('scrolling','no'),o.appendChild(n)})(document,'iframe','ibb-widget-root-994882113',"banners.itunes.apple.com/banner.html?partnerId=&aId=&bt=catalog&t=catalog_white&id=994882113&c=us&l=en-US&w=300&h=250&store=apps"); + .lead + =env.t('notifyAndroidApp') + .row.text-center + .sendgrid-subscription-widget(data-token='WEc2mXZw9YqemYAJp3a%2FO%2FxyvSWuFCFS2x88bbdMa%2FJjEH%2FlDdpc%2Flbz4PPZZRZMnTmr8kNOzI6ypw%2Br3r55wQ%3D%3D') + script. + !function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?"http":"https";if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+"://s3.amazonaws.com/subscription-cdn/0.2/widget.min.js";fjs.parentNode.insertBefore(js,fjs);}}(document, "script", "sendgrid-subscription-widget-js"); +