musora-content-services 2.5.1 → 2.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.coderabbit.yaml +0 -0
  2. package/.editorconfig +0 -0
  3. package/.github/pull_request_template.md +0 -0
  4. package/.github/workflows/node.js.yml +0 -0
  5. package/.prettierignore +0 -0
  6. package/.prettierrc +0 -0
  7. package/CHANGELOG.md +14 -0
  8. package/README.md +0 -0
  9. package/babel.config.cjs +0 -0
  10. package/docs/Content-Organization.html +0 -0
  11. package/docs/ContentOrganization.html +2 -2
  12. package/docs/Gamification.html +2 -2
  13. package/docs/UserManagement.html +0 -0
  14. package/docs/UserManagementSystem.html +2 -2
  15. package/docs/api_types.js.html +2 -2
  16. package/docs/config.js.html +2 -2
  17. package/docs/content-org_content-org.js.html +2 -2
  18. package/docs/content-org_playlists-types.js.html +2 -2
  19. package/docs/content-org_playlists.js.html +2 -2
  20. package/docs/content.js.html +2 -4
  21. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  22. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  23. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  24. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  25. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  26. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  27. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  28. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  29. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  30. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  31. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  32. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  33. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  34. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  35. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  36. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  37. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  38. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  39. package/docs/gamification_awards.js.html +2 -2
  40. package/docs/gamification_gamification.js.html +2 -2
  41. package/docs/gamification_types.js.html +2 -2
  42. package/docs/global.html +448 -2
  43. package/docs/global.html#User +0 -0
  44. package/docs/index.html +2 -2
  45. package/docs/module-Awards.html +2 -2
  46. package/docs/module-Config.html +2 -2
  47. package/docs/module-Content-Services-V2.html +8 -8
  48. package/docs/module-Interests.html +2 -2
  49. package/docs/module-Permissions.html +2 -2
  50. package/docs/module-Playlists.html +2 -2
  51. package/docs/module-Railcontent-Services.html +754 -44
  52. package/docs/module-Sanity-Services.html +2 -2
  53. package/docs/module-Session-Management.html +0 -0
  54. package/docs/module-Sessions.html +2 -2
  55. package/docs/module-User-Activity.html +521 -12
  56. package/docs/module-User-Management.html +0 -0
  57. package/docs/module-User-Permissions.html +0 -0
  58. package/docs/module-UserManagement.html +2 -2
  59. package/docs/module-UserProfile.html +266 -0
  60. package/docs/railcontent.js.html +41 -2
  61. package/docs/sanity.js.html +2 -2
  62. package/docs/scripts/collapse.js +0 -0
  63. package/docs/scripts/commonNav.js +0 -0
  64. package/docs/scripts/linenumber.js +0 -0
  65. package/docs/scripts/nav.js +0 -0
  66. package/docs/scripts/polyfill.js +0 -0
  67. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  68. package/docs/scripts/prettify/lang-css.js +0 -0
  69. package/docs/scripts/prettify/prettify.js +0 -0
  70. package/docs/scripts/search.js +0 -0
  71. package/docs/styles/jsdoc.css +0 -0
  72. package/docs/styles/prettify.css +0 -0
  73. package/docs/types.js.html +0 -0
  74. package/docs/userActivity.js.html +350 -263
  75. package/docs/user_interests.js.html +2 -2
  76. package/docs/user_management.js.html +2 -2
  77. package/docs/user_permissions.js.html +2 -2
  78. package/docs/user_profile.js.html +105 -0
  79. package/docs/user_sessions.js.html +2 -2
  80. package/docs/user_types.js.html +22 -2
  81. package/docs/user_user-management-system.js.html +2 -2
  82. package/docs/user_user-management.js.html +0 -0
  83. package/jest.config.js +0 -0
  84. package/jsdoc.json +0 -0
  85. package/package.json +1 -1
  86. package/src/contentMetaData.js +14 -0
  87. package/src/filterBuilder.js +0 -0
  88. package/src/index.d.ts +15 -0
  89. package/src/index.js +15 -0
  90. package/src/infrastructure/http/HttpClient.ts +0 -0
  91. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  92. package/src/infrastructure/http/index.ts +0 -0
  93. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  94. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  95. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  96. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  97. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  98. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  99. package/src/lib/httpHelper.js +0 -0
  100. package/src/lib/lastUpdated.js +0 -0
  101. package/src/services/api/types.js +0 -0
  102. package/src/services/config.js +0 -0
  103. package/src/services/content-org/content-org.js +0 -0
  104. package/src/services/content-org/playlists-types.js +0 -0
  105. package/src/services/content-org/playlists.js +0 -0
  106. package/src/services/content.js +0 -2
  107. package/src/services/contentAggregator.js +0 -0
  108. package/src/services/contentLikes.js +0 -0
  109. package/src/services/dataContext.js +0 -0
  110. package/src/services/dateUtils.js +0 -0
  111. package/src/services/forum.js +0 -0
  112. package/src/services/gamification/awards.js +0 -0
  113. package/src/services/gamification/gamification.js +0 -0
  114. package/src/services/gamification/types.js +0 -0
  115. package/src/services/imageSRCBuilder.js +0 -0
  116. package/src/services/imageSRCVerify.js +0 -0
  117. package/src/services/railcontent.js +39 -0
  118. package/src/services/recommendations.js +0 -0
  119. package/src/services/types.js +0 -0
  120. package/src/services/user/interests.js +0 -0
  121. package/src/services/user/management.js +0 -0
  122. package/src/services/user/permissions.js +0 -0
  123. package/src/services/user/profile.js +33 -0
  124. package/src/services/user/sessions.js +0 -0
  125. package/src/services/user/types.js +20 -0
  126. package/src/services/user/user-management-system.js +0 -0
  127. package/src/services/userActivity.js +348 -261
  128. package/test/HttpClient.test.js +0 -0
  129. package/test/content.test.js +8 -6
  130. package/test/contentLikes.test.js +0 -0
  131. package/test/contentProgress.test.js +0 -0
  132. package/test/dataContext.test.js +0 -0
  133. package/test/forum.test.js +0 -0
  134. package/test/imageSRCBuilder.test.js +0 -0
  135. package/test/imageSRCVerify.test.js +0 -0
  136. package/test/initializeTests.js +0 -0
  137. package/test/lib/lastUpdated.test.js +0 -0
  138. package/test/live/contentProgressLive.test.js +0 -0
  139. package/test/live/railcontentLive.test.js +0 -0
  140. package/test/localStorageMock.js +0 -0
  141. package/test/log.js +0 -0
  142. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  143. package/test/mockData/mockData_user_practices.json +0 -0
  144. package/test/sanityQueryService.test.js +6 -0
  145. package/test/streakMessage.test.js +0 -0
  146. package/test/user/permissions.test.js +0 -0
  147. package/test/userActivity.test.js +0 -0
  148. package/tools/generate-index.cjs +0 -0
  149. package/.yarnrc.yml +0 -1
  150. package/docs/module-Content-Services.html +0 -763
@@ -29,7 +29,7 @@
29
29
  <nav >
30
30
 
31
31
 
