musora-content-services 2.5.1 → 2.6.0

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 (48) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/docs/ContentOrganization.html +2 -2
  3. package/docs/Gamification.html +2 -2
  4. package/docs/UserManagementSystem.html +2 -2
  5. package/docs/api_types.js.html +2 -2
  6. package/docs/config.js.html +2 -2
  7. package/docs/content-org_content-org.js.html +2 -2
  8. package/docs/content-org_playlists-types.js.html +2 -2
  9. package/docs/content-org_playlists.js.html +2 -2
  10. package/docs/content.js.html +2 -4
  11. package/docs/gamification_awards.js.html +2 -2
  12. package/docs/gamification_gamification.js.html +2 -2
  13. package/docs/gamification_types.js.html +2 -2
  14. package/docs/global.html +448 -2
  15. package/docs/index.html +2 -2
  16. package/docs/module-Awards.html +2 -2
  17. package/docs/module-Config.html +2 -2
  18. package/docs/module-Content-Services-V2.html +8 -8
  19. package/docs/module-Interests.html +2 -2
  20. package/docs/module-Permissions.html +2 -2
  21. package/docs/module-Playlists.html +2 -2
  22. package/docs/module-Railcontent-Services.html +754 -44
  23. package/docs/module-Sanity-Services.html +2 -2
  24. package/docs/module-Sessions.html +2 -2
  25. package/docs/module-User-Activity.html +525 -16
  26. package/docs/module-UserManagement.html +2 -2
  27. package/docs/module-UserProfile.html +266 -0
  28. package/docs/railcontent.js.html +41 -2
  29. package/docs/sanity.js.html +2 -2
  30. package/docs/userActivity.js.html +348 -263
  31. package/docs/user_interests.js.html +2 -2
  32. package/docs/user_management.js.html +2 -2
  33. package/docs/user_permissions.js.html +2 -2
  34. package/docs/user_profile.js.html +105 -0
  35. package/docs/user_sessions.js.html +2 -2
  36. package/docs/user_types.js.html +22 -2
  37. package/docs/user_user-management-system.js.html +2 -2
  38. package/package.json +1 -1
  39. package/src/contentMetaData.js +14 -0
  40. package/src/index.d.ts +15 -0
  41. package/src/index.js +15 -0
  42. package/src/services/content.js +0 -2
  43. package/src/services/railcontent.js +39 -0
  44. package/src/services/user/profile.js +33 -0
  45. package/src/services/user/types.js +20 -0
  46. package/src/services/userActivity.js +346 -261
  47. package/test/content.test.js +8 -6
  48. package/test/sanityQueryService.test.js +6 -0
@@ -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,13 @@
49
49
  * @module User-Activity
50
50
  */
51
51
 
52
- import {fetchUserPractices, logUserPractice, fetchUserPracticeMeta, fetchUserPracticeNotes, fetchHandler} from './railcontent'
52
+ import {fetchUserPractices, logUserPractice, fetchUserPracticeMeta, fetchUserPracticeNotes, fetchHandler, fetchRecentUserActivities} from './railcontent'
53
53
  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
- ]
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
+
66
59
 
67
60
  const DATA_KEY_PRACTICES = 'practices'
68
61
  const DATA_KEY_LAST_UPDATED_TIME = 'u'
@@ -70,25 +63,43 @@ const DATA_KEY_LAST_UPDATED_TIME = 'u'
70
63
  const DAYS = ['M', 'T', 'W', 'T', 'F', 'S', 'S']
71
64
 
72
65
  const streakMessages = {
73
- startStreak: "Start your streak by taking any lesson!",
74
- restartStreak: "Restart your streak by taking any lesson!",
66
+ startStreak: 'Start your streak by taking any lesson!',
67
+ restartStreak: 'Restart your streak by taking any lesson!',
75
68
 
76
69
  // 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!`,
70
+ dailyStreak: (streak) =>
71
+ `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak! Way to keep it going!`,
72
+ dailyStreakShort: (streak) =>
73
+ `Nice! You have ${getIndefiniteArticle(streak)} ${streak} day streak!`,
74
+ weeklyStreak: (streak) =>
75
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep up the momentum!`,
76
+ greatJobWeeklyStreak: (streak) =>
77
+ `Great job! You have ${getIndefiniteArticle(streak)} ${streak} week streak! Way to keep it going!`,
81
78
 
82
79
  // 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
- };
80
+ dailyStreakReminder: (streak) =>
81
+ `You have ${getIndefiniteArticle(streak)} ${streak} day streak! Keep it going with any lesson or song!`,
82
+ weeklyStreakKeepUp: (streak) =>
83
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep up the momentum!`,
84
+ weeklyStreakReminder: (streak) =>
85
+ `You have ${getIndefiniteArticle(streak)} ${streak} week streak! Keep it going with any lesson or song!`,
86
+ }
87
87
 
