diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1460da60e1..d40517fa19 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1,6 +1,6 @@ { "name": "habitica", - "version": "3.101.0", + "version": "3.101.1", "dependencies": { "@gulp-sourcemaps/map-sources": { "version": "1.0.0", diff --git a/package.json b/package.json index dedc8aee2b..8f7d4d3424 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "habitica", "description": "A habit tracker app which treats your goals like a Role Playing Game.", - "version": "3.101.0", + "version": "3.101.1", "main": "./website/server/index.js", "dependencies": { "@slack/client": "^3.8.1", diff --git a/test/api/v3/integration/tasks/GET-tasks_user.test.js b/test/api/v3/integration/tasks/GET-tasks_user.test.js index 87af87d7a5..318bf29168 100644 --- a/test/api/v3/integration/tasks/GET-tasks_user.test.js +++ b/test/api/v3/integration/tasks/GET-tasks_user.test.js @@ -130,7 +130,8 @@ describe('GET /tasks/user', () => { }); it('returns dailies with isDue for the date specified', async () => { - let startDate = moment().subtract('1', 'days').toDate(); + // @TODO Add required format + let startDate = moment().subtract('1', 'days').toISOString(); let createdTasks = await user.post('/tasks/user', [ { text: 'test daily', @@ -150,4 +151,28 @@ describe('GET /tasks/user', () => { expect(dailys2[0]._id).to.equal(createdTasks._id); expect(dailys2[0].isDue).to.be.true; }); + + it('returns dailies with isDue for the date specified and will add CDS offset if time is not supplied', async () => { + await user.update({ + 'preferences.dayStart': 7, + }); + let startDate = moment().subtract('4', 'days').startOf('day').toISOString(); + await user.post('/tasks/user', [ + { + text: 'test daily', + type: 'daily', + startDate, + frequency: 'daily', + everyX: 2, + }, + ]); + + let today = moment().format('YYYY-MM-DD'); + let dailys = await user.get(`/tasks/user?type=dailys&dueDate=${today}`); + expect(dailys[0].isDue).to.be.true; + + let yesterday = moment().subtract('1', 'days').format('YYYY-MM-DD'); + let dailys2 = await user.get(`/tasks/user?type=dailys&dueDate=${yesterday}`); + expect(dailys2[0].isDue).to.be.false; + }); }); diff --git a/website/client-old/js/controllers/notificationCtrl.js b/website/client-old/js/controllers/notificationCtrl.js index 7dc2e3a033..e66be981f3 100644 --- a/website/client-old/js/controllers/notificationCtrl.js +++ b/website/client-old/js/controllers/notificationCtrl.js @@ -3,6 +3,8 @@ habitrpg.controller('NotificationCtrl', ['$scope', '$rootScope', 'Shared', 'Content', 'User', 'Guide', 'Notification', 'Analytics', 'Achievement', 'Social', 'Tasks', function ($scope, $rootScope, Shared, Content, User, Guide, Notification, Analytics, Achievement, Social, Tasks) { + var isRunningYesterdailies = false; + $rootScope.$watch('user', function (after, before) { runYesterDailies(); }); @@ -12,13 +14,17 @@ habitrpg.controller('NotificationCtrl', }); function runYesterDailies() { + if (isRunningYesterdailies) return; + var userLastCron = moment(User.user.lastCron).local(); var userDayStart = moment().startOf('day').add({ hours: User.user.preferences.dayStart }); if (!User.user.needsCron) return; var dailys = User.user.dailys; - if (!Boolean(dailys) || dailys.length === 0) return; + if (!$rootScope.appLoaded) return; + + isRunningYesterdailies = true; var yesterDay = moment().subtract('1', 'day').startOf('day').add({ hours: User.user.preferences.dayStart }); var yesterDailies = []; @@ -31,7 +37,9 @@ habitrpg.controller('NotificationCtrl', }); if (yesterDailies.length === 0) { - User.runCron(); + User.runCron().then(function () { + isRunningYesterdailies = false; + }); return; }; @@ -61,7 +69,10 @@ habitrpg.controller('NotificationCtrl', }); $scope.ageDailies = function () { - User.runCron(); + User.runCron() + .then(function () { + isRunningYesterdailies = false; + }); }; }], }); diff --git a/website/client-old/js/services/userServices.js b/website/client-old/js/services/userServices.js index 7d12ee0c3e..5ba7b008c4 100644 --- a/website/client-old/js/services/userServices.js +++ b/website/client-old/js/services/userServices.js @@ -418,7 +418,7 @@ angular.module('habitrpg') }, runCron: function () { - $http({ + return $http({ method: "POST", url: 'api/v3/cron', }) diff --git a/website/common/script/cron.js b/website/common/script/cron.js index 5ee846ecd6..fa1f2a388b 100644 --- a/website/common/script/cron.js +++ b/website/common/script/cron.js @@ -101,6 +101,7 @@ export function shouldDo (day, dailyTask, options = {}) { } let o = sanitizeOptions(options); let startOfDayWithCDSTime = startOfDay(defaults({ now: day }, o)); + // The time portion of the Start Date is never visible to or modifiable by the user so we must ignore it. // Therefore, we must also ignore the time portion of the user's day start (startOfDayWithCDSTime), otherwise the date comparison will be wrong for some times. // NB: The user's day start date has already been converted to the PREVIOUS day's date if the time portion was before CDS. diff --git a/website/server/libs/taskManager.js b/website/server/libs/taskManager.js index e9bcc4f2d2..bbf67fd03f 100644 --- a/website/server/libs/taskManager.js +++ b/website/server/libs/taskManager.js @@ -26,10 +26,21 @@ async function _validateTaskAlias (tasks, res) { export function setNextDue (task, user, dueDateOption) { if (task.type !== 'daily') return; + let now = moment().toDate(); let dateTaskIsDue = Date.now(); - if (dueDateOption) dateTaskIsDue = moment(dueDateOption); + if (dueDateOption) { + dateTaskIsDue = moment(dueDateOption); + // If not time is supplied. Let's assume we want start of Custom Day Start day. + if (dateTaskIsDue.hour() === 0 && dateTaskIsDue.minute() === 0 && dateTaskIsDue.second() === 0 && dateTaskIsDue.millisecond() === 0) { + dateTaskIsDue.add(user.preferences.dayStart, 'hours'); + } + + now = dateTaskIsDue; + } + let optionsForShouldDo = user.preferences.toObject(); + optionsForShouldDo.now = now; task.isDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo); optionsForShouldDo.nextDue = true; let nextDue = shared.shouldDo(dateTaskIsDue, task, optionsForShouldDo); @@ -176,6 +187,12 @@ export async function getTasks (req, res, options = {}) { let tasks = await mQuery.exec(); + if (dueDate) { + tasks.forEach((task) => { + setNextDue(task, user, dueDate); + }); + } + // Order tasks based on tasksOrder if (type && type !== 'completedTodos' && type !== '_allCompletedTodos') { let order = owner.tasksOrder[type]; @@ -190,8 +207,6 @@ export async function getTasks (req, res, options = {}) { } else { orderedTasks[i] = task; } - - if (dueDate) setNextDue(task, user, dueDate); }); // Remove empty values from the array and add any unordered task