32
- <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Awards.html">Awards</a><ul class='methods'><li data-type='method'><a href="module-Awards.html#.fetchAwardsForUser">fetchAwardsForUser</a></li></ul></li><li><a href="module-Config.html">Config</a><ul class='methods'><li data-type='method'><a href="module-Config.html#.initializeService">initializeService</a></li></ul></li><li><a href="module-Content-Services-V2.html">Content-Services-V2</a><ul class='methods'><li data-type='method'><a href="module-Content-Services-V2.html#.getContentRows">getContentRows</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getNewAndUpcoming">getNewAndUpcoming</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getRecent">getRecent</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getRecommendedForYou">getRecommendedForYou</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getScheduleContentRows">getScheduleContentRows</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getTabResults">getTabResults</a></li></ul></li><li><a href="module-Interests.html">Interests</a><ul class='methods'><li data-type='method'><a href="module-Interests.html#.fetchInterests">fetchInterests</a></li><li data-type='method'><a href="module-Interests.html#.fetchUninterests">fetchUninterests</a></li><li data-type='method'><a href="module-Interests.html#.markContentAsInterested">markContentAsInterested</a></li><li data-type='method'><a href="module-Interests.html#.markContentAsNotInterested">markContentAsNotInterested</a></li><li data-type='method'><a href="module-Interests.html#.removeContentAsInterested">removeContentAsInterested</a></li><li data-type='method'><a href="module-Interests.html#.removeContentAsNotInterested">removeContentAsNotInterested</a></li></ul></li><li><a href="module-Permissions.html">Permissions</a><ul class='methods'><li data-type='method'><a href="module-Permissions.html#.fetchUserPermissions">fetchUserPermissions</a></li><li data-type='method'><a href="module-Permissions.html#.reset">reset</a></li></ul></li><li><a href="module-Playlists.html">Playlists</a><ul class='methods'><li data-type='method'><a href="module-Playlists.html#.addItemToPlaylist">addItemToPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.createPlaylist">createPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.duplicatePlaylist">duplicatePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.fetchPlaylist">fetchPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.fetchPlaylistItems">fetchPlaylistItems</a></li><li data-type='method'><a href="module-Playlists.html#.fetchUserPlaylists">fetchUserPlaylists</a></li><li data-type='method'><a href="module-Playlists.html#.togglePlaylistPrivate">togglePlaylistPrivate</a></li><li data-type='method'><a href="module-Playlists.html#.updatePlaylist">updatePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~likePlaylist">likePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~reportPlaylist">reportPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~togglePlaylistPrivate">togglePlaylistPrivate</a></li><li data-type='method'><a href="module-Playlists.html#~unlikePlaylist">unlikePlaylist</a></li></ul></li><li><a href="module-Railcontent-Services.html">Railcontent-Services</a><ul class='methods'><li data-type='method'><a href="module-Railcontent-Services.html#.assignModeratorToComment">assignModeratorToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.closeComment">closeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.createComment">createComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deleteComment">deleteComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.editComment">editComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchAllCompletedStates">fetchAllCompletedStates</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCarouselCardData">fetchCarouselCardData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeIndexMetadata">fetchChallengeIndexMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeLessonData">fetchChallengeLessonData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeMetadata">fetchChallengeMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeUserActiveChallenges">fetchChallengeUserActiveChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCommentRelies">fetchCommentRelies</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchComments">fetchComments</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedChallenges">fetchCompletedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedContent">fetchCompletedContent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedState">fetchCompletedState</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentInProgress">fetchContentInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentPageUserData">fetchContentPageUserData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchNextContentDataForParent">fetchNextContentDataForParent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchOwnedChallenges">fetchOwnedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchSongsInProgress">fetchSongsInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchTopComment">fetchTopComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserAward">fetchUserAward</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserBadges">fetchUserBadges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserChallengeProgress">fetchUserChallengeProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserPracticeNotes">fetchUserPracticeNotes</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.likeComment">likeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.openComment">openComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCommunityNotification">postChallengesCommunityNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnroll">postChallengesEnroll</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnrollmentNotification">postChallengesEnrollmentNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesHideCompletedBanner">postChallengesHideCompletedBanner</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesLeave">postChallengesLeave</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSetStartDate">postChallengesSetStartDate</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSoloNotification">postChallengesSoloNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesUnlock">postChallengesUnlock</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.replyToComment">replyToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.reportComment">reportComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.setStudentViewForUser">setStudentViewForUser</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unassignModeratorToComment">unassignModeratorToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unlikeComment">unlikeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#~fetchLastInteractedChild">fetchLastInteractedChild</a></li></ul></li><li><a href="module-Sanity-Services.html">Sanity-Services</a><ul class='methods'><li data-type='method'><a href="module-Sanity-Services.html#.fetchAll">fetchAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllFilterOptions">fetchAllFilterOptions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllPacks">fetchAllPacks</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtistLessons">fetchArtistLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtists">fetchArtists</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentId">fetchByRailContentId</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentIds">fetchByRailContentIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByReference">fetchByReference</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCoachLessons">fetchCoachLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchComingSoon">fetchComingSoon</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCommentModContentData">fetchCommentModContentData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchFoundation">fetchFoundation</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchGenreLessons">fetchGenreLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLeaving">fetchLeaving</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonContent">fetchLessonContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonsFeaturingThisContent">fetchLessonsFeaturingThisContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMetadata">fetchMetadata</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethod">fetchMethod</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildren">fetchMethodChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildrenIds">fetchMethodChildrenIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodPreviousNextLesson">fetchMethodPreviousNextLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNewReleases">fetchNewReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNextPreviousLesson">fetchNextPreviousLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchOtherSongVersions">fetchOtherSongVersions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackAll">fetchPackAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackData">fetchPackData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchParentForDownload">fetchParentForDownload</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedLessons">fetchRelatedLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedRecommendedContent">fetchRelatedRecommendedContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedSongs">fetchRelatedSongs</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchReturning">fetchReturning</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSanity">fetchSanity</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchScheduledReleases">fetchScheduledReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchShowsData">fetchShowsData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongArtistCount">fetchSongArtistCount</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongById">fetchSongById</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchUpcomingEvents">fetchUpcomingEvents</a></li><li data-type='method'><a href="module-Sanity-Services.html#.jumpToContinueContent">jumpToContinueContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#~fetchRelatedByLicense">fetchRelatedByLicense</a></li><li data-type='method'><a href="module-Sanity-Services.html#~getNextAndPreviousQuarterDates">getNextAndPreviousQuarterDates</a></li><li data-type='method'><a href="module-Sanity-Services.html#~getQueryFromPage">getQueryFromPage</a></li><li data-type='method'><a href="module-Sanity-Services.html#~handleCustomFetchAll">handleCustomFetchAll</a></li></ul></li><li><a href="module-Sessions.html">Sessions</a><ul class='methods'><li data-type='method'><a href="module-Sessions.html#.login">login</a></li><li data-type='method'><a href="module-Sessions.html#.logout">logout</a></li></ul></li><li><a href="module-User-Activity.html">User-Activity</a><ul class='methods'><li data-type='method'><a href="module-User-Activity.html#.calculateLongestStreaks">calculateLongestStreaks</a></li><li data-type='method'><a href="module-User-Activity.html#.createPracticeNotes">createPracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.deletePracticeSession">deletePracticeSession</a></li><li data-type='method'><a href="module-User-Activity.html#.getPracticeNotes">getPracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.getPracticeSessions">getPracticeSessions</a></li><li data-type='method'><a href="module-User-Activity.html#.getRecentActivity">getRecentActivity</a></li><li data-type='method'><a href="module-User-Activity.html#.getUserMonthlyStats">getUserMonthlyStats</a></li><li data-type='method'><a href="module-User-Activity.html#.getUserWeeklyStats">getUserWeeklyStats</a></li><li data-type='method'><a href="module-User-Activity.html#.recordUserPractice">recordUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.removeUserPractice">removeUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.restorePracticeSession">restorePracticeSession</a></li><li data-type='method'><a href="module-User-Activity.html#.restoreUserPractice">restoreUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.updatePracticeNotes">updatePracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.updateUserPractice">updateUserPractice</a></li></ul></li><li><a href="module-UserManagement.html">UserManagement</a><ul class='methods'><li data-type='method'><a href="module-UserManagement.html#.blockUser">blockUser</a></li><li data-type='method'><a href="module-UserManagement.html#.unblockUser">unblockUser</a></li></ul></li></ul><h3>Namespaces</h3><ul><li><a href="ContentOrganization.html">ContentOrganization</a></li><li><a href="Gamification.html">Gamification</a></li><li><a href="UserManagementSystem.html">UserManagementSystem</a></li></ul><h3><a href="global.html">Global</a></h3>
32
+ <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Awards.html">Awards</a><ul class='methods'><li data-type='method'><a href="module-Awards.html#.fetchAwardsForUser">fetchAwardsForUser</a></li></ul></li><li><a href="module-Config.html">Config</a><ul class='methods'><li data-type='method'><a href="module-Config.html#.initializeService">initializeService</a></li></ul></li><li><a href="module-Content-Services-V2.html">Content-Services-V2</a><ul class='methods'><li data-type='method'><a href="module-Content-Services-V2.html#.getContentRows">getContentRows</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getNewAndUpcoming">getNewAndUpcoming</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getRecent">getRecent</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getRecommendedForYou">getRecommendedForYou</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getScheduleContentRows">getScheduleContentRows</a></li><li data-type='method'><a href="module-Content-Services-V2.html#.getTabResults">getTabResults</a></li></ul></li><li><a href="module-Interests.html">Interests</a><ul class='methods'><li data-type='method'><a href="module-Interests.html#.fetchInterests">fetchInterests</a></li><li data-type='method'><a href="module-Interests.html#.fetchUninterests">fetchUninterests</a></li><li data-type='method'><a href="module-Interests.html#.markContentAsInterested">markContentAsInterested</a></li><li data-type='method'><a href="module-Interests.html#.markContentAsNotInterested">markContentAsNotInterested</a></li><li data-type='method'><a href="module-Interests.html#.removeContentAsInterested">removeContentAsInterested</a></li><li data-type='method'><a href="module-Interests.html#.removeContentAsNotInterested">removeContentAsNotInterested</a></li></ul></li><li><a href="module-Permissions.html">Permissions</a><ul class='methods'><li data-type='method'><a href="module-Permissions.html#.fetchUserPermissions">fetchUserPermissions</a></li><li data-type='method'><a href="module-Permissions.html#.reset">reset</a></li></ul></li><li><a href="module-Playlists.html">Playlists</a><ul class='methods'><li data-type='method'><a href="module-Playlists.html#.addItemToPlaylist">addItemToPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.createPlaylist">createPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.duplicatePlaylist">duplicatePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.fetchPlaylist">fetchPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.fetchPlaylistItems">fetchPlaylistItems</a></li><li data-type='method'><a href="module-Playlists.html#.fetchUserPlaylists">fetchUserPlaylists</a></li><li data-type='method'><a href="module-Playlists.html#.togglePlaylistPrivate">togglePlaylistPrivate</a></li><li data-type='method'><a href="module-Playlists.html#.updatePlaylist">updatePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~likePlaylist">likePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~reportPlaylist">reportPlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~togglePlaylistPrivate">togglePlaylistPrivate</a></li><li data-type='method'><a href="module-Playlists.html#~unlikePlaylist">unlikePlaylist</a></li></ul></li><li><a href="module-Railcontent-Services.html">Railcontent-Services</a><ul class='methods'><li data-type='method'><a href="module-Railcontent-Services.html#.assignModeratorToComment">assignModeratorToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.closeComment">closeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.createComment">createComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.deleteComment">deleteComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.editComment">editComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchAllCompletedStates">fetchAllCompletedStates</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCarouselCardData">fetchCarouselCardData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeIndexMetadata">fetchChallengeIndexMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeLessonData">fetchChallengeLessonData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeMetadata">fetchChallengeMetadata</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchChallengeUserActiveChallenges">fetchChallengeUserActiveChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCommentRelies">fetchCommentRelies</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchComments">fetchComments</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedChallenges">fetchCompletedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedContent">fetchCompletedContent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchCompletedState">fetchCompletedState</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentInProgress">fetchContentInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchContentPageUserData">fetchContentPageUserData</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchNextContentDataForParent">fetchNextContentDataForParent</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchOwnedChallenges">fetchOwnedChallenges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchRecentUserActivities">fetchRecentUserActivities</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchSongsInProgress">fetchSongsInProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchTopComment">fetchTopComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserAward">fetchUserAward</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserBadges">fetchUserBadges</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserChallengeProgress">fetchUserChallengeProgress</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.fetchUserPracticeNotes">fetchUserPracticeNotes</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.likeComment">likeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.openComment">openComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesCommunityNotification">postChallengesCommunityNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnroll">postChallengesEnroll</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesEnrollmentNotification">postChallengesEnrollmentNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesHideCompletedBanner">postChallengesHideCompletedBanner</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesLeave">postChallengesLeave</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSetStartDate">postChallengesSetStartDate</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesSoloNotification">postChallengesSoloNotification</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postChallengesUnlock">postChallengesUnlock</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.replyToComment">replyToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.reportComment">reportComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.setStudentViewForUser">setStudentViewForUser</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unassignModeratorToComment">unassignModeratorToComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.unlikeComment">unlikeComment</a></li><li data-type='method'><a href="module-Railcontent-Services.html#~fetchLastInteractedChild">fetchLastInteractedChild</a></li></ul></li><li><a href="module-Sanity-Services.html">Sanity-Services</a><ul class='methods'><li data-type='method'><a href="module-Sanity-Services.html#.fetchAll">fetchAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllFilterOptions">fetchAllFilterOptions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchAllPacks">fetchAllPacks</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtistLessons">fetchArtistLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchArtists">fetchArtists</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentId">fetchByRailContentId</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByRailContentIds">fetchByRailContentIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchByReference">fetchByReference</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCoachLessons">fetchCoachLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchComingSoon">fetchComingSoon</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchCommentModContentData">fetchCommentModContentData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchFoundation">fetchFoundation</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchGenreLessons">fetchGenreLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLeaving">fetchLeaving</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonContent">fetchLessonContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchLessonsFeaturingThisContent">fetchLessonsFeaturingThisContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMetadata">fetchMetadata</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethod">fetchMethod</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildren">fetchMethodChildren</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodChildrenIds">fetchMethodChildrenIds</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodPreviousNextLesson">fetchMethodPreviousNextLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNewReleases">fetchNewReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchNextPreviousLesson">fetchNextPreviousLesson</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchOtherSongVersions">fetchOtherSongVersions</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackAll">fetchPackAll</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchPackData">fetchPackData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchParentForDownload">fetchParentForDownload</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedLessons">fetchRelatedLessons</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedRecommendedContent">fetchRelatedRecommendedContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchRelatedSongs">fetchRelatedSongs</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchReturning">fetchReturning</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSanity">fetchSanity</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchScheduledReleases">fetchScheduledReleases</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchShowsData">fetchShowsData</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongArtistCount">fetchSongArtistCount</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchSongById">fetchSongById</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchUpcomingEvents">fetchUpcomingEvents</a></li><li data-type='method'><a href="module-Sanity-Services.html#.jumpToContinueContent">jumpToContinueContent</a></li><li data-type='method'><a href="module-Sanity-Services.html#~fetchRelatedByLicense">fetchRelatedByLicense</a></li><li data-type='method'><a href="module-Sanity-Services.html#~getNextAndPreviousQuarterDates">getNextAndPreviousQuarterDates</a></li><li data-type='method'><a href="module-Sanity-Services.html#~getQueryFromPage">getQueryFromPage</a></li><li data-type='method'><a href="module-Sanity-Services.html#~handleCustomFetchAll">handleCustomFetchAll</a></li></ul></li><li><a href="module-Sessions.html">Sessions</a><ul class='methods'><li data-type='method'><a href="module-Sessions.html#.login">login</a></li><li data-type='method'><a href="module-Sessions.html#.logout">logout</a></li></ul></li><li><a href="module-User-Activity.html">User-Activity</a><ul class='methods'><li data-type='method'><a href="module-User-Activity.html#.calculateLongestStreaks">calculateLongestStreaks</a></li><li data-type='method'><a href="module-User-Activity.html#.createPracticeNotes">createPracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.deletePracticeSession">deletePracticeSession</a></li><li data-type='method'><a href="module-User-Activity.html#.deleteUserActivity">deleteUserActivity</a></li><li data-type='method'><a href="module-User-Activity.html#.getPracticeNotes">getPracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.getPracticeSessions">getPracticeSessions</a></li><li data-type='method'><a href="module-User-Activity.html#.getRecentActivity">getRecentActivity</a></li><li data-type='method'><a href="module-User-Activity.html#.getUserMonthlyStats">getUserMonthlyStats</a></li><li data-type='method'><a href="module-User-Activity.html#.getUserWeeklyStats">getUserWeeklyStats</a></li><li data-type='method'><a href="module-User-Activity.html#.recordUserActivity">recordUserActivity</a></li><li data-type='method'><a href="module-User-Activity.html#.recordUserPractice">recordUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.removeUserPractice">removeUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.restorePracticeSession">restorePracticeSession</a></li><li data-type='method'><a href="module-User-Activity.html#.restoreUserPractice">restoreUserPractice</a></li><li data-type='method'><a href="module-User-Activity.html#.updatePracticeNotes">updatePracticeNotes</a></li><li data-type='method'><a href="module-User-Activity.html#.updateUserPractice">updateUserPractice</a></li></ul></li><li><a href="module-UserManagement.html">UserManagement</a><ul class='methods'><li data-type='method'><a href="module-UserManagement.html#.blockUser">blockUser</a></li><li data-type='method'><a href="module-UserManagement.html#.unblockUser">unblockUser</a></li></ul></li><li><a href="module-UserProfile.html">UserProfile</a><ul class='methods'><li data-type='method'><a href="module-UserProfile.html#.otherStats">otherStats</a></li></ul></li></ul><h3>Namespaces</h3><ul><li><a href="ContentOrganization.html">ContentOrganization</a></li><li><a href="Gamification.html">Gamification</a></li><li><a href="UserManagementSystem.html">UserManagementSystem</a></li></ul><h3><a href="global.html">Global</a></h3>
33
33
 