88
88
  function getIndefiniteArticle(streak) {
89
- return streak === 8 || (streak >= 80 &amp;&amp; streak &lt;= 89) || (streak >= 800 &amp;&amp; streak &lt;= 899) ? 'an' : 'a'
89
+ return streak === 8 || (streak >= 80 &amp;&amp; streak &lt;= 89) || (streak >= 800 &amp;&amp; streak &lt;= 899)
90
+ ? 'an'
91
+ : 'a'
90
92
  }
91
93
 
94
+ export async function getUserPractices(userId = globalConfig.sessionConfig.userId) {
95
+ if (userId !== globalConfig.sessionConfig.userId) {
96
+ let data = await fetchUserPractices({ userId })
97
+ return data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
98
+ } else {
99
+ let data = await userActivityContext.getData()
100
+ return data?.[DATA_KEY_PRACTICES] ?? {}
101
+ }
102
+ }
92
103
 
93
104
  export let userActivityContext = new DataContext(UserActivityVersionKey, fetchUserPractices)
94
105
 
@@ -107,24 +118,24 @@ export async function getUserWeeklyStats() {
107
118
  let data = await userActivityContext.getData()
108
119
  let practices = data?.[DATA_KEY_PRACTICES] ?? {}
109
120
  let sortedPracticeDays = Object.keys(practices)
110
- .map(date => new Date(date))
111
- .sort((a, b) => b - a);
121
+ .map((date) => new Date(date))
122
+ .sort((a, b) => b - a)
112
123
 
113
- let today = new Date();
114
- today.setHours(0, 0, 0, 0);
124
+ let today = new Date()
125
+ today.setHours(0, 0, 0, 0)
115
126
  let startOfWeek = getMonday(today) // Get last Monday
116
127
  let dailyStats = []
117
128
 
118
129
  for (let i = 0; i &lt; 7; i++) {
119
130
  let day = new Date(startOfWeek)
120
131
  day.setDate(startOfWeek.getDate() + i)
121
- let hasPractice = sortedPracticeDays.some(practiceDate => isSameDate(practiceDate, day));
132
+ let hasPractice = sortedPracticeDays.some((practiceDate) => isSameDate(practiceDate, day))
122
133
  let isActive = isSameDate(today, day)
123
- let type = (hasPractice ? 'tracked' : (isActive ? 'active' : 'none'))
134
+ let type = hasPractice ? 'tracked' : isActive ? 'active' : 'none'
124
135
  dailyStats.push({ key: i, label: DAYS[i], isActive, inStreak: hasPractice, type })
125
136
  }
126
137
 
127
- let { streakMessage } = getStreaksAndMessage(practices);
138
+ let { streakMessage } = getStreaksAndMessage(practices)
128
139
 
129
140
  return { data: { dailyActiveStats: dailyStats, streakMessage, practices } }
130
141
  }
