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.
- package/CHANGELOG.md +7 -0
- package/docs/ContentOrganization.html +2 -2
- package/docs/Gamification.html +2 -2
- package/docs/UserManagementSystem.html +2 -2
- package/docs/api_types.js.html +2 -2
- package/docs/config.js.html +2 -2
- package/docs/content-org_content-org.js.html +2 -2
- package/docs/content-org_playlists-types.js.html +2 -2
- package/docs/content-org_playlists.js.html +2 -2
- package/docs/content.js.html +2 -4
- package/docs/gamification_awards.js.html +2 -2
- package/docs/gamification_gamification.js.html +2 -2
- package/docs/gamification_types.js.html +2 -2
- package/docs/global.html +448 -2
- package/docs/index.html +2 -2
- package/docs/module-Awards.html +2 -2
- package/docs/module-Config.html +2 -2
- package/docs/module-Content-Services-V2.html +8 -8
- package/docs/module-Interests.html +2 -2
- package/docs/module-Permissions.html +2 -2
- package/docs/module-Playlists.html +2 -2
- package/docs/module-Railcontent-Services.html +754 -44
- package/docs/module-Sanity-Services.html +2 -2
- package/docs/module-Sessions.html +2 -2
- package/docs/module-User-Activity.html +525 -16
- package/docs/module-UserManagement.html +2 -2
- package/docs/module-UserProfile.html +266 -0
- package/docs/railcontent.js.html +41 -2
- package/docs/sanity.js.html +2 -2
- package/docs/userActivity.js.html +348 -263
- package/docs/user_interests.js.html +2 -2
- package/docs/user_management.js.html +2 -2
- package/docs/user_permissions.js.html +2 -2
- package/docs/user_profile.js.html +105 -0
- package/docs/user_sessions.js.html +2 -2
- package/docs/user_types.js.html +22 -2
- package/docs/user_user-management-system.js.html +2 -2
- package/package.json +1 -1
- package/src/contentMetaData.js +14 -0
- package/src/index.d.ts +15 -0
- package/src/index.js +15 -0
- package/src/services/content.js +0 -2
- package/src/services/railcontent.js +39 -0
- package/src/services/user/profile.js +33 -0
- package/src/services/user/types.js +20 -0
- package/src/services/userActivity.js +346 -261
- package/test/content.test.js +8 -6
- 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
|
|
55
|
-
import {lessonTypesMapping} from
|
|
56
|
-
import { convertToTimeZone, getMonday, getWeekNumber, isSameDate, isNextDay } from './dateUtils.js'
|
|
57
|
-
import {globalConfig} from
|
|
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:
|
|
74
|
-
restartStreak:
|
|
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) =>
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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) =>
|
|
84
|
-
|
|
85
|
-
|
|
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 && streak <= 89) || (streak >= 800
|
|
89
|
+
return streak === 8 || (streak >= 80 && streak <= 89) || (streak >= 800 && streak <= 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 < 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 =
|
|
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(
|
|
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 < 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 =
|
|
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 {
|
|
264
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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][
|
|
384
|
-
|
|
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
|
-
|
|
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
|
-
|
|
521
|
-
|
|
522
|
-
|
|
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
|
|
547
|
+
const meta = await fetchUserPracticeMeta(userPracticesIds, userId)
|
|
548
|
+
if (!meta.data.length) return { data: { practices: [], practiceDuration: 0 } }
|
|
543
549
|
|
|
544
|
-
const formattedMeta = meta.data
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
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
|
-
|
|
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?.[
|
|
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('&')
|
|
664
|
+
if (!ids.length) return ''
|
|
665
|
+
return '?' + ids.map((id) => `${paramName}[]=${id}`).join('&')
|
|
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 {
|
|
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]
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
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
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
let hasLastWeekPractice = sortedPracticeDays.some(
|
|
732
|
-
|
|
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 && date < today
|
|
734
|
+
)
|
|
735
|
+
let hasLastWeekPractice = sortedPracticeDays.some(
|
|
736
|
+
(date) => date >= lastWeekStart && date < currentWeekStart
|
|
737
|
+
)
|
|
738
|
+
let hasOlderPractice = sortedPracticeDays.some((date) => date < 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 (
|
|
746
|
-
|
|
747
|
-
|
|
751
|
+
if (
|
|
752
|
+
(hasYesterdayPractice && currentDailyStreak >= 2) ||
|
|
753
|
+
(hasYesterdayPractice && sortedPracticeDays.length == 1) ||
|
|
754
|
+
(hasYesterdayPractice && !hasLastWeekPractice && 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
|
|
767
|
-
let
|
|
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(
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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 < 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(
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
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 < 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<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<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
|
|
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>
|