34
34
  </nav>
35
35
 
@@ -49,20 +49,19 @@
49
49
  * @module User-Activity
50
50
  */
51
51
 
52
- import {fetchUserPractices, logUserPractice, fetchUserPracticeMeta, fetchUserPracticeNotes, fetchHandler} from './railcontent'
52
+ import {
53
+ fetchUserPractices,
54
+ logUserPractice,
55
+ fetchUserPracticeMeta,
56
+ fetchUserPracticeNotes,
57
+ fetchHandler,
58
+ fetchRecentUserActivities,
59
+ } from './railcontent'
53
60
  import { DataContext, UserActivityVersionKey } from './dataContext.js'
54
- import {fetchByRailContentIds} from "./sanity";
55
- import {lessonTypesMapping} from "../contentTypeConfig";
56
- import { convertToTimeZone, getMonday, getWeekNumber, isSameDate, isNextDay } from './dateUtils.js';
57
- import {globalConfig} from "./config";
58
-
59
- const recentActivity = [
60
- { id: 5,title: '3 Easy Classical Songs For Beginners', action: 'Comment', thumbnail: 'https://cdn.sanity.io/images/4032r8py/production/8a7fb4d7473306c5fa51ba2e8867e03d44342b18-1920x1080.jpg', summary: 'Just completed the advanced groove lesson! I’m finally feeling more confident with my fills. Thanks for the clear explanations and practice tips! ', date: '2025-03-25 10:09:48' },
61
- { id:4, title: 'Piano Man by Billy Joel', action: 'Play', thumbnail:'https://cdn.sanity.io/images/4032r8py/production/107c258114540170399dfd72a50dae51575552f4-1000x1000.jpg', date: '2025-03-25 10:04:48' },
62
- { id:3, title: 'General Piano Discussion', action: 'Post', thumbnail: 'https://cdn.sanity.io/images/4032r8py/production/2331571d237b42dbf72f0cf35fdf163d996c5c5a-1920x1080.jpg', summary: 'Just completed the advanced groove lesson! I’m finally feeling more confident with my fills. Thanks for the clear explanations and practice tips! ', date: '2025-03-25 09:49:48' },
63
- { id:2, title: 'Welcome To Guitareo', action: 'Complete', thumbnail: 'https://cdn.sanity.io/images/4032r8py/production/2331571d237b42dbf72f0cf35fdf163d996c5c5a-1920x1080.jpg',date: '2025-03-25 09:34:48' },
64
- { id:1, title: 'Welcome To Guitareo', action: 'Start', thumbnail: 'https://cdn.sanity.io/images/4032r8py/production/2331571d237b42dbf72f0cf35fdf163d996c5c5a-1920x1080.jpg',date: '2025-03-25 09:04:48' },
65
- ]
61
+ import { fetchByRailContentIds } from './sanity'
62
+ import { lessonTypesMapping } from '../contentTypeConfig'
63
+ import { convertToTimeZone, getMonday, getWeekNumber, isSameDate, isNextDay } from './dateUtils.js'
64
+ import { globalConfig } from './config'
66
65
 