@@ -159,27 +170,20 @@ export async function getUserWeeklyStats() {
159
170
  * // Get stats for another user
160
171
  * getUserMonthlyStats({ userId: 123 }).then(console.log);
161
172
  */
162
- export async function getUserMonthlyStats( params = {}) {
163
- const now = new Date();
173
+ export async function getUserMonthlyStats(params = {}) {
174
+ const now = new Date()
164
175
  const {
165
176
  year = now.getFullYear(),
166
177
  month = now.getMonth(),
167
178
  day = 1,
168
179
  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
- }
180
+ } = params
181
+ let practices = await getUserPractices(userId)
178
182
 
179
183
  // Get the first day of the specified month and the number of days in that month
180
184
  let firstDayOfMonth = new Date(year, month, 1)
181
185
  let today = new Date()
182
- today.setHours(0, 0, 0, 0);
186
+ today.setHours(0, 0, 0, 0)
183
187
 
184
188
  let startOfGrid = getMonday(firstDayOfMonth)
185
189
 
@@ -203,7 +207,7 @@ export async function getUserMonthlyStats( params = {}) {
203
207
  endOfMonth.setDate(endOfMonth.getDate() + 1)
204
208
  }
205
209
 
206
- let daysInMonth = Math.ceil((endOfMonth - startOfGrid) / (1000 * 60 * 60 * 24)) + 1;
210
+ let daysInMonth = Math.ceil((endOfMonth - startOfGrid) / (1000 * 60 * 60 * 24)) + 1
207
211
 
208
212
  let dailyStats = []
209
213
  let practiceDuration = 0
@@ -213,26 +217,26 @@ export async function getUserMonthlyStats( params = {}) {
213
217
  for (let i = 0; i &lt; daysInMonth; i++) {
214
218
  let day = new Date(startOfGrid)
215
219
  day.setDate(startOfGrid.getDate() + i)
216
- let dayKey = `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`;
220
+ let dayKey = `${day.getFullYear()}-${String(day.getMonth() + 1).padStart(2, '0')}-${String(day.getDate()).padStart(2, '0')}`
217
221
 
218
222
  // Check if the user has activity for the day
219
223
  let dayActivity = practices[dayKey] ?? null
220
224
  let weekKey = getWeekNumber(day)
221
225
 
222
226
  if (!weeklyStats[weekKey]) {
223
- weeklyStats[weekKey] = { key: weekKey, inStreak: false };
227
+ weeklyStats[weekKey] = { key: weekKey, inStreak: false }
224
228
  }
225
229
 
226
230
  if (dayActivity !== null) {
227
231
  practiceDuration += dayActivity.reduce((sum, entry) => sum + entry.duration_seconds, 0)
228
- daysPracticed++;
232
+ daysPracticed++
229
233
  }
230
234
 
231
235
  let isActive = isSameDate(today, day)
232
- let type = ((dayActivity !== null) ? 'tracked' : (isActive ? 'active' : 'none'))
233
- let isInStreak = dayActivity !== null;
236
+ let type = dayActivity !== null ? 'tracked' : isActive ? 'active' : 'none'
237
+ let isInStreak = dayActivity !== null
234
238
  if (isInStreak) {
235
- weeklyStats[weekKey].inStreak = true;
239
+ weeklyStats[weekKey].inStreak = true
236
240
  }
237
241
 
238
242
  dailyStats.push({
@@ -258,16 +262,17 @@ export async function getUserMonthlyStats( params = {}) {
258
262
  return obj
259
263
  }, {})
260
264
 
261
- let { currentDailyStreak, currentWeeklyStreak } = calculateStreaks(filteredPractices);
265
+ let { currentDailyStreak, currentWeeklyStreak } = calculateStreaks(filteredPractices)
262
266
 
263
- return { data: {
264
- dailyActiveStats: dailyStats,
267
+ return {
268
+ data: {
269
+ dailyActiveStats: dailyStats,
265
270
  weeklyActiveStats: Object.values(weeklyStats),
266
271
  practiceDuration,
267
272
  currentDailyStreak,
268
273
  currentWeeklyStreak,
269
274
  daysPracticed,
270
- }
275
+ },
271
276
  }
272
277
  }
273
278
 
@@ -302,37 +307,39 @@ export async function getUserMonthlyStats( params = {}) {
302
307
  * .catch(error => console.error(error));
303
308
  */
304
309
  export async function recordUserPractice(practiceDetails) {
305
- practiceDetails.auto = 0;
310
+ practiceDetails.auto = 0
306
311
  if (practiceDetails.content_id) {
307
- practiceDetails.auto = 1;
312
+ practiceDetails.auto = 1
308
313
  }
309
314
 
310
315
  await userActivityContext.update(
311
316
  async function (localContext) {
312
- let userData = localContext.data ?? { [DATA_KEY_PRACTICES]: {} };
313
- localContext.data = userData;
317
+ let userData = localContext.data ?? { [DATA_KEY_PRACTICES]: {} }
318
+ localContext.data = userData
314
319
  },
315
320
  async function () {
316
- const response = await logUserPractice(practiceDetails);
321
+ const response = await logUserPractice(practiceDetails)
317
322
  if (response) {
318
323
  await userActivityContext.updateLocal(async function (localContext) {
319
324
  const newPractices = response.data ?? []
320
- newPractices.forEach(newPractice => {
321
- const { date } = newPractice;
325
+ newPractices.forEach((newPractice) => {
326
+ const { date } = newPractice
322
327
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
323
- localContext.data[DATA_KEY_PRACTICES][date] = [];
328
+ localContext.data[DATA_KEY_PRACTICES][date] = []
324
329
  }
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
- });
330
+ localContext.data[DATA_KEY_PRACTICES][date][DATA_KEY_LAST_UPDATED_TIME] = Math.round(
331
+ new Date().getTime() / 1000
332
+ )
333
+ localContext.data[DATA_KEY_PRACTICES][date].push({
334
+ id: newPractice.id,
335
+ duration_seconds: newPractice.duration_seconds, // Add the new practice for this date
336
+ })
337
+ })
338
+ })
332
339
  }
333
- return response;
340
+ return response
334
341
  }
335
- );
342
+ )
336
343
  }
337
344
  /**
338
345
  * Updates a user's practice session with new details and syncs the changes remotely.
@@ -375,21 +382,21 @@ export async function updateUserPractice(id, practiceDetails) {
375
382
  * .catch(error => console.error(error));
376
383
  */
377
384
  export async function removeUserPractice(id) {
378
- let url = `/api/user/practices/v1/practices${buildQueryString([id])}`;
385
+ let url = `/api/user/practices/v1/practices${buildQueryString([id])}`
379
386
  await userActivityContext.update(
380
387
  async function (localContext) {
381
388
  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
- });
389
+ Object.keys(localContext.data[DATA_KEY_PRACTICES]).forEach((date) => {
390
+ localContext.data[DATA_KEY_PRACTICES][date] = localContext.data[DATA_KEY_PRACTICES][
391
+ date
392
+ ].filter((practice) => practice.id !== id)
393
+ })
387
394
  }
388
395
  },
389
396
  async function () {
390
- return await fetchHandler(url, 'delete');
397
+ return await fetchHandler(url, 'delete')
391
398
  }
392
- );
399
+ )
393
400
  }
