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