67
66
  const DATA_KEY_PRACTICES = 'practices'
68
67
  const DATA_KEY_LAST_UPDATED_TIME = 'u'
@@ -70,25 +69,43 @@ const DATA_KEY_LAST_UPDATED_TIME = 'u'
70
69
  const DAYS = ['M', 'T', 'W', 'T', 'F', 'S', 'S']
71
70
 
72
71
  const streakMessages = {
73
- startStreak: "Start your streak by taking any lesson!",
74
- restartStreak: "Restart your streak by taking any lesson!",
72
+ startStreak: 'Start your streak by taking any lesson!',
73
+ restartStreak: 'Restart your streak by taking any lesson!',
75
74
 
76
75
  // Messages when last active day is today
77
- dailyStreak: (streak) => `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak! Way to keep it going!`,
78
- dailyStreakShort: (streak) => `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak!`,
79
- weeklyStreak: (streak) => `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep up the momentum!`,
80
- greatJobWeeklyStreak: (streak) => `Great job! You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep it going!`,
76
+ dailyStreak: (streak) =>
77
+ `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak! Way to keep it going!`,
78
+ dailyStreakShort: (streak) =>
79
+ `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak!`,
80
+ weeklyStreak: (streak) =>
81
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep up the momentum!`,
82
+ greatJobWeeklyStreak: (streak) =>
83
+ `Great job! You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep it going!`,
81
84
 
82
85
  // Messages when last active day is NOT today
83
- dailyStreakReminder: (streak) => `You have ${getIndefiniteArticle(streak)} ${streak} day streak! Keep it going with any lesson or song!`,
84
- weeklyStreakKeepUp: (streak) => `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep up the momentum!`,
85
- weeklyStreakReminder: (streak) => `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep it going with any lesson or song!`,
86
- };
86
+ dailyStreakReminder: (streak) =>
87
+ `You have ${getIndefiniteArticle(streak)} ${streak} day streak! Keep it going with any lesson or song!`,
88
+ weeklyStreakKeepUp: (streak) =>
89
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep up the momentum!`,
90
+ weeklyStreakReminder: (streak) =>
91
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep it going with any lesson or song!`,
92
+ }
87
93
 
88
94
  function getIndefiniteArticle(streak) {
89
- return streak === 8 || (streak >= 80 &amp;&amp; streak &lt;= 89) || (streak >= 800 &amp;&amp; streak &lt;= 899) ? 'an' : 'a'
95
+ return streak === 8 || (streak >= 80 &amp;&amp; streak &lt;= 89) || (streak >= 800 &amp;&amp; streak &lt;= 899)
96
+ ? 'an'
97
+ : 'a'
90
98
  }
91
99
 
100
+ export async function getUserPractices(userId = globalConfig.sessionConfig.userId) {
101
+ if (userId !== globalConfig.sessionConfig.userId) {
102
+ let data = await fetchUserPractices({ userId })
103
+ return data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
104
+ } else {
105
+ let data = await userActivityContext.getData()
106
+ return data?.[DATA_KEY_PRACTICES] ?? {}
107
+ }
108
+ }
92
109
 
93
110
  export let userActivityContext = new DataContext(UserActivityVersionKey, fetchUserPractices)
94
111
 
@@ -107,24 +124,24 @@ export async function getUserWeeklyStats() {
107
124
  let data = await userActivityContext.getData()
108
125
  let practices = data?.[DATA_KEY_PRACTICES] ?? {}
109
126
  let sortedPracticeDays = Object.keys(practices)
110
- .map(date => new Date(date))
111
- .sort((a, b) => b - a);
127
+ .map((date) => new Date(date))
128
+ .sort((a, b) => b - a)
112
129
 
113
- let today = new Date();
114
- today.setHours(0, 0, 0, 0);
130
+ let today = new Date()
131
+ today.setHours(0, 0, 0, 0)
115
132
  let startOfWeek = getMonday(today) // Get last Monday
116
133
  let dailyStats = []
117
134
 
118
135
  for (let i = 0; i &lt; 7; i++) {
119
136
  let day = new Date(startOfWeek)
120
137
  day.setDate(startOfWeek.getDate() + i)
121
- let hasPractice = sortedPracticeDays.some(practiceDate => isSameDate(practiceDate, day));
138
+ let hasPractice = sortedPracticeDays.some((practiceDate) => isSameDate(practiceDate, day))
122
139
  let isActive = isSameDate(today, day)
123
- let type = (hasPractice ? 'tracked' : (isActive ? 'active' : 'none'))
140
+ let type = hasPractice ? 'tracked' : isActive ? 'active' : 'none'
124
141
  dailyStats.push({ key: i, label: DAYS[i], isActive, inStreak: hasPractice, type })
125
142
  }
126
143
 
127
- let { streakMessage } = getStreaksAndMessage(practices);
144
+ let { streakMessage } = getStreaksAndMessage(practices)
128
145
 
129
146
  return { data: { dailyActiveStats: dailyStats, streakMessage, practices } }
130
147
  }
@@ -159,27 +176,20 @@ export async function getUserWeeklyStats() {
159
176
  * // Get stats for another user
160
177
  * getUserMonthlyStats({ userId: 123 }).then(console.log);
161
178
  */
162
- export async function getUserMonthlyStats( params = {}) {
163
- const now = new Date();
179
+ export async function getUserMonthlyStats(params = {}) {
180
+ const now = new Date()
164
181
  const {
165
182
  year = now.getFullYear(),
166
183
  month = now.getMonth(),
167
184
  day = 1,
168
185
  userId = globalConfig.sessionConfig.userId,
169
- } = params;
170
- let practices = {}
171
- if(userId !== globalConfig.sessionConfig.userId) {
172
- let data = await fetchUserPractices({userId});
173
- practices = data?.["data"]?.[DATA_KEY_PRACTICES]?? {}
174
- }else {
175
- let data = await userActivityContext.getData()
176
- practices = data?.[DATA_KEY_PRACTICES] ?? {}
177
- }
186
+ } = params
187
+ let practices = await getUserPractices(userId)
178
188
 
179
189
  // Get the first day of the specified month and the number of days in that month
180
190
  let firstDayOfMonth = new Date(year, month, 1)
181
191
  let today = new Date()
182
- today.setHours(0, 0, 0, 0);
192
+ today.setHours(0, 0, 0, 0)
183
193
 
184
194
  let startOfGrid = getMonday(firstDayOfMonth)
185
195
 
@@ -203,7 +213,7 @@ export async function getUserMonthlyStats( params = {}) {
203
213
  endOfMonth.setDate(endOfMonth.getDate() + 1)
204
214
  }
205
215
 
206
- let daysInMonth = Math.ceil((endOfMonth - startOfGrid) / (1000 * 60 * 60 * 24)) + 1;
216
+ let daysInMonth = Math.ceil((endOfMonth - startOfGrid) / (1000 * 60 * 60 * 24)) + 1
207
217
 
208
218
  let dailyStats = []
209
219
  let practiceDuration = 0
@@ -213,26 +223,26 @@ export async function getUserMonthlyStats( params = {}) {
213
223
  for (let i = 0; i &lt; daysInMonth; i++) {
214
224
  let day = new Date(startOfGrid)
215
225
  day.setDate(startOfGrid.getDate() + i)
216
- let dayKey = `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`;
226
+ let dayKey = `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`
217
227
 
218
228
  // Check if the user has activity for the day
219
229
  let dayActivity = practices[dayKey] ?? null
220
230
  let weekKey = getWeekNumber(day)
221
231
 
222
232
  if (!weeklyStats[weekKey]) {
223
- weeklyStats[weekKey] = { key: weekKey, inStreak: false };
233
+ weeklyStats[weekKey] = { key: weekKey, inStreak: false }
224
234
  }
225
235
 
226
236
  if (dayActivity !== null) {
227
237
  practiceDuration += dayActivity.reduce((sum, entry) => sum + entry.duration_seconds, 0)
228
- daysPracticed++;
238
+ daysPracticed++
229
239
  }
230
240
 
231
241
  let isActive = isSameDate(today, day)
232
- let type = ((dayActivity !== null) ? 'tracked' : (isActive ? 'active' : 'none'))
233
- let isInStreak = dayActivity !== null;
242
+ let type = dayActivity !== null ? 'tracked' : isActive ? 'active' : 'none'
243
+ let isInStreak = dayActivity !== null
234
244
  if (isInStreak) {
235
- weeklyStats[weekKey].inStreak = true;
245
+ weeklyStats[weekKey].inStreak = true
236
246
  }
237
247
 
238
248
  dailyStats.push({
@@ -258,16 +268,17 @@ export async function getUserMonthlyStats( params = {}) {
258
268
  return obj
259
269
  }, {})
260
270
 
261
- let { currentDailyStreak, currentWeeklyStreak } = calculateStreaks(filteredPractices);
271
+ let { currentDailyStreak, currentWeeklyStreak } = calculateStreaks(filteredPractices)
262
272
 
263
- return { data: {
264
- dailyActiveStats: dailyStats,
273
+ return {
274
+ data: {
275
+ dailyActiveStats: dailyStats,
265
276
  weeklyActiveStats: Object.values(weeklyStats),
266
277
  practiceDuration,
267
278
  currentDailyStreak,
268
279
  currentWeeklyStreak,
269
280
  daysPracticed,
270
- }
281
+ },
271
282
  }
272
283
  }
273
284
 
@@ -302,37 +313,39 @@ export async function getUserMonthlyStats( params = {}) {
302
313
  * .catch(error => console.error(error));
303
314
  */
304
315
  export async function recordUserPractice(practiceDetails) {
305
- practiceDetails.auto = 0;
316
+ practiceDetails.auto = 0
306
317
  if (practiceDetails.content_id) {
307
- practiceDetails.auto = 1;
318
+ practiceDetails.auto = 1
308
319
  }
309
320
 
310
321
  await userActivityContext.update(
311
322
  async function (localContext) {
312
- let userData = localContext.data ?? { [DATA_KEY_PRACTICES]: {} };
313
- localContext.data = userData;
323
+ let userData = localContext.data ?? { [DATA_KEY_PRACTICES]: {} }
324
+ localContext.data = userData
314
325
  },
315
326
  async function () {
316
- const response = await logUserPractice(practiceDetails);
327
+ const response = await logUserPractice(practiceDetails)
317
328
  if (response) {
318
329
  await userActivityContext.updateLocal(async function (localContext) {
319
330
  const newPractices = response.data ?? []
320
- newPractices.forEach(newPractice => {
321
- const { date } = newPractice;
331
+ newPractices.forEach((newPractice) => {
332
+ const { date } = newPractice
322
333
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
323
- localContext.data[DATA_KEY_PRACTICES][date] = [];
334
+ localContext.data[DATA_KEY_PRACTICES][date] = []
324
335
  }
325
- localContext.data[DATA_KEY_PRACTICES][date][DATA_KEY_LAST_UPDATED_TIME] = Math.round(new Date().getTime() / 1000)
326
- localContext.data[DATA_KEY_PRACTICES][date].push({
327
- id: newPractice.id,
328
- duration_seconds: newPractice.duration_seconds // Add the new practice for this date
329
- });
330
- });
331
- });
336
+ localContext.data[DATA_KEY_PRACTICES][date][DATA_KEY_LAST_UPDATED_TIME] = Math.round(
337
+ new Date().getTime() / 1000
338
+ )
339
+ localContext.data[DATA_KEY_PRACTICES][date].push({
340
+ id: newPractice.id,
341
+ duration_seconds: newPractice.duration_seconds, // Add the new practice for this date
342
+ })
343
+ })
344
+ })
332
345
  }
333
- return response;
346
+ return response
334
347
  }
335
- );
348
+ )
336
349
  }
337
350
  /**
338
351
  * Updates a user's practice session with new details and syncs the changes remotely.
@@ -375,21 +388,21 @@ export async function updateUserPractice(id, practiceDetails) {
375
388
  * .catch(error => console.error(error));
376
389
  */
377
390
  export async function removeUserPractice(id) {
378
- let url = `/api/user/practices/v1/practices${buildQueryString([id])}`;
391
+ let url = `/api/user/practices/v1/practices${buildQueryString([id])}`
379
392
  await userActivityContext.update(
380
393
  async function (localContext) {
381
394
  if (localContext.data?.[DATA_KEY_PRACTICES]) {
382
- Object.keys(localContext.data[DATA_KEY_PRACTICES]).forEach(date => {
383
- localContext.data[DATA_KEY_PRACTICES][date] = localContext.data[DATA_KEY_PRACTICES][date].filter(
384
- practice => practice.id !== id
385
- );
386
- });
395
+ Object.keys(localContext.data[DATA_KEY_PRACTICES]).forEach((date) => {
396
+ localContext.data[DATA_KEY_PRACTICES][date] = localContext.data[DATA_KEY_PRACTICES][
397
+ date
398
+ ].filter((practice) => practice.id !== id)
399
+ })
387
400
  }
388
401
  },
389
402
  async function () {
390
- return await fetchHandler(url, 'delete');
403
+ return await fetchHandler(url, 'delete')
391
404
  }
392
- );
405
+ )
393
406
  }
394
407
 
395
408
  /**
@@ -405,22 +418,32 @@ export async function removeUserPractice(id) {
405
418
  * .catch(error => console.error(error));
406
419
  */
407
420
  export async function restoreUserPractice(id) {
408
- let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`;
409
- const response = await fetchHandler(url, 'put');
421
+ let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`
422
+ const response = await fetchHandler(url, 'put')
410
423
  if (response?.data) {
411
424
  await userActivityContext.updateLocal(async function (localContext) {
412
- const restoredPractice = response.data;
413
- const { date } = restoredPractice;
425
+ const restoredPractice = response.data
426
+ const { date } = restoredPractice
414
427
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
415
- localContext.data[DATA_KEY_PRACTICES][date] = [];
428
+ localContext.data[DATA_KEY_PRACTICES][date] = []
416
429
  }
417
430
  localContext.data[DATA_KEY_PRACTICES][date].push({
418
431
  id: restoredPractice.id,
419
432
  duration_seconds: restoredPractice.duration_seconds,
420
- });
421
- });
433
+ })
434
+ })
435
+ }
436
+ const formattedMeta = await formatPracticeMeta(response.data)
437
+ const practiceDuration = formattedMeta.reduce(
438
+ (total, practice) => total + (practice.duration || 0),
439
+ 0
440
+ )
441
+ return {
442
+ data: formattedMeta,
443
+ message: response.message,
444
+ version: response.version,
445
+ practiceDuration,
422
446
  }
423
- return response;
424
447
  }
425
448
 
426
449
  /**
@@ -440,20 +463,20 @@ export async function restoreUserPractice(id) {
440
463
  * .catch(error => console.error("Delete failed:", error));
441
464
  */
442
465
  export async function deletePracticeSession(day) {
443
- const userPracticesIds = await getUserPracticeIds(day);
444
- if (!userPracticesIds.length) return [];
466
+ const userPracticesIds = await getUserPracticeIds(day)
467
+ if (!userPracticesIds.length) return []
445
468
 
446
- const url = `/api/user/practices/v1/practices${buildQueryString(userPracticesIds)}`;
469
+ const url = `/api/user/practices/v1/practices${buildQueryString(userPracticesIds)}`
447
470
  await userActivityContext.update(
448
471
  async function (localContext) {
449
472
  if (localContext.data?.[DATA_KEY_PRACTICES]?.[day]) {
450
- delete localContext.data[DATA_KEY_PRACTICES][day];
473
+ delete localContext.data[DATA_KEY_PRACTICES][day]
451
474
  }
452
475
  },
453
476
  async function () {
454
- return await fetchHandler(url, 'DELETE', null);
477
+ return await fetchHandler(url, 'DELETE', null)
455
478
  }
456
- );
479
+ )
457
480
  }
458
481
 
459
482
  /**
@@ -473,25 +496,31 @@ export async function deletePracticeSession(day) {
473
496
  * .catch(error => console.error("Restore failed:", error));
474
497
  */
475
498
  export async function restorePracticeSession(date) {
476
- const url = `/api/user/practices/v1/practices/restore?date=${date}`;
477
- const response = await fetchHandler(url, 'PUT', null);
499
+ const url = `/api/user/practices/v1/practices/restore?date=${date}`
500
+ const response = await fetchHandler(url, 'PUT', null)
478
501
 
479
502
  if (response?.data) {
480
503
  await userActivityContext.updateLocal(async function (localContext) {
481
504
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
482
- localContext.data[DATA_KEY_PRACTICES][date] = [];
505
+ localContext.data[DATA_KEY_PRACTICES][date] = []
483
506
  }
484
507
 
485
- response.data.forEach(restoredPractice => {
508
+ response.data.forEach((restoredPractice) => {
486
509
  localContext.data[DATA_KEY_PRACTICES][date].push({
487
510
  id: restoredPractice.id,
488
511
  duration_seconds: restoredPractice.duration_seconds,
489
- });
490
- });
491
- });
512
+ })
513
+ })
514
+ })
492
515
  }
493
516
 
494
- return response;
517
+ const formattedMeta = await formatPracticeMeta(response?.data)
518
+ const practiceDuration = formattedMeta.reduce(
519
+ (total, practice) => total + (practice.duration || 0),
520
+ 0
521
+ )
522
+
523
+ return { data: formattedMeta, practiceDuration }
495
524
  }
496
525
 
497
526
  /**
@@ -516,50 +545,21 @@ export async function restorePracticeSession(date) {
516
545
  * .then(response => console.log(response))
517
546
  * .catch(error => console.error(error));
518
547
  */
519
- export async function getPracticeSessions(params ={}) {
520
- const {
521
- day,
522
- userId = globalConfig.sessionConfig.userId,
523
- } = params;
524
- const userPracticesIds = await getUserPracticeIds(day, userId);
525
- if (!userPracticesIds.length) return { data: { practices: [], practiceDuration: 0} };
526
-
527
- const meta = await fetchUserPracticeMeta(userPracticesIds, userId);
528
- if (!meta.data.length) return { data: { practices: [], practiceDuration: 0 } };
529
- const practiceDuration = meta.data.reduce((total, practice) => total + (practice.duration_seconds || 0), 0);
530
- const contentIds = meta.data.map(practice => practice.content_id).filter(id => id !== null);
531
-
532
- const contents = await fetchByRailContentIds(contentIds);
533
- const getFormattedType = (type) => {
534
- for (const [key, values] of Object.entries(lessonTypesMapping)) {
535
- if (values.includes(type)) {
536
- return key.replace(/\b\w/g, char => char.toUpperCase());
537
- }
538
- }
539
- return null;
540
- };
548
+ export async function getPracticeSessions(params = {}) {
549
+ const { day, userId = globalConfig.sessionConfig.userId } = params
550
+ const userPracticesIds = await getUserPracticeIds(day, userId)
551
+ if (!userPracticesIds.length) return { data: { practices: [], practiceDuration: 0 } }
541
552
 
542
- const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
553
+ const meta = await fetchUserPracticeMeta(userPracticesIds, userId)
554
+ if (!meta.data.length) return { data: { practices: [], practiceDuration: 0 } }
543
555
 
544
- const formattedMeta = meta.data.map(practice => {
545
- const utcDate = new Date(practice.created_at);
546
- const content = contents.find(c => c.id === practice.content_id) || {};
547
- return {
548
- id: practice.id,
549
- auto: practice.auto,
550
- thumbnail: (practice.content_id)? content.thumbnail : '',
551
- duration: practice.duration_seconds || 0,
552
- content_url: content.url || null,
553
- title: (practice.content_id)? content.title : practice.title,
554
- category_id: practice.category_id,
555
- instrument_id: practice.instrument_id ,
556
- content_type: getFormattedType(content.type || ''),
557
- content_id: practice.content_id || null,
558
- content_brand: content.brand || null,
559
- created_at: convertToTimeZone(utcDate, userTimeZone)
560
- };
561
- });
562
- return { data: { practices: formattedMeta, practiceDuration} };
556
+ const formattedMeta = await formatPracticeMeta(meta.data)
557
+ const practiceDuration = formattedMeta.reduce(
558
+ (total, practice) => total + (practice.duration || 0),
559
+ 0
560
+ )
561
+
562
+ return { data: { practices: formattedMeta, practiceDuration } }
563
563
  }
564
564
 
565
565
  /**
@@ -576,8 +576,8 @@ export async function getPracticeSessions(params ={}) {
576
576
  * .catch(error => console.error("Failed to get notes:", error));
577
577
  */
578
578
  export async function getPracticeNotes(day) {
579
- const notes = await fetchUserPracticeNotes(day);
580
- return { data: notes };
579
+ const notes = await fetchUserPracticeNotes(day)
580
+ return { data: notes }
581
581
  }
582
582
 
583
583
  /**
@@ -594,8 +594,8 @@ export async function getPracticeNotes(day) {
594
594
  * .then(({ data }) => console.log("Recent activity:", data))
595
595
  * .catch(error => console.error("Failed to get recent activity:", error));
596
596
  */
597
- export async function getRecentActivity() {
598
- return { data: recentActivity };
597
+ export async function getRecentActivity({ page = 1, limit = 5, tabName = null } = {}) {
598
+ return await fetchRecentUserActivities({ page, limit, tabName })
599
599
  }
600
600
 
601
601
  /**
@@ -635,218 +635,305 @@ export async function updatePracticeNotes(payload) {
635
635
  }
636
636
 
637
637
  function getStreaksAndMessage(practices) {
638
- let { currentDailyStreak, currentWeeklyStreak, streakMessage } = calculateStreaks(practices, true);
638
+ let { currentDailyStreak, currentWeeklyStreak, streakMessage } = calculateStreaks(practices, true)
639
639
 
640
640
  return {
641
641
  currentDailyStreak,
642
642
  currentWeeklyStreak,
643
643
  streakMessage,
644
- };
644
+ }
645
645
  }
646
646
 
647
647
  async function getUserPracticeIds(day = new Date().toISOString().split('T')[0], userId = null) {
648
- let practices = {};
649
- if(userId !== globalConfig.sessionConfig.userId) {
650
- let data = await fetchUserPractices({userId});
651
- practices = data?.["data"]?.[DATA_KEY_PRACTICES]?? {}
652
- }else {
648
+ let practices = {}
649
+ if (userId !== globalConfig.sessionConfig.userId) {
650
+ let data = await fetchUserPractices({ userId })
651
+ practices = data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
652
+ } else {
653
653
  let data = await userActivityContext.getData()
654
654
  practices = data?.[DATA_KEY_PRACTICES] ?? {}
655
655
  }
656
- let userPracticesIds = [];
657
- Object.keys(practices).forEach(date => {
656
+ let userPracticesIds = []
657
+ Object.keys(practices).forEach((date) => {
658
658
  if (date === day) {
659
- practices[date].forEach(practice => userPracticesIds.push(practice.id));
659
+ practices[date].forEach((practice) => userPracticesIds.push(practice.id))
660
660
  }
661
- });
662
- return userPracticesIds;
661
+ })
662
+ return userPracticesIds
663
663
  }
664
664
 
665
665
  function buildQueryString(ids, paramName = 'practice_ids') {
666
- if (!ids.length) return '';
667
- return '?' + ids.map(id => `${paramName}[]=${id}`).join('&amp;');
666
+ if (!ids.length) return ''
667
+ return '?' + ids.map((id) => `${paramName}[]=${id}`).join('&amp;')
668
668
  }
669
669
 
670
670
  // Helper: Calculate streaks
671
671
  function calculateStreaks(practices, includeStreakMessage = false) {
672
- let currentDailyStreak = 0;
673
- let currentWeeklyStreak = 0;
674
- let lastActiveDay = null;
675
- let streakMessage = '';
672
+ let currentDailyStreak = 0
673
+ let currentWeeklyStreak = 0
674
+ let lastActiveDay = null
675
+ let streakMessage = ''
676
676
 
677
677
  let sortedPracticeDays = Object.keys(practices)
678
- .map(dateStr => {
679
- const [year, month, day] = dateStr.split('-').map(Number);
680
- const newDate = new Date();
681
- newDate.setFullYear(year, month - 1, day);
682
- return newDate;
678
+ .map((dateStr) => {
679
+ const [year, month, day] = dateStr.split('-').map(Number)
680
+ const newDate = new Date()
681
+ newDate.setFullYear(year, month - 1, day)
682
+ return newDate
683
683
  })
684
- .sort((a, b) => a - b);
684
+ .sort((a, b) => a - b)
685
685
  if (sortedPracticeDays.length === 0) {
686
- return { currentDailyStreak: 0, currentWeeklyStreak: 0, streakMessage: streakMessages.startStreak };
686
+ return {
687
+ currentDailyStreak: 0,
688
+ currentWeeklyStreak: 0,
689
+ streakMessage: streakMessages.startStreak,
690
+ }
687
691
  }
688
- lastActiveDay = sortedPracticeDays[sortedPracticeDays.length - 1];
692
+ lastActiveDay = sortedPracticeDays[sortedPracticeDays.length - 1]
689
693
 
690
- let dailyStreak = 0;
691
- let prevDay = null;
694
+ let dailyStreak = 0
695
+ let prevDay = null
692
696
  sortedPracticeDays.forEach((currentDay) => {
693
697
  if (prevDay === null || isNextDay(prevDay, currentDay)) {
694
- dailyStreak++;
698
+ dailyStreak++
695
699
  } else {
696
- dailyStreak = 1;
700
+ dailyStreak = 1
697
701
  }
698
- prevDay = currentDay;
699
- });
700
- currentDailyStreak = dailyStreak;
702
+ prevDay = currentDay
703
+ })
704
+ currentDailyStreak = dailyStreak
701
705
 
702
706
  // Weekly streak calculation
703
- let weekNumbers = new Set(sortedPracticeDays.map(date => getWeekNumber(date)));
704
- let weeklyStreak = 0;
705
- let lastWeek = null;
706
- [...weekNumbers].sort((a, b) => b - a).forEach(week => {
707
- if (lastWeek === null || week === lastWeek - 1) {
708
- weeklyStreak++;
709
- } else {
710
- return;
711
- }
712
- lastWeek = week;
713
- });
714
- currentWeeklyStreak = weeklyStreak;
707
+ let weekNumbers = new Set(sortedPracticeDays.map((date) => getWeekNumber(date)))
708
+ let weeklyStreak = 0
709
+ let lastWeek = null
710
+ ;[...weekNumbers]
711
+ .sort((a, b) => b - a)
712
+ .forEach((week) => {
713
+ if (lastWeek === null || week === lastWeek - 1) {
714
+ weeklyStreak++
715
+ } else {
716
+ return
717
+ }
718
+ lastWeek = week
719
+ })
720
+ currentWeeklyStreak = weeklyStreak
715
721
 
716
722
  // Calculate streak message only if includeStreakMessage is true
717
723
  if (includeStreakMessage) {
718
- let today = new Date();
719
- let yesterday = new Date(today);
720
- yesterday.setDate(today.getDate() - 1);
721
-
722
- let currentWeekStart = getMonday(today);
723
- let lastWeekStart = new Date(currentWeekStart);
724
- lastWeekStart.setDate(currentWeekStart.getDate() - 7);
725
-
726
- let hasYesterdayPractice = sortedPracticeDays.some(date =>
727
- isSameDate(date, yesterday)
728
- );
729
- let hasCurrentWeekPractice = sortedPracticeDays.some(date => date >= currentWeekStart);
730
- let hasCurrentWeekPreviousPractice = sortedPracticeDays.some(date => date >= currentWeekStart &amp;&amp; date &lt; today);
731
- let hasLastWeekPractice = sortedPracticeDays.some(date => date >= lastWeekStart &amp;&amp; date &lt; currentWeekStart);
732
- let hasOlderPractice = sortedPracticeDays.some(date => date &lt; lastWeekStart );
724
+ let today = new Date()
725
+ let yesterday = new Date(today)
726
+ yesterday.setDate(today.getDate() - 1)
727
+
728
+ let currentWeekStart = getMonday(today)
729
+ let lastWeekStart = new Date(currentWeekStart)
730
+ lastWeekStart.setDate(currentWeekStart.getDate() - 7)
731
+
732
+ let hasYesterdayPractice = sortedPracticeDays.some((date) => isSameDate(date, yesterday))
733
+ let hasCurrentWeekPractice = sortedPracticeDays.some((date) => date >= currentWeekStart)
734
+ let hasCurrentWeekPreviousPractice = sortedPracticeDays.some(
735
+ (date) => date >= currentWeekStart &amp;&amp; date &lt; today
736
+ )
737
+ let hasLastWeekPractice = sortedPracticeDays.some(
738
+ (date) => date >= lastWeekStart &amp;&amp; date &lt; currentWeekStart
739
+ )
740
+ let hasOlderPractice = sortedPracticeDays.some((date) => date &lt; lastWeekStart)
733
741
 
734
742
  if (isSameDate(lastActiveDay, today)) {
735
743
  if (hasYesterdayPractice) {
736
- streakMessage = streakMessages.dailyStreak(currentDailyStreak);
744
+ streakMessage = streakMessages.dailyStreak(currentDailyStreak)
737
745
  } else if (hasCurrentWeekPreviousPractice) {
738
- streakMessage = streakMessages.weeklyStreak(currentWeeklyStreak);
746
+ streakMessage = streakMessages.weeklyStreak(currentWeeklyStreak)
739
747
  } else if (hasLastWeekPractice) {
740
- streakMessage = streakMessages.greatJobWeeklyStreak(currentWeeklyStreak);
748
+ streakMessage = streakMessages.greatJobWeeklyStreak(currentWeeklyStreak)
741
749
  } else {
742
- streakMessage = streakMessages.dailyStreakShort(currentDailyStreak);
750
+ streakMessage = streakMessages.dailyStreakShort(currentDailyStreak)
743
751
  }
744
752
  } else {
745
- if ((hasYesterdayPractice &amp;&amp; currentDailyStreak >= 2) || (hasYesterdayPractice &amp;&amp; sortedPracticeDays.length == 1)
746
- || (hasYesterdayPractice &amp;&amp; !hasLastWeekPractice &amp;&amp; hasOlderPractice)){
747
- streakMessage = streakMessages.dailyStreakReminder(currentDailyStreak);
753
+ if (
754
+ (hasYesterdayPractice &amp;&amp; currentDailyStreak >= 2) ||
755
+ (hasYesterdayPractice &amp;&amp; sortedPracticeDays.length == 1) ||
756
+ (hasYesterdayPractice &amp;&amp; !hasLastWeekPractice &amp;&amp; hasOlderPractice)
757
+ ) {
758
+ streakMessage = streakMessages.dailyStreakReminder(currentDailyStreak)
748
759
  } else if (hasCurrentWeekPractice) {
749
- streakMessage = streakMessages.weeklyStreakKeepUp(currentWeeklyStreak);
760
+ streakMessage = streakMessages.weeklyStreakKeepUp(currentWeeklyStreak)
750
761
  } else if (hasLastWeekPractice) {
751
- streakMessage = streakMessages.weeklyStreakReminder(currentWeeklyStreak);
762
+ streakMessage = streakMessages.weeklyStreakReminder(currentWeeklyStreak)
752
763
  } else {
753
- streakMessage = streakMessages.restartStreak;
764
+ streakMessage = streakMessages.restartStreak
754
765
  }
755
766
  }
756
767
  }
757
768
 
758
- return { currentDailyStreak, currentWeeklyStreak, streakMessage };
769
+ return { currentDailyStreak, currentWeeklyStreak, streakMessage }
759
770
  }
760
771
 
761
772
  /**
762
773
  * Calculates the longest daily, weekly streaks and totalPracticeSeconds from user practice dates.
763
774
  * @returns {{ longestDailyStreak: number, longestWeeklyStreak: number, totalPracticeSeconds:number }}
764
775
  */
765
- export async function calculateLongestStreaks() {
766
- let data = await userActivityContext.getData()
767
- let practices = data?.[DATA_KEY_PRACTICES] ?? {}
768
- let totalPracticeSeconds = 0;
776
+ export async function calculateLongestStreaks(userId = globalConfig.sessionConfig.userId) {
777
+ let practices = await getUserPractices(userId)
778
+ let totalPracticeSeconds = 0
769
779
  // Calculate total practice duration
770
780
  for (const date in practices) {
771
781
  for (const entry of practices[date]) {
772
- totalPracticeSeconds += entry.duration_seconds;
782
+ totalPracticeSeconds += entry.duration_seconds
773
783
  }
774
784
  }
775
785
 
776
786
  let practiceDates = Object.keys(practices)
777
- .map(dateStr => {
778
- const [y, m, d] = dateStr.split('-').map(Number);
779
- const newDate = new Date();
780
- newDate.setFullYear(y, m - 1, d);
781
- return newDate;
787
+ .map((dateStr) => {
788
+ const [y, m, d] = dateStr.split('-').map(Number)
789
+ const newDate = new Date()
790
+ newDate.setFullYear(y, m - 1, d)
791
+ return newDate
782
792
  })
783
- .sort((a, b) => a - b);
793
+ .sort((a, b) => a - b)
784
794
 
785
795
  if (!practiceDates || practiceDates.length === 0) {
786
- return {longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0};
796
+ return { longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0 }
787
797
  }
788
798
 
789
799
  // Normalize to Date objects
790
800
  const normalizedDates = [
791
- ...new Set(practiceDates.map(d => {
792
- const date = new Date(d);
793
- date.setHours(0, 0, 0, 0);
794
- return date.getTime();
795
- }))
796
- ].sort((a, b) => a - b);
801
+ ...new Set(
802
+ practiceDates.map((d) => {
803
+ const date = new Date(d)
804
+ date.setHours(0, 0, 0, 0)
805
+ return date.getTime()
806
+ })
807
+ ),
808
+ ].sort((a, b) => a - b)
797
809
 
798
810
  // ----- Daily Streak -----
799
- let longestDailyStreak = 1;
800
- let currentDailyStreak = 1;
811
+ let longestDailyStreak = 1
812
+ let currentDailyStreak = 1
801
813
  for (let i = 1; i &lt; normalizedDates.length; i++) {
802
- const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24);
814
+ const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24)
803
815
  if (diffInDays === 1) {
804
- currentDailyStreak++;
805
- longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak);
816
+ currentDailyStreak++
817
+ longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak)
806
818
  } else {
807
- currentDailyStreak = 1;
819
+ currentDailyStreak = 1
808
820
  }
809
821
  }
810
822
 
811
823
  // ----- Weekly Streak -----
812
824
  const weekStartDates = [
813
- ...new Set(normalizedDates.map(ts => {
814
- const d = new Date(ts);
815
- const day = d.getDay();
816
- const diff = d.getDate() - day + (day === 0 ? -6 : 1); // adjust to Monday
817
- d.setDate(diff);
818
- return d.getTime(); // timestamp for Monday
819
- }))
820
- ].sort((a, b) => a - b);
821
-
822
- let longestWeeklyStreak = 1;
823
- let currentWeeklyStreak = 1;
825
+ ...new Set(
826
+ normalizedDates.map((ts) => {
827
+ const d = new Date(ts)
828
+ const day = d.getDay()
829
+ const diff = d.getDate() - day + (day === 0 ? -6 : 1) // adjust to Monday
830
+ d.setDate(diff)
831
+ return d.getTime() // timestamp for Monday
832
+ })
833
+ ),
834
+ ].sort((a, b) => a - b)
835
+
836
+ let longestWeeklyStreak = 1
837
+ let currentWeeklyStreak = 1
824
838
 
825
839
  for (let i = 1; i &lt; weekStartDates.length; i++) {
826
- const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7);
840
+ const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7)
827
841
  if (diffInWeeks === 1) {
828
- currentWeeklyStreak++;
829
- longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak);
842
+ currentWeeklyStreak++
843
+ longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak)
830
844
  } else {
831
- currentWeeklyStreak = 1;
845
+ currentWeeklyStreak = 1
832
846
  }
833
847
  }
834
848
 
835
849
  return {
836
850
  longestDailyStreak,
837
851
  longestWeeklyStreak,
838
- totalPracticeSeconds
839
- };
852
+ totalPracticeSeconds,
853
+ }
840
854
  }
841
855
 
856
+ async function formatPracticeMeta(practices) {
857
+ const contentIds = practices.map((p) => p.content_id).filter((id) => id !== null)
858
+ const contents = await fetchByRailContentIds(contentIds)
842
859
 
860
+ const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
843
861
 
862
+ return practices.map((practice) => {
863
+ const utcDate = new Date(practice.created_at)
864
+ const content = contents.find((c) => c.id === practice.content_id) || {}
844
865
 
866
+ return {
867
+ id: practice.id,
868
+ auto: practice.auto,
869
+ thumbnail: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
870
+ thumbnail_url: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
871
+ duration: practice.duration_seconds || 0,
872
+ duration_seconds: practice.duration_seconds || 0,
873
+ content_url: content.url || null,
874
+ title: practice.content_id ? content.title : practice.title,
875
+ category_id: practice.category_id,
876
+ instrument_id: practice.instrument_id,
877
+ content_type: getFormattedType(content.type || ''),
878
+ content_id: practice.content_id || null,
879
+ content_brand: content.brand || null,
880
+ created_at: convertToTimeZone(utcDate, userTimeZone),
881
+ }
882
+ })
883
+ }
845
884
 
885
+ export function getFormattedType(type) {
886
+ for (const [key, values] of Object.entries(lessonTypesMapping)) {
887
+ if (values.includes(type)) {
888
+ return key.replace(/\b\w/g, (char) => char.toUpperCase())
889
+ }
890
+ }
891
+ return null
892
+ }
846
893
 
894
+ /**
895
+ * Records a new user activity in the system.
896
+ *
897
+ * @param {Object} payload - The data representing the user activity.
898
+ * @param {number} payload.user_id - The ID of the user.
899
+ * @param {string} payload.action - The type of action (e.g., 'start', 'complete', 'comment', etc.).
900
+ * @param {string} payload.brand - The brand associated with the activity.
901
+ * @param {string} payload.type - The content type (e.g., 'lesson', 'song', etc.).
902
+ * @param {number} payload.content_id - The ID of the related content.
903
+ * @param {string} payload.date - The date of the activity (ISO format).
904
+ * @returns {Promise&lt;Object>} - A promise that resolves to the API response after recording the activity.
905
+ *
906
+ * @example
907
+ * recordUserActivity({
908
+ * user_id: 123,
909
+ * action: 'start',
910
+ * brand: 'pianote',
911
+ * type: 'lesson',
912
+ * content_id: 4561,
913
+ * date: '2025-05-15'
914
+ * }).then(response => console.log(response))
915
+ * .catch(error => console.error(error));
916
+ */
917
+ export async function recordUserActivity(payload) {
918
+ const url = `/api/user-management-system/v1/activities`
919
+ return await fetchHandler(url, 'POST', null, payload)
920
+ }
847
921
 
848
-
849
-
922
+ /**
923
+ * Deletes a specific user activity by its ID.
924
+ *
925
+ * @param {number|string} id - The ID of the user activity to delete.
926
+ * @returns {Promise&lt;Object>} - A promise that resolves to the API response after deletion.
927
+ *
928
+ * @example
929
+ * deleteUserActivity(789)
930
+ * .then(response => console.log('Deleted:', response))
931
+ * .catch(error => console.error(error));
932
+ */
933
+ export async function deleteUserActivity(id) {
934
+ const url = `/api/user-management-system/v1/activities/${id}`
935
+ return await fetchHandler(url, 'DELETE')
936
+ }
850
937
  </code></pre>
851
938
  </article>
852
939
  </section>
@@ -861,7 +948,7 @@ export async function calculateLongestStreaks() {
861
948
  <br class="clear">
862
949
 
863
950
  <footer>
864
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Wed May 14 2025 19:23:31 GMT+0000 (Coordinated Universal Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
951
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Wed May 21 2025 14:49:14 GMT+0000 (Coordinated Universal Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
865
952
  </footer>
866
953
 
867
954
  <script>prettyPrint();</script>