394
401
 
395
402
  /**
@@ -405,22 +412,32 @@ export async function removeUserPractice(id) {
405
412
  * .catch(error => console.error(error));
406
413
  */
407
414
  export async function restoreUserPractice(id) {
408
- let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`;
409
- const response = await fetchHandler(url, 'put');
415
+ let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`
416
+ const response = await fetchHandler(url, 'put')
410
417
  if (response?.data) {
411
418
  await userActivityContext.updateLocal(async function (localContext) {
412
- const restoredPractice = response.data;
413
- const { date } = restoredPractice;
419
+ const restoredPractice = response.data
420
+ const { date } = restoredPractice
414
421
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
415
- localContext.data[DATA_KEY_PRACTICES][date] = [];
422
+ localContext.data[DATA_KEY_PRACTICES][date] = []
416
423
  }
417
424
  localContext.data[DATA_KEY_PRACTICES][date].push({
418
425
  id: restoredPractice.id,
419
426
  duration_seconds: restoredPractice.duration_seconds,
420
- });
421
- });
427
+ })
428
+ })
429
+ }
430
+ const formattedMeta = await formatPracticeMeta(response.data)
431
+ const practiceDuration = formattedMeta.reduce(
432
+ (total, practice) => total + (practice.duration || 0),
433
+ 0
434
+ )
435
+ return {
436
+ data: formattedMeta,
437
+ message: response.message,
438
+ version: response.version,
439
+ practiceDuration,
422
440
  }
423
- return response;
424
441
  }
425
442
 
426
443
  /**
@@ -440,20 +457,20 @@ export async function restoreUserPractice(id) {
440
457
  * .catch(error => console.error("Delete failed:", error));
441
458
  */
442
459
  export async function deletePracticeSession(day) {
443
- const userPracticesIds = await getUserPracticeIds(day);
444
- if (!userPracticesIds.length) return [];
460
+ const userPracticesIds = await getUserPracticeIds(day)
461
+ if (!userPracticesIds.length) return []
445
462
 
446
- const url = `/api/user/practices/v1/practices${buildQueryString(userPracticesIds)}`;
463
+ const url = `/api/user/practices/v1/practices${buildQueryString(userPracticesIds)}`
447
464
  await userActivityContext.update(
448
465
  async function (localContext) {
449
466
  if (localContext.data?.[DATA_KEY_PRACTICES]?.[day]) {
450
- delete localContext.data[DATA_KEY_PRACTICES][day];
467
+ delete localContext.data[DATA_KEY_PRACTICES][day]
451
468
  }
452
469
  },
453
470
  async function () {
454
- return await fetchHandler(url, 'DELETE', null);
471
+ return await fetchHandler(url, 'DELETE', null)
455
472
  }
456
- );
473
+ )
457
474
  }
458
475
 
459
476
  /**
@@ -473,25 +490,31 @@ export async function deletePracticeSession(day) {
473
490
  * .catch(error => console.error("Restore failed:", error));
474
491
  */
475
492
  export async function restorePracticeSession(date) {
476
- const url = `/api/user/practices/v1/practices/restore?date=${date}`;
477
- const response = await fetchHandler(url, 'PUT', null);
493
+ const url = `/api/user/practices/v1/practices/restore?date=${date}`
494
+ const response = await fetchHandler(url, 'PUT', null)
478
495
 
479
496
  if (response?.data) {
480
497
  await userActivityContext.updateLocal(async function (localContext) {
481
498
  if (!localContext.data[DATA_KEY_PRACTICES][date]) {
482
- localContext.data[DATA_KEY_PRACTICES][date] = [];
499
+ localContext.data[DATA_KEY_PRACTICES][date] = []
483
500
  }
484
501
 
485
- response.data.forEach(restoredPractice => {
502
+ response.data.forEach((restoredPractice) => {
486
503
  localContext.data[DATA_KEY_PRACTICES][date].push({
487
504
  id: restoredPractice.id,
488
505
  duration_seconds: restoredPractice.duration_seconds,
489
- });
490
- });
491
- });
506
+ })
507
+ })
508
+ })
492
509
  }
