Added needsCron field
This commit is contained in:
@@ -13,6 +13,9 @@ import amazonPayments from '../../libs/amazonPayments';
|
||||
import stripePayments from '../../libs/stripePayments';
|
||||
import paypalPayments from '../../libs/paypalPayments';
|
||||
|
||||
const daysSince = common.daysSince;
|
||||
|
||||
|
||||
schema.methods.isSubscribed = function isSubscribed () {
|
||||
let now = new Date();
|
||||
let plan = this.purchased.plan;
|
||||
@@ -27,7 +30,7 @@ schema.methods.hasNotCancelled = function hasNotCancelled () {
|
||||
|
||||
// Get an array of groups ids the user is member of
|
||||
schema.methods.getGroups = function getUserGroups () {
|
||||
let userGroups = this.guilds.slice(0); // clone user.guilds so we don't modify the original
|
||||
let userGroups = this.guilds.slice(0); // clone this.guilds so we don't modify the original
|
||||
if (this.party._id) userGroups.push(this.party._id);
|
||||
userGroups.push(TAVERN_ID);
|
||||
return userGroups;
|
||||
@@ -35,7 +38,7 @@ schema.methods.getGroups = function getUserGroups () {
|
||||
|
||||
|
||||
/**
|
||||
* Sends a message to a user. Archives a copy in sender's inbox.
|
||||
* Sends a message to a this. Archives a copy in sender's inbox.
|
||||
*
|
||||
* @param userToReceiveMessage The receiver
|
||||
* @param options
|
||||
@@ -63,7 +66,7 @@ schema.methods.sendMessage = async function sendMessage (userToReceiveMessage, o
|
||||
* Creates a notification based on the input parameters and adds it to the local user notifications array.
|
||||
* This does not save the notification to the database or interact with the database in any way.
|
||||
*
|
||||
* @param type The type of notification to add to the user. Possible values are defined in the UserNotificaiton Schema
|
||||
* @param type The type of notification to add to the this. Possible values are defined in the UserNotificaiton Schema
|
||||
* @param data The data to add to the notification
|
||||
*/
|
||||
schema.methods.addNotification = function addUserNotification (type, data = {}) {
|
||||
@@ -80,7 +83,7 @@ schema.methods.addNotification = function addUserNotification (type, data = {})
|
||||
* the user document(s) opened.
|
||||
*
|
||||
* @param query A Mongoose query defining the users to add the notification to.
|
||||
* @param type The type of notification to add to the user. Possible values are defined in the UserNotificaiton Schema
|
||||
* @param type The type of notification to add to the this. Possible values are defined in the UserNotificaiton Schema
|
||||
* @param data The data to add to the notification
|
||||
*/
|
||||
schema.statics.pushNotification = async function pushNotification (query, type, data = {}) {
|
||||
@@ -95,7 +98,7 @@ schema.statics.pushNotification = async function pushNotification (query, type,
|
||||
// Add stats.toNextLevel, stats.maxMP and stats.maxHealth
|
||||
// to a JSONified User stats object
|
||||
schema.methods.addComputedStatsToJSONObj = function addComputedStatsToUserJSONObj (statsObject) {
|
||||
// NOTE: if an item is manually added to user.stats then
|
||||
// NOTE: if an item is manually added to this.stats then
|
||||
// common/fns/predictableRandom must be tweaked so the new item is not considered.
|
||||
// Otherwise the client will have it while the server won't and the results will be different.
|
||||
statsObject.toNextLevel = common.tnl(this.stats.lvl);
|
||||
@@ -123,3 +126,85 @@ schema.methods.cancelSubscription = async function cancelSubscription () {
|
||||
|
||||
return await payments.cancelSubscription({user: this});
|
||||
};
|
||||
|
||||
schema.methods.daysUserHasMissed = function daysUserHasMissed (now, req = {}) {
|
||||
// If the user's timezone has changed (due to travel or daylight savings),
|
||||
// cron can be triggered twice in one day, so we check for that and use
|
||||
// both timezones to work out if cron should run.
|
||||
// CDS = Custom Day Start time.
|
||||
let timezoneOffsetFromUserPrefs = this.preferences.timezoneOffset;
|
||||
let timezoneOffsetAtLastCron = isFinite(this.preferences.timezoneOffsetAtLastCron) ? this.preferences.timezoneOffsetAtLastCron : timezoneOffsetFromUserPrefs;
|
||||
let timezoneOffsetFromBrowser = typeof req.header === 'function' && Number(req.header('x-user-timezoneoffset'));
|
||||
timezoneOffsetFromBrowser = isFinite(timezoneOffsetFromBrowser) ? timezoneOffsetFromBrowser : timezoneOffsetFromUserPrefs;
|
||||
// NB: All timezone offsets can be 0, so can't use `... || ...` to apply non-zero defaults
|
||||
|
||||
if (timezoneOffsetFromBrowser !== timezoneOffsetFromUserPrefs) {
|
||||
// The user's browser has just told Habitica that the user's timezone has
|
||||
// changed so store and use the new zone.
|
||||
this.preferences.timezoneOffset = timezoneOffsetFromBrowser;
|
||||
timezoneOffsetFromUserPrefs = timezoneOffsetFromBrowser;
|
||||
}
|
||||
|
||||
// How many days have we missed using the user's current timezone:
|
||||
let daysMissed = daysSince(this.lastCron, defaults({now}, this.preferences));
|
||||
|
||||
if (timezoneOffsetAtLastCron !== timezoneOffsetFromUserPrefs) {
|
||||
// Since cron last ran, the user's timezone has changed.
|
||||
// How many days have we missed using the old timezone:
|
||||
let daysMissedNewZone = daysMissed;
|
||||
let daysMissedOldZone = daysSince(this.lastCron, defaults({
|
||||
now,
|
||||
timezoneOffsetOverride: timezoneOffsetAtLastCron,
|
||||
}, this.preferences));
|
||||
|
||||
if (timezoneOffsetAtLastCron < timezoneOffsetFromUserPrefs) {
|
||||
// The timezone change was in the unsafe direction.
|
||||
// E.g., timezone changes from UTC+1 (offset -60) to UTC+0 (offset 0).
|
||||
// or timezone changes from UTC-4 (offset 240) to UTC-5 (offset 300).
|
||||
// Local time changed from, for example, 03:00 to 02:00.
|
||||
|
||||
if (daysMissedOldZone > 0 && daysMissedNewZone > 0) {
|
||||
// Both old and new timezones indicate that we SHOULD run cron, so
|
||||
// it is safe to do so immediately.
|
||||
daysMissed = Math.min(daysMissedOldZone, daysMissedNewZone);
|
||||
// use minimum value to be nice to user
|
||||
} else if (daysMissedOldZone > 0) {
|
||||
// The old timezone says that cron should run; the new timezone does not.
|
||||
// This should be impossible for this direction of timezone change, but
|
||||
// just in case I'm wrong...
|
||||
// TODO
|
||||
// console.log("zone has changed - old zone says run cron, NEW zone says no - stop cron now only -- SHOULD NOT HAVE GOT TO HERE", timezoneOffsetAtLastCron, timezoneOffsetFromUserPrefs, now); // used in production for confirming this never happens
|
||||
} else if (daysMissedNewZone > 0) {
|
||||
// The old timezone says that cron should NOT run -- i.e., cron has
|
||||
// already run today, from the old timezone's point of view.
|
||||
// The new timezone says that cron SHOULD run, but this is almost
|
||||
// certainly incorrect.
|
||||
// This happens when cron occurred at a time soon after the CDS. When
|
||||
// you reinterpret that time in the new timezone, it looks like it
|
||||
// was before the CDS, because local time has stepped backwards.
|
||||
// To fix this, rewrite the cron time to a time that the new
|
||||
// timezone interprets as being in today.
|
||||
|
||||
daysMissed = 0; // prevent cron running now
|
||||
let timezoneOffsetDiff = timezoneOffsetAtLastCron - timezoneOffsetFromUserPrefs;
|
||||
// e.g., for dangerous zone change: 240 - 300 = -60 or -660 - -600 = -60
|
||||
|
||||
this.lastCron = moment(this.lastCron).subtract(timezoneOffsetDiff, 'minutes');
|
||||
// NB: We don't change this.auth.timestamps.loggedin so that will still record the time that the previous cron actually ran.
|
||||
// From now on we can ignore the old timezone:
|
||||
this.preferences.timezoneOffsetAtLastCron = timezoneOffsetFromUserPrefs;
|
||||
} else {
|
||||
// Both old and new timezones indicate that cron should
|
||||
// NOT run.
|
||||
daysMissed = 0; // prevent cron running now
|
||||
}
|
||||
} else if (timezoneOffsetAtLastCron > timezoneOffsetFromUserPrefs) {
|
||||
daysMissed = daysMissedNewZone;
|
||||
// TODO: Either confirm that there is nothing that could possibly go wrong here and remove the need for this else branch, or fix stuff.
|
||||
// There are probably situations where the Dailies do not reset early enough for a user who was expecting the zone change and wants to use all their Dailies immediately in the new zone;
|
||||
// if so, we should provide an option for easy reset of Dailies (can't be automatic because there will be other situations where the user was not prepared).
|
||||
}
|
||||
}
|
||||
|
||||
return {daysMissed, timezoneOffsetFromUserPrefs};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user