TutorialHandler.mjs 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. import UserUpdater from '../User/UserUpdater.js'
  2. const POSTPONE_DURATION_MS = 24 * 60 * 60 * 1000 // 1 day
  3. /**
  4. * Change the tutorial state
  5. *
  6. * @param {string} userId
  7. * @param {string} tutorialKey
  8. * @param {'completed' | 'postponed'} state
  9. * @param {Date} [postponedUntil] - The date until which the tutorial is postponed
  10. */
  11. async function setTutorialState(
  12. userId,
  13. tutorialKey,
  14. state,
  15. postponedUntil = null
  16. ) {
  17. const updateData = {
  18. state,
  19. updatedAt: new Date(),
  20. }
  21. if (state === 'postponed' && postponedUntil) {
  22. updateData.postponedUntil = postponedUntil
  23. }
  24. await UserUpdater.promises.updateUser(userId, {
  25. $set: {
  26. [`completedTutorials.${tutorialKey}`]: updateData,
  27. },
  28. })
  29. }
  30. /**
  31. * Returns a list of inactive tutorials for a given user
  32. *
  33. * The user must be loaded with the completedTutorials property.
  34. */
  35. function getInactiveTutorials(user, tutorialKey) {
  36. const inactiveTutorials = []
  37. for (const [key, record] of Object.entries(user.completedTutorials ?? {})) {
  38. if (record instanceof Date) {
  39. // Legacy format: single date means the tutorial was completed
  40. inactiveTutorials.push(key)
  41. } else if (record.state === 'postponed') {
  42. const defaultPostponedUntil = new Date(
  43. record.updatedAt.getTime() + POSTPONE_DURATION_MS
  44. )
  45. const postponedUntil = record.postponedUntil ?? defaultPostponedUntil
  46. if (new Date() < postponedUntil) {
  47. inactiveTutorials.push(key)
  48. }
  49. } else {
  50. inactiveTutorials.push(key)
  51. }
  52. }
  53. return inactiveTutorials
  54. }
  55. export default { setTutorialState, getInactiveTutorials }