493
510
 
494
- return response;
511
+ const formattedMeta = await formatPracticeMeta(response?.data)
512
+ const practiceDuration = formattedMeta.reduce(
513
+ (total, practice) => total + (practice.duration || 0),
514
+ 0
515
+ )
516
+
517
+ return { data: formattedMeta, practiceDuration }
495
518
  }
496
519
 
497
520
  /**
@@ -516,50 +539,21 @@ export async function restorePracticeSession(date) {
516
539
  * .then(response => console.log(response))
517
540
  * .catch(error => console.error(error));
518
541
  */
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
- };
542
+ export async function getPracticeSessions(params = {}) {
543
+ const { day, userId = globalConfig.sessionConfig.userId } = params
544
+ const userPracticesIds = await getUserPracticeIds(day, userId)
545
+ if (!userPracticesIds.length) return { data: { practices: [], practiceDuration: 0 } }
541
546
 
542
- const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
547
+ const meta = await fetchUserPracticeMeta(userPracticesIds, userId)
548
+ if (!meta.data.length) return { data: { practices: [], practiceDuration: 0 } }
543
549
 
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} };
550
+ const formattedMeta = await formatPracticeMeta(meta.data)
551
+ const practiceDuration = formattedMeta.reduce(
552
+ (total, practice) => total + (practice.duration || 0),
553
+ 0
554
+ )
555
+
556
+ return { data: { practices: formattedMeta, practiceDuration } }
563
557
  }
564
558
 
565
559
  /**
@@ -576,8 +570,8 @@ export async function getPracticeSessions(params ={}) {
576
570
  * .catch(error => console.error("Failed to get notes:", error));
577
571
  */
578
572
  export async function getPracticeNotes(day) {
579
- const notes = await fetchUserPracticeNotes(day);
580
- return { data: notes };
573
+ const notes = await fetchUserPracticeNotes(day)
574
+ return { data: notes }
581
575
  }
582
576
 
583
577
  /**
@@ -594,8 +588,12 @@ export async function getPracticeNotes(day) {
594
588
  * .then(({ data }) => console.log("Recent activity:", data))
595
589
  * .catch(error => console.error("Failed to get recent activity:", error));
596
590
  */
597
- export async function getRecentActivity() {
598
- return { data: recentActivity };
591
+ export async function getRecentActivity({
592
+ page = 1,
593
+ limit = 5,
594
+ tabName = null
595
+ } = {}) {
596
+ return await fetchRecentUserActivities({ page, limit, tabName });
599
597
  }
600
598
 
601
599
  /**
@@ -635,218 +633,305 @@ export async function updatePracticeNotes(payload) {
635
633
  }
636
634
 
637
635
  function getStreaksAndMessage(practices) {
638
- let { currentDailyStreak, currentWeeklyStreak, streakMessage } = calculateStreaks(practices, true);
636
+ let { currentDailyStreak, currentWeeklyStreak, streakMessage } = calculateStreaks(practices, true)
639
637
 
640
638
  return {
641
639
  currentDailyStreak,
642
640
  currentWeeklyStreak,
643
641
  streakMessage,
644
- };
642
+ }
645
643
  }
646
644
 
647
645
  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 {
646
+ let practices = {}
647
+ if (userId !== globalConfig.sessionConfig.userId) {
648
+ let data = await fetchUserPractices({ userId })
649
+ practices = data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
650
+ } else {
653
651
  let data = await userActivityContext.getData()
654
652
  practices = data?.[DATA_KEY_PRACTICES] ?? {}
655
653
  }
656
- let userPracticesIds = [];
657
- Object.keys(practices).forEach(date => {
654
+ let userPracticesIds = []
655
+ Object.keys(practices).forEach((date) => {
658
656
  if (date === day) {
659
- practices[date].forEach(practice => userPracticesIds.push(practice.id));
657
+ practices[date].forEach((practice) => userPracticesIds.push(practice.id))
660
658
  }
661
- });
662
- return userPracticesIds;
659
+ })
660
+ return userPracticesIds
663
661
  }
664
662
 
665
663
  function buildQueryString(ids, paramName = 'practice_ids') {
666
- if (!ids.length) return '';
667
- return '?' + ids.map(id => `${paramName}[]=${id}`).join('&amp;');
664
+ if (!ids.length) return ''
665
+ return '?' + ids.map((id) => `${paramName}[]=${id}`).join('&amp;')
668
666
  }
669
667
 
670
668
  // Helper: Calculate streaks
671
669
  function calculateStreaks(practices, includeStreakMessage = false) {
672
- let currentDailyStreak = 0;
673
- let currentWeeklyStreak = 0;
674
- let lastActiveDay = null;
675
- let streakMessage = '';
670
+ let currentDailyStreak = 0
671
+ let currentWeeklyStreak = 0
672
+ let lastActiveDay = null
673
+ let streakMessage = ''
676
674
 
677
675
  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;
676
+ .map((dateStr) => {
677
+ const [year, month, day] = dateStr.split('-').map(Number)
678
+ const newDate = new Date()
679
+ newDate.setFullYear(year, month - 1, day)
680
+ return newDate
683
681
  })
684
- .sort((a, b) => a - b);
682
+ .sort((a, b) => a - b)
685
683
  if (sortedPracticeDays.length === 0) {
686
- return { currentDailyStreak: 0, currentWeeklyStreak: 0, streakMessage: streakMessages.startStreak };
684
+ return {
685
+ currentDailyStreak: 0,
686
+ currentWeeklyStreak: 0,
687
+ streakMessage: streakMessages.startStreak,
688
+ }
687
689
  }
688
- lastActiveDay = sortedPracticeDays[sortedPracticeDays.length - 1];
690
+ lastActiveDay = sortedPracticeDays[sortedPracticeDays.length - 1]
689
691
 
690
- let dailyStreak = 0;
691
- let prevDay = null;
692
+ let dailyStreak = 0
693
+ let prevDay = null
692
694
  sortedPracticeDays.forEach((currentDay) => {
693
695
  if (prevDay === null || isNextDay(prevDay, currentDay)) {
694
- dailyStreak++;
696
+ dailyStreak++
695
697
  } else {
696
- dailyStreak = 1;
698
+ dailyStreak = 1
697
699
  }
698
- prevDay = currentDay;
699
- });
700
- currentDailyStreak = dailyStreak;
700
+ prevDay = currentDay
701
+ })
702
+ currentDailyStreak = dailyStreak
701
703
 
702
704
  // 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;
705
+ let weekNumbers = new Set(sortedPracticeDays.map((date) => getWeekNumber(date)))
706
+ let weeklyStreak = 0
707
+ let lastWeek = null
708
+ ;[...weekNumbers]
709
+ .sort((a, b) => b - a)
710
+ .forEach((week) => {
711
+ if (lastWeek === null || week === lastWeek - 1) {
712
+ weeklyStreak++
713
+ } else {
714
+ return
715
+ }
716
+ lastWeek = week
717
+ })
718
+ currentWeeklyStreak = weeklyStreak
715
719
 
716
720
  // Calculate streak message only if includeStreakMessage is true
717
721
  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 );
722
+ let today = new Date()
723
+ let yesterday = new Date(today)
724
+ yesterday.setDate(today.getDate() - 1)
725
+
726
+ let currentWeekStart = getMonday(today)
727
+ let lastWeekStart = new Date(currentWeekStart)
728
+ lastWeekStart.setDate(currentWeekStart.getDate() - 7)
729
+
730
+ let hasYesterdayPractice = sortedPracticeDays.some((date) => isSameDate(date, yesterday))
731
+ let hasCurrentWeekPractice = sortedPracticeDays.some((date) => date >= currentWeekStart)
732
+ let hasCurrentWeekPreviousPractice = sortedPracticeDays.some(
733
+ (date) => date >= currentWeekStart &amp;&amp; date &lt; today
734
+ )
735
+ let hasLastWeekPractice = sortedPracticeDays.some(
736
+ (date) => date >= lastWeekStart &amp;&amp; date &lt; currentWeekStart
737
+ )
738
+ let hasOlderPractice = sortedPracticeDays.some((date) => date &lt; lastWeekStart)
733
739
 
734
740
  if (isSameDate(lastActiveDay, today)) {
735
741
  if (hasYesterdayPractice) {
736
- streakMessage = streakMessages.dailyStreak(currentDailyStreak);
742
+ streakMessage = streakMessages.dailyStreak(currentDailyStreak)
737
743
  } else if (hasCurrentWeekPreviousPractice) {
738
- streakMessage = streakMessages.weeklyStreak(currentWeeklyStreak);
744
+ streakMessage = streakMessages.weeklyStreak(currentWeeklyStreak)
739
745
  } else if (hasLastWeekPractice) {
740
- streakMessage = streakMessages.greatJobWeeklyStreak(currentWeeklyStreak);
746
+ streakMessage = streakMessages.greatJobWeeklyStreak(currentWeeklyStreak)
741
747
  } else {
742
- streakMessage = streakMessages.dailyStreakShort(currentDailyStreak);
748
+ streakMessage = streakMessages.dailyStreakShort(currentDailyStreak)
743
749
  }
744
750
  } 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);
751
+ if (
752
+ (hasYesterdayPractice &amp;&amp; currentDailyStreak >= 2) ||
753
+ (hasYesterdayPractice &amp;&amp; sortedPracticeDays.length == 1) ||
754
+ (hasYesterdayPractice &amp;&amp; !hasLastWeekPractice &amp;&amp; hasOlderPractice)
755
+ ) {
756
+ streakMessage = streakMessages.dailyStreakReminder(currentDailyStreak)
748
757
  } else if (hasCurrentWeekPractice) {
749
- streakMessage = streakMessages.weeklyStreakKeepUp(currentWeeklyStreak);
758
+ streakMessage = streakMessages.weeklyStreakKeepUp(currentWeeklyStreak)
750
759
  } else if (hasLastWeekPractice) {
751
- streakMessage = streakMessages.weeklyStreakReminder(currentWeeklyStreak);
760
+ streakMessage = streakMessages.weeklyStreakReminder(currentWeeklyStreak)
752
761
  } else {
753
- streakMessage = streakMessages.restartStreak;
762
+ streakMessage = streakMessages.restartStreak
754
763
  }
755
764
  }
756
765
  }
757
766
 
758
- return { currentDailyStreak, currentWeeklyStreak, streakMessage };
767
+ return { currentDailyStreak, currentWeeklyStreak, streakMessage }
759
768
  }
760
769
 
761
770
  /**
762
771
  * Calculates the longest daily, weekly streaks and totalPracticeSeconds from user practice dates.
763
772
  * @returns {{ longestDailyStreak: number, longestWeeklyStreak: number, totalPracticeSeconds:number }}
764
773
  */
765
- export async function calculateLongestStreaks() {
766
- let data = await userActivityContext.getData()
767
- let practices = data?.[DATA_KEY_PRACTICES] ?? {}
768
- let totalPracticeSeconds = 0;
774
+ export async function calculateLongestStreaks(userId = globalConfig.sessionConfig.userId) {
775
+ let practices = await getUserPractices(userId)
776
+ let totalPracticeSeconds = 0
769
777
  // Calculate total practice duration
770
778
  for (const date in practices) {
771
779
  for (const entry of practices[date]) {
772
- totalPracticeSeconds += entry.duration_seconds;
780
+ totalPracticeSeconds += entry.duration_seconds
773
781
  }
774
782
  }
775
783
 
776
784
  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;
785
+ .map((dateStr) => {
786
+ const [y, m, d] = dateStr.split('-').map(Number)
787
+ const newDate = new Date()
788
+ newDate.setFullYear(y, m - 1, d)
789
+ return newDate
782
790
  })
783
- .sort((a, b) => a - b);
791
+ .sort((a, b) => a - b)
784
792
 
785
793
  if (!practiceDates || practiceDates.length === 0) {
786
- return {longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0};
794
+ return { longestDailyStreak: 0, longestWeeklyStreak: 0, totalPracticeSeconds: 0 }
787
795
  }
788
796
 
789
797
  // Normalize to Date objects
790
798
  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);
799
+ ...new Set(
800
+ practiceDates.map((d) => {
801
+ const date = new Date(d)
802
+ date.setHours(0, 0, 0, 0)
803
+ return date.getTime()
804
+ })
805
+ ),
806
+ ].sort((a, b) => a - b)
797
807
 
798
808
  // ----- Daily Streak -----
799
- let longestDailyStreak = 1;
800
- let currentDailyStreak = 1;
809
+ let longestDailyStreak = 1
810
+ let currentDailyStreak = 1
801
811
  for (let i = 1; i &lt; normalizedDates.length; i++) {
802
- const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24);
812
+ const diffInDays = (normalizedDates[i] - normalizedDates[i - 1]) / (1000 * 60 * 60 * 24)
803
813
  if (diffInDays === 1) {
804
- currentDailyStreak++;
805
- longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak);
814
+ currentDailyStreak++
815
+ longestDailyStreak = Math.max(longestDailyStreak, currentDailyStreak)
806
816
  } else {
807
- currentDailyStreak = 1;
817
+ currentDailyStreak = 1
808
818
  }
809
819
  }
810
820
 
811
821
  // ----- Weekly Streak -----
812
822
  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;
823
+ ...new Set(
824
+ normalizedDates.map((ts) => {
825
+ const d = new Date(ts)
826
+ const day = d.getDay()
827
+ const diff = d.getDate() - day + (day === 0 ? -6 : 1) // adjust to Monday
828
+ d.setDate(diff)
829
+ return d.getTime() // timestamp for Monday
830
+ })
831
+ ),
832
+ ].sort((a, b) => a - b)
833
+
834
+ let longestWeeklyStreak = 1
835
+ let currentWeeklyStreak = 1
824
836
 
825
837
  for (let i = 1; i &lt; weekStartDates.length; i++) {
826
- const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7);
838
+ const diffInWeeks = (weekStartDates[i] - weekStartDates[i - 1]) / (1000 * 60 * 60 * 24 * 7)
827
839
  if (diffInWeeks === 1) {
828
- currentWeeklyStreak++;
829
- longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak);
840
+ currentWeeklyStreak++
841
+ longestWeeklyStreak = Math.max(longestWeeklyStreak, currentWeeklyStreak)
830
842
  } else {
831
- currentWeeklyStreak = 1;
843
+ currentWeeklyStreak = 1
832
844
  }
833
845
  }
834
846
 
835
847
  return {
836
848
  longestDailyStreak,
837
849
  longestWeeklyStreak,
838
- totalPracticeSeconds
839
- };
850
+ totalPracticeSeconds,
851
+ }
840
852
  }
841
853
 
854
+ async function formatPracticeMeta(practices) {
855
+ const contentIds = practices.map((p) => p.content_id).filter((id) => id !== null)
856
+ const contents = await fetchByRailContentIds(contentIds)
842
857
 
858
+ const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
843
859
 
860
+ return practices.map((practice) => {
861
+ const utcDate = new Date(practice.created_at)
862
+ const content = contents.find((c) => c.id === practice.content_id) || {}
844
863
 
864
+ return {
865
+ id: practice.id,
866
+ auto: practice.auto,
867
+ thumbnail: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
868
+ thumbnail_url: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
869
+ duration: practice.duration_seconds || 0,
870
+ duration_seconds: practice.duration_seconds || 0,
871
+ content_url: content.url || null,
872
+ title: practice.content_id ? content.title : practice.title,
873
+ category_id: practice.category_id,
874
+ instrument_id: practice.instrument_id,
875
+ content_type: getFormattedType(content.type || ''),
876
+ content_id: practice.content_id || null,
877
+ content_brand: content.brand || null,
878
+ created_at: convertToTimeZone(utcDate, userTimeZone),
879
+ }
880
+ })
881
+ }
845
882
 
883
+ export function getFormattedType(type) {
884
+ for (const [key, values] of Object.entries(lessonTypesMapping)) {
885
+ if (values.includes(type)) {
886
+ return key.replace(/\b\w/g, (char) => char.toUpperCase())
887
+ }
888
+ }
889
+ return null
890
+ }
846
891
 
892
+ /**
893
+ * Records a new user activity in the system.
894
+ *
895
+ * @param {Object} payload - The data representing the user activity.
896
+ * @param {number} payload.user_id - The ID of the user.
897
+ * @param {string} payload.action - The type of action (e.g., 'start', 'complete', 'comment', etc.).
898
+ * @param {string} payload.brand - The brand associated with the activity.
899
+ * @param {string} payload.type - The content type (e.g., 'lesson', 'song', etc.).
900
+ * @param {number} payload.content_id - The ID of the related content.
901
+ * @param {string} payload.date - The date of the activity (ISO format).
902
+ * @returns {Promise&lt;Object>} - A promise that resolves to the API response after recording the activity.
903
+ *
904
+ * @example
905
+ * recordUserActivity({
906
+ * user_id: 123,
907
+ * action: 'start',
908
+ * brand: 'pianote',
909
+ * type: 'lesson',
910
+ * content_id: 4561,
911
+ * date: '2025-05-15'
912
+ * }).then(response => console.log(response))
913
+ * .catch(error => console.error(error));
914
+ */
915
+ export async function recordUserActivity(payload) {
916
+ const url = `/api/user-management-system/v1/activities`
917
+ return await fetchHandler(url, 'POST', null, payload)
918
+ }
847
919
 
848
-
849
-
920
+ /**
921
+ * Deletes a specific user activity by its ID.
922
+ *
923
+ * @param {number|string} id - The ID of the user activity to delete.
924
+ * @returns {Promise&lt;Object>} - A promise that resolves to the API response after deletion.
925
+ *
926
+ * @example
927
+ * deleteUserActivity(789)
928
+ * .then(response => console.log('Deleted:', response))
929
+ * .catch(error => console.error(error));
930
+ */
931
+ export async function deleteUserActivity(id) {
932
+ const url = `/api/user-management-system/v1/activities/${id}`
933
+ return await fetchHandler(url, 'DELETE')
934
+ }
850
935
  </code></pre>
851
936
  </article>
852
937
  </section>
@@ -861,7 +946,7 @@ export async function calculateLongestStreaks() {
861
946
  <br class="clear">
862
947
 
863
948
  <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.
949
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Wed May 21 2025 13:48:43 GMT+0300 (Eastern European Summer Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
865
950
  </footer>
866
951
 
867
952
  <script>prettyPrint();</script>