musora-content-services 1.6.7 → 1.6.8
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/.github/workflows/node.js.yml +0 -0
- package/.prettierignore +0 -0
- package/.prettierrc +0 -0
- package/CHANGELOG.md +2 -0
- package/babel.config.cjs +0 -0
- 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/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/v2/ContentOrganization.html +2 -2
- package/docs/v2/Forums.html +269 -0
- package/docs/v2/Gamification.html +2 -2
- package/docs/v2/UserManagementSystem.html +2 -2
- package/docs/v2/api_types.js.html +2 -2
- package/docs/v2/config.js.html +2 -8
- package/docs/v2/content-org_content-org.js.html +2 -2
- package/docs/v2/content-org_guided-courses.ts.html +110 -0
- package/docs/v2/content-org_playlists-types.js.html +14 -2
- package/docs/v2/content-org_playlists.js.html +39 -17
- package/docs/v2/content.js.html +120 -64
- package/docs/v2/forums_categories.ts.html +137 -0
- package/docs/v2/forums_discussions.js.html +95 -0
- package/docs/v2/forums_forum.js.html +95 -0
- package/docs/v2/forums_forums.ts.html +77 -0
- package/docs/v2/forums_posts.ts.html +158 -0
- package/docs/v2/forums_threads.ts.html +271 -0
- package/docs/v2/gamification_awards.js.html +35 -534
- package/docs/v2/gamification_awards.ts.html +181 -0
- package/docs/v2/gamification_gamification.js.html +2 -2
- package/docs/v2/gamification_types.js.html +7 -25
- package/docs/v2/global.html +296 -492
- package/docs/v2/index.html +2 -3
- package/docs/v2/module-Accounts.html +1471 -0
- package/docs/v2/module-Awards.html +396 -14
- package/docs/v2/module-Categories.html +711 -0
- package/docs/v2/module-Config.html +4 -8
- package/docs/v2/module-Content-Services-V2.html +460 -46
- package/docs/v2/module-ForumCategories.html +687 -0
- package/docs/v2/module-ForumDiscussions.html +370 -0
- package/docs/v2/module-Forums.html +11961 -0
- package/docs/v2/module-GuidedCourses.html +108 -0
- package/docs/v2/module-Interests.html +2 -2
- package/docs/v2/module-Payments.html +250 -0
- package/docs/v2/module-Permissions.html +2 -2
- package/docs/v2/module-Playlists.html +212 -44
- package/docs/v2/module-Railcontent-Services.html +691 -2177
- package/docs/v2/module-Sanity-Services.html +200 -941
- package/docs/v2/module-Sessions.html +3 -3
- package/docs/v2/module-Threads.html +1119 -0
- package/docs/v2/module-UserActivity.html +193 -23
- package/docs/v2/module-UserChat.html +410 -0
- package/docs/v2/module-UserManagement.html +585 -12
- package/docs/v2/module-UserMemberships.html +556 -0
- package/docs/v2/module-UserNotifications.html +1399 -27
- package/docs/v2/module-UserProfile.html +106 -2
- package/docs/v2/railcontent.js.html +105 -235
- package/docs/v2/sanity.js.html +263 -415
- package/docs/v2/userActivity.js.html +532 -451
- package/docs/v2/user_account.ts.html +190 -0
- package/docs/v2/user_chat.js.html +98 -0
- package/docs/v2/user_interests.js.html +2 -2
- package/docs/v2/user_management.js.html +45 -2
- package/docs/v2/user_memberships.js.html +144 -0
- package/docs/v2/user_notifications.js.html +203 -21
- package/docs/v2/user_payments.ts.html +97 -0
- package/docs/v2/user_permissions.js.html +2 -2
- package/docs/v2/user_profile.js.html +12 -2
- package/docs/v2/user_sessions.js.html +33 -2
- package/docs/v2/user_types.js.html +10 -2
- package/docs/v2/user_user-management-system.js.html +2 -2
- package/jest.config.js +0 -0
- package/link_mcs.sh +0 -0
- package/package.json +1 -1
- package/src/contentTypeConfig.js +3 -3
- package/src/services/imageSRCVerify.js +0 -0
- package/test/dataContext.test.js +0 -0
- package/test/imageSRCBuilder.test.js +0 -0
- package/test/imageSRCVerify.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/.yarnrc.yml +0 -1
- package/docs/v2/Content-Organization.html +0 -245
- package/docs/v2/UserManagement.html +0 -269
- package/docs/v2/global.html#User +0 -293
- package/docs/v2/module-Notifications.html +0 -1183
- package/docs/v2/module-Session-Management.html +0 -575
- package/docs/v2/module-User-Activity.html +0 -4410
- package/docs/v2/module-User-Management.html +0 -490
- package/docs/v2/module-User-Permissions.html +0 -406
- package/docs/v2/types.js.html +0 -122
- package/docs/v2/user_user-management.js.html +0 -78
|
@@ -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#.deletePlaylist">deletePlaylist</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#.undeletePlaylist">undeletePlaylist</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-UserActivity.html">UserActivity</a><ul class='methods'><li data-type='method'><a href="module-UserActivity.html#.calculateLongestStreaks">calculateLongestStreaks</a></li><li data-type='method'><a href="module-UserActivity.html#.createPracticeNotes">createPracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.deletePracticeSession">deletePracticeSession</a></li><li data-type='method'><a href="module-UserActivity.html#.deleteUserActivity">deleteUserActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.getPracticeNotes">getPracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.getPracticeSessions">getPracticeSessions</a></li><li data-type='method'><a href="module-UserActivity.html#.getProgressRows">getProgressRows</a></li><li data-type='method'><a href="module-UserActivity.html#.getRecentActivity">getRecentActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.getUserMonthlyStats">getUserMonthlyStats</a></li><li data-type='method'><a href="module-UserActivity.html#.getUserWeeklyStats">getUserWeeklyStats</a></li><li data-type='method'><a href="module-UserActivity.html#.pinProgressRow">pinProgressRow</a></li><li data-type='method'><a href="module-UserActivity.html#.recordUserActivity">recordUserActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.recordUserPractice">recordUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.removeUserPractice">removeUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.restorePracticeSession">restorePracticeSession</a></li><li data-type='method'><a href="module-UserActivity.html#.restoreUserPractice">restoreUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.unpinProgressRow">unpinProgressRow</a></li><li data-type='method'><a href="module-UserActivity.html#.updatePracticeNotes">updatePracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.updateUserPractice">updateUserPractice</a></li></ul></li><li><a href="module-UserChat.html">UserChat</a><ul class='methods'><li data-type='method'><a href="module-UserChat.html#.fetchChatSettings">fetchChatSettings</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#.deletePicture">deletePicture</a></li><li data-type='method'><a href="module-UserManagement.html#.unblockUser">unblockUser</a></li><li data-type='method'><a href="module-UserManagement.html#.uploadPicture">uploadPicture</a></li><li data-type='method'><a href="module-UserManagement.html#.uploadPictureFromS3">uploadPictureFromS3</a></li></ul></li><li><a href="module-UserNotifications.html">UserNotifications</a><ul class='methods'><li data-type='method'><a href="module-UserNotifications.html#.deleteNotification">deleteNotification</a></li><li data-type='method'><a href="module-UserNotifications.html#.fetchNotifications">fetchNotifications</a></li><li data-type='method'><a href="module-UserNotifications.html#.markAllNotificationsAsRead">markAllNotificationsAsRead</a></li><li data-type='method'><a href="module-UserNotifications.html#.markNotificationAsRead">markNotificationAsRead</a></li><li data-type='method'><a href="module-UserNotifications.html#.markNotificationAsUnread">markNotificationAsUnread</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>
|
|
32
|
+
<h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-Accounts.html">Accounts</a><ul class='methods'><li data-type='method'><a href="module-Accounts.html#~confirmEmailChange">confirmEmailChange</a></li><li data-type='method'><a href="module-Accounts.html#~requestEmailChange">requestEmailChange</a></li><li data-type='method'><a href="module-Accounts.html#~resetPassword">resetPassword</a></li><li data-type='method'><a href="module-Accounts.html#~sendAccountSetupEmail">sendAccountSetupEmail</a></li><li data-type='method'><a href="module-Accounts.html#~sendPasswordResetEmail">sendPasswordResetEmail</a></li><li data-type='method'><a href="module-Accounts.html#~setupAccount">setupAccount</a></li><li data-type='method'><a href="module-Accounts.html#~status">status</a></li></ul></li><li><a href="module-Awards.html">Awards</a><ul class='methods'><li data-type='method'><a href="module-Awards.html#~fetchAwardsForUser">fetchAwardsForUser</a></li><li data-type='method'><a href="module-Awards.html#~fetchCertificate">fetchCertificate</a></li><li data-type='method'><a href="module-Awards.html#~getAwardDataForGuidedContent">getAwardDataForGuidedContent</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#.getRecentForTab">getRecentForTab</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-Forums.html">Forums</a><ul class='methods'><li data-type='method'><a href="module-Forums.html#~createForumCategory">createForumCategory</a></li><li data-type='method'><a href="module-Forums.html#~createPost">createPost</a></li><li data-type='method'><a href="module-Forums.html#~createThread">createThread</a></li><li data-type='method'><a href="module-Forums.html#~deletePost">deletePost</a></li><li data-type='method'><a href="module-Forums.html#~deleteThread">deleteThread</a></li><li data-type='method'><a href="module-Forums.html#~fetchCommunityGuidelines">fetchCommunityGuidelines</a></li><li data-type='method'><a href="module-Forums.html#~fetchFollowedThreads">fetchFollowedThreads</a></li><li data-type='method'><a href="module-Forums.html#~fetchForumCategories">fetchForumCategories</a></li><li data-type='method'><a href="module-Forums.html#~fetchLatestThreads">fetchLatestThreads</a></li><li data-type='method'><a href="module-Forums.html#~fetchPosts">fetchPosts</a></li><li data-type='method'><a href="module-Forums.html#~fetchThreads">fetchThreads</a></li><li data-type='method'><a href="module-Forums.html#~followThread">followThread</a></li><li data-type='method'><a href="module-Forums.html#~lockThread">lockThread</a></li><li data-type='method'><a href="module-Forums.html#~pinThread">pinThread</a></li><li data-type='method'><a href="module-Forums.html#~unfollowThread">unfollowThread</a></li><li data-type='method'><a href="module-Forums.html#~unlockThread">unlockThread</a></li><li data-type='method'><a href="module-Forums.html#~unpinThread">unpinThread</a></li><li data-type='method'><a href="module-Forums.html#~updateForumCategory">updateForumCategory</a></li><li data-type='method'><a href="module-Forums.html#~updateThread">updateThread</a></li></ul></li><li></li><li></li><li><a href="module-GuidedCourses.html">GuidedCourses</a></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-Payments.html">Payments</a><ul class='methods'><li data-type='method'><a href="module-Payments.html#~fetchCustomerPayments">fetchCustomerPayments</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#.deletePlaylist">deletePlaylist</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#.undeletePlaylist">undeletePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#.updatePlaylist">updatePlaylist</a></li><li data-type='method'><a href="module-Playlists.html#~deleteItemsFromPlaylist">deleteItemsFromPlaylist</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#~restoreItemFromPlaylist">restoreItemFromPlaylist</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#.fetchComment">fetchComment</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#.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#.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#.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#.postContentComplete">postContentComplete</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postContentReset">postContentReset</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postContentRestore">postContentRestore</a></li><li data-type='method'><a href="module-Railcontent-Services.html#.postContentStart">postContentStart</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#.restoreComment">restoreComment</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#.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#.fetchSiblingContent">fetchSiblingContent</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#~getQueryFromPage">getQueryFromPage</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-UserActivity.html">UserActivity</a><ul class='methods'><li data-type='method'><a href="module-UserActivity.html#.calculateLongestStreaks">calculateLongestStreaks</a></li><li data-type='method'><a href="module-UserActivity.html#.createPracticeNotes">createPracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.deletePracticeSession">deletePracticeSession</a></li><li data-type='method'><a href="module-UserActivity.html#.deleteUserActivity">deleteUserActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.getPracticeNotes">getPracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.getPracticeSessions">getPracticeSessions</a></li><li data-type='method'><a href="module-UserActivity.html#.getProgressRows">getProgressRows</a></li><li data-type='method'><a href="module-UserActivity.html#.getRecentActivity">getRecentActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.getUserMonthlyStats">getUserMonthlyStats</a></li><li data-type='method'><a href="module-UserActivity.html#.getUserWeeklyStats">getUserWeeklyStats</a></li><li data-type='method'><a href="module-UserActivity.html#.pinProgressRow">pinProgressRow</a></li><li data-type='method'><a href="module-UserActivity.html#.recordUserActivity">recordUserActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.recordUserPractice">recordUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.removeUserPractice">removeUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.restorePracticeSession">restorePracticeSession</a></li><li data-type='method'><a href="module-UserActivity.html#.restoreUserActivity">restoreUserActivity</a></li><li data-type='method'><a href="module-UserActivity.html#.restoreUserPractice">restoreUserPractice</a></li><li data-type='method'><a href="module-UserActivity.html#.unpinProgressRow">unpinProgressRow</a></li><li data-type='method'><a href="module-UserActivity.html#.updatePracticeNotes">updatePracticeNotes</a></li><li data-type='method'><a href="module-UserActivity.html#.updateUserPractice">updateUserPractice</a></li></ul></li><li><a href="module-UserChat.html">UserChat</a><ul class='methods'><li data-type='method'><a href="module-UserChat.html#.fetchChatSettings">fetchChatSettings</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#.blockedUsers">blockedUsers</a></li><li data-type='method'><a href="module-UserManagement.html#.deletePicture">deletePicture</a></li><li data-type='method'><a href="module-UserManagement.html#.getUserData">getUserData</a></li><li data-type='method'><a href="module-UserManagement.html#.isDisplayNameAvailable">isDisplayNameAvailable</a></li><li data-type='method'><a href="module-UserManagement.html#.unblockUser">unblockUser</a></li><li data-type='method'><a href="module-UserManagement.html#.updateDisplayName">updateDisplayName</a></li><li data-type='method'><a href="module-UserManagement.html#.uploadPicture">uploadPicture</a></li><li data-type='method'><a href="module-UserManagement.html#.uploadPictureFromS3">uploadPictureFromS3</a></li></ul></li><li><a href="module-UserMemberships.html">UserMemberships</a><ul class='methods'><li data-type='method'><a href="module-UserMemberships.html#.fetchMemberships">fetchMemberships</a></li><li data-type='method'><a href="module-UserMemberships.html#.fetchRechargeTokens">fetchRechargeTokens</a></li><li data-type='method'><a href="module-UserMemberships.html#.upgradeSubscription">upgradeSubscription</a></li></ul></li><li><a href="module-UserNotifications.html">UserNotifications</a><ul class='methods'><li data-type='method'><a href="module-UserNotifications.html#.deleteNotification">deleteNotification</a></li><li data-type='method'><a href="module-UserNotifications.html#.fetchLiveEventPollingState">fetchLiveEventPollingState</a></li><li data-type='method'><a href="module-UserNotifications.html#.fetchNotificationSettings">fetchNotificationSettings</a></li><li data-type='method'><a href="module-UserNotifications.html#.fetchNotifications">fetchNotifications</a></li><li data-type='method'><a href="module-UserNotifications.html#.fetchUnreadCount">fetchUnreadCount</a></li><li data-type='method'><a href="module-UserNotifications.html#.markAllNotificationsAsRead">markAllNotificationsAsRead</a></li><li data-type='method'><a href="module-UserNotifications.html#.markNotificationAsRead">markNotificationAsRead</a></li><li data-type='method'><a href="module-UserNotifications.html#.markNotificationAsUnread">markNotificationAsUnread</a></li><li data-type='method'><a href="module-UserNotifications.html#.pauseLiveEventPolling">pauseLiveEventPolling</a></li><li data-type='method'><a href="module-UserNotifications.html#.restoreNotification">restoreNotification</a></li><li data-type='method'><a href="module-UserNotifications.html#.startLiveEventPolling">startLiveEventPolling</a></li><li data-type='method'><a href="module-UserNotifications.html#.updateNotificationSetting">updateNotificationSetting</a></li></ul></li><li><a href="module-UserProfile.html">UserProfile</a><ul class='methods'><li data-type='method'><a href="module-UserProfile.html#.deleteProfilePicture">deleteProfilePicture</a></li><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="Forums.html">Forums</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
|
|
|
@@ -55,16 +55,34 @@ import {
|
|
|
55
55
|
fetchUserPracticeMeta,
|
|
56
56
|
fetchUserPracticeNotes,
|
|
57
57
|
fetchHandler,
|
|
58
|
-
fetchRecentUserActivities,
|
|
58
|
+
fetchRecentUserActivities,
|
|
59
59
|
} from './railcontent'
|
|
60
60
|
import { DataContext, UserActivityVersionKey } from './dataContext.js'
|
|
61
|
-
import { fetchByRailContentIds, fetchShows } from './sanity'
|
|
62
|
-
import {fetchPlaylist, fetchUserPlaylists} from
|
|
63
|
-
import {
|
|
61
|
+
import { fetchByRailContentId, fetchByRailContentIds, fetchShows } from './sanity'
|
|
62
|
+
import { fetchPlaylist, fetchUserPlaylists } from './content-org/playlists'
|
|
63
|
+
import {guidedCourses} from './content-org/guided-courses'
|
|
64
|
+
import {
|
|
65
|
+
getMonday,
|
|
66
|
+
getWeekNumber,
|
|
67
|
+
isSameDate,
|
|
68
|
+
isNextDay,
|
|
69
|
+
getTimeRemainingUntilLocal,
|
|
70
|
+
toDayjs,
|
|
71
|
+
} from './dateUtils.js'
|
|
64
72
|
import { globalConfig } from './config'
|
|
65
|
-
import {
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
import {
|
|
74
|
+
collectionLessonTypes,
|
|
75
|
+
progressTypesMapping,
|
|
76
|
+
recentTypes,
|
|
77
|
+
showsLessonTypes,
|
|
78
|
+
songs,
|
|
79
|
+
} from '../contentTypeConfig'
|
|
80
|
+
import { getAllStartedOrCompleted, getProgressStateByIds } from './contentProgress'
|
|
81
|
+
import { TabResponseType } from '../contentMetaData'
|
|
82
|
+
import dayjs from 'dayjs'
|
|
83
|
+
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore'
|
|
84
|
+
import weekOfYear from 'dayjs/plugin/weekOfYear'
|
|
85
|
+
import { addContextToContent } from './contentAggregator.js'
|
|
68
86
|
|
|
69
87
|
const DATA_KEY_PRACTICES = 'practices'
|
|
70
88
|
const DATA_KEY_LAST_UPDATED_TIME = 'u'
|
|
@@ -102,7 +120,7 @@ function getIndefiniteArticle(streak) {
|
|
|
102
120
|
|
|
103
121
|
export async function getUserPractices(userId = globalConfig.sessionConfig.userId) {
|
|
104
122
|
if (userId !== globalConfig.sessionConfig.userId) {
|
|
105
|
-
let data = await fetchUserPractices({ userId })
|
|
123
|
+
let data = await fetchUserPractices(0, { userId: userId })
|
|
106
124
|
return data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
|
|
107
125
|
} else {
|
|
108
126
|
let data = await userActivityContext.getData()
|
|
@@ -124,24 +142,30 @@ export let userActivityContext = new DataContext(UserActivityVersionKey, fetchUs
|
|
|
124
142
|
* .catch(error => console.error(error));
|
|
125
143
|
*/
|
|
126
144
|
export async function getUserWeeklyStats() {
|
|
145
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
127
146
|
let data = await userActivityContext.getData()
|
|
128
147
|
let practices = data?.[DATA_KEY_PRACTICES] ?? {}
|
|
129
148
|
let sortedPracticeDays = Object.keys(practices)
|
|
130
|
-
.map((date) =>
|
|
131
|
-
.sort((a, b) => b - a)
|
|
132
|
-
|
|
133
|
-
let
|
|
134
|
-
today.setHours(0, 0, 0, 0)
|
|
135
|
-
let startOfWeek = getMonday(today) // Get last Monday
|
|
149
|
+
.map((date) => toDayjs(date)) // Convert to dayjs instance
|
|
150
|
+
.sort((a, b) => b.valueOf() - a.valueOf())
|
|
151
|
+
let today = dayjs()
|
|
152
|
+
let startOfWeek = getMonday(today, timeZone) // Get last Monday
|
|
136
153
|
let dailyStats = []
|
|
137
|
-
|
|
138
154
|
for (let i = 0; i < 7; i++) {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
155
|
+
const day = startOfWeek.add(i, 'day')
|
|
156
|
+
let hasPractice = sortedPracticeDays.some((practiceDate) =>
|
|
157
|
+
isSameDate(practiceDate, day.format('YYYY-MM-DD'))
|
|
158
|
+
)
|
|
159
|
+
let isActive = isSameDate(today.format(), day.format())
|
|
143
160
|
let type = hasPractice ? 'tracked' : isActive ? 'active' : 'none'
|
|
144
|
-
dailyStats.push({
|
|
161
|
+
dailyStats.push({
|
|
162
|
+
key: i,
|
|
163
|
+
label: DAYS[i],
|
|
164
|
+
isActive,
|
|
165
|
+
inStreak: hasPractice,
|
|
166
|
+
type,
|
|
167
|
+
day: day.format('YYYY-MM-DD'),
|
|
168
|
+
})
|
|
145
169
|
}
|
|
146
170
|
|
|
147
171
|
let { streakMessage } = getStreaksAndMessage(practices)
|
|
@@ -180,83 +204,77 @@ export async function getUserWeeklyStats() {
|
|
|
180
204
|
* getUserMonthlyStats({ userId: 123 }).then(console.log);
|
|
181
205
|
*/
|
|
182
206
|
export async function getUserMonthlyStats(params = {}) {
|
|
183
|
-
const now =
|
|
207
|
+
const now = dayjs()
|
|
184
208
|
const {
|
|
185
|
-
year = now.
|
|
186
|
-
month = now.
|
|
187
|
-
day = 1,
|
|
209
|
+
year = now.year(),
|
|
210
|
+
month = now.month(), // 0-indexed
|
|
188
211
|
userId = globalConfig.sessionConfig.userId,
|
|
189
212
|
} = params
|
|
190
|
-
|
|
213
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
214
|
+
const practices = await getUserPractices(userId)
|
|
191
215
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
today.setHours(0, 0, 0, 0)
|
|
216
|
+
const firstDayOfMonth = dayjs.tz(`${year}-${month + 1}-01`, timeZone).startOf('day')
|
|
217
|
+
const endOfMonth = firstDayOfMonth.endOf('month')
|
|
218
|
+
const today = dayjs().tz(timeZone).startOf('day')
|
|
196
219
|
|
|
197
|
-
let startOfGrid = getMonday(firstDayOfMonth)
|
|
220
|
+
let startOfGrid = getMonday(firstDayOfMonth, timeZone)
|
|
198
221
|
|
|
199
|
-
|
|
200
|
-
previousWeekStart.
|
|
201
|
-
|
|
202
|
-
let previousWeekEnd = new Date(startOfGrid)
|
|
203
|
-
previousWeekEnd.setDate(previousWeekEnd.getDate() - 1)
|
|
222
|
+
// Previous week range
|
|
223
|
+
const previousWeekStart = startOfGrid.subtract(7, 'day')
|
|
224
|
+
const previousWeekEnd = startOfGrid.subtract(1, 'day')
|
|
204
225
|
|
|
205
226
|
let hadStreakBeforeMonth = false
|
|
206
|
-
for (let d =
|
|
207
|
-
|
|
208
|
-
if (practices[
|
|
227
|
+
for (let d = previousWeekStart.clone(); d.isSameOrBefore(previousWeekEnd); d = d.add(1, 'day')) {
|
|
228
|
+
const key = d.format('YYYY-MM-DD')
|
|
229
|
+
if (practices[key]) {
|
|
209
230
|
hadStreakBeforeMonth = true
|
|
210
231
|
break
|
|
211
232
|
}
|
|
212
233
|
}
|
|
213
234
|
|
|
214
|
-
let endOfMonth = new Date(year, month + 1, 0)
|
|
215
|
-
|
|
216
|
-
|
|
235
|
+
// let endOfMonth = new Date(year, month + 1, 0)
|
|
236
|
+
let endOfGrid = endOfMonth.clone()
|
|
237
|
+
while (endOfGrid.day() !== 0) {
|
|
238
|
+
endOfGrid = endOfGrid.add(1, 'day')
|
|
217
239
|
}
|
|
218
|
-
|
|
219
|
-
let daysInMonth = Math.ceil((endOfMonth - startOfGrid) / (1000 * 60 * 60 * 24)) + 1
|
|
220
|
-
|
|
240
|
+
const daysInMonth = endOfGrid.diff(startOfGrid, 'day') + 1
|
|
221
241
|
let dailyStats = []
|
|
222
242
|
let practiceDuration = 0
|
|
223
243
|
let daysPracticed = 0
|
|
224
244
|
let weeklyStats = {}
|
|
225
245
|
|
|
226
246
|
for (let i = 0; i < daysInMonth; i++) {
|
|
227
|
-
let day =
|
|
228
|
-
day.
|
|
229
|
-
let
|
|
230
|
-
|
|
231
|
-
// Check if the user has activity for the day
|
|
232
|
-
let dayActivity = practices[dayKey] ?? null
|
|
247
|
+
let day = startOfGrid.add(i, 'day')
|
|
248
|
+
let key = day.format('YYYY-MM-DD')
|
|
249
|
+
let activity = practices[key] ?? null
|
|
233
250
|
let weekKey = getWeekNumber(day)
|
|
234
251
|
|
|
235
252
|
if (!weeklyStats[weekKey]) {
|
|
236
253
|
weeklyStats[weekKey] = { key: weekKey, inStreak: false }
|
|
237
254
|
}
|
|
238
255
|
|
|
239
|
-
if (
|
|
240
|
-
practiceDuration +=
|
|
256
|
+
if (activity && day.isBetween(firstDayOfMonth, endOfMonth, null, '[]')) {
|
|
257
|
+
practiceDuration += activity.reduce((sum, entry) => sum + entry.duration_seconds, 0)
|
|
241
258
|
daysPracticed++
|
|
242
259
|
}
|
|
243
260
|
|
|
244
|
-
|
|
245
|
-
let type = dayActivity !== null ? 'tracked' : isActive ? 'active' : 'none'
|
|
246
|
-
let isInStreak = dayActivity !== null
|
|
247
|
-
if (isInStreak) {
|
|
261
|
+
if (activity) {
|
|
248
262
|
weeklyStats[weekKey].inStreak = true
|
|
249
263
|
}
|
|
250
264
|
|
|
265
|
+
const isActive = day.isSame(today, 'day')
|
|
266
|
+
const type = activity ? 'tracked' : isActive ? 'active' : 'none'
|
|
267
|
+
|
|
251
268
|
dailyStats.push({
|
|
252
269
|
key: i,
|
|
253
|
-
label:
|
|
270
|
+
label: key,
|
|
254
271
|
isActive,
|
|
255
|
-
inStreak:
|
|
272
|
+
inStreak: !!activity,
|
|
256
273
|
type,
|
|
257
274
|
})
|
|
258
275
|
}
|
|
259
276
|
|
|
277
|
+
// Continue streak into month
|
|
260
278
|
if (hadStreakBeforeMonth) {
|
|
261
279
|
const firstWeekKey = getWeekNumber(startOfGrid)
|
|
262
280
|
if (weeklyStats[firstWeekKey]) {
|
|
@@ -264,14 +282,15 @@ export async function getUserMonthlyStats(params = {}) {
|
|
|
264
282
|
}
|
|
265
283
|
}
|
|
266
284
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
.
|
|
270
|
-
|
|
271
|
-
|
|
285
|
+
// Filter past practices only
|
|
286
|
+
let filteredPractices = Object.entries(practices)
|
|
287
|
+
.filter(([date]) => dayjs(date).isSameOrBefore(endOfMonth))
|
|
288
|
+
.reduce((acc, [date, val]) => {
|
|
289
|
+
acc[date] = val
|
|
290
|
+
return acc
|
|
272
291
|
}, {})
|
|
273
292
|
|
|
274
|
-
|
|
293
|
+
const { currentDailyStreak, currentWeeklyStreak } = calculateStreaks(filteredPractices)
|
|
275
294
|
|
|
276
295
|
return {
|
|
277
296
|
data: {
|
|
@@ -310,13 +329,16 @@ export async function getUserMonthlyStats(params = {}) {
|
|
|
310
329
|
* auto: false,
|
|
311
330
|
* category_id: 5,
|
|
312
331
|
* title: "Guitar Warm-up",
|
|
313
|
-
* thumbnail_url: "https://example.com/thumbnail.jpg"
|
|
332
|
+
* thumbnail_url: "https://example.com/thumbnail.jpg",
|
|
333
|
+
* instrument_id: 1,
|
|
334
|
+
* instrument_id: 2,
|
|
314
335
|
* })
|
|
315
336
|
* .then(response => console.log(response))
|
|
316
337
|
* .catch(error => console.error(error));
|
|
317
338
|
*/
|
|
318
339
|
export async function recordUserPractice(practiceDetails) {
|
|
319
340
|
practiceDetails.auto = 0
|
|
341
|
+
practiceDetails.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
320
342
|
if (practiceDetails.content_id) {
|
|
321
343
|
practiceDetails.auto = 1
|
|
322
344
|
}
|
|
@@ -396,9 +418,14 @@ export async function removeUserPractice(id) {
|
|
|
396
418
|
async function (localContext) {
|
|
397
419
|
if (localContext.data?.[DATA_KEY_PRACTICES]) {
|
|
398
420
|
Object.keys(localContext.data[DATA_KEY_PRACTICES]).forEach((date) => {
|
|
399
|
-
localContext.data[DATA_KEY_PRACTICES][date]
|
|
400
|
-
|
|
401
|
-
|
|
421
|
+
const filtered = localContext.data[DATA_KEY_PRACTICES][date].filter(
|
|
422
|
+
(practice) => practice.id !== id
|
|
423
|
+
)
|
|
424
|
+
if (filtered.length > 0) {
|
|
425
|
+
localContext.data[DATA_KEY_PRACTICES][date] = filtered
|
|
426
|
+
} else {
|
|
427
|
+
delete localContext.data[DATA_KEY_PRACTICES][date]
|
|
428
|
+
}
|
|
402
429
|
})
|
|
403
430
|
}
|
|
404
431
|
},
|
|
@@ -423,20 +450,23 @@ export async function removeUserPractice(id) {
|
|
|
423
450
|
export async function restoreUserPractice(id) {
|
|
424
451
|
let url = `/api/user/practices/v1/practices/restore${buildQueryString([id])}`
|
|
425
452
|
const response = await fetchHandler(url, 'put')
|
|
426
|
-
if (response?.data) {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
453
|
+
if (response?.data?.length) {
|
|
454
|
+
const restoredPractice = response.data.find((p) => p.id === id)
|
|
455
|
+
if (restoredPractice) {
|
|
456
|
+
await userActivityContext.updateLocal(async function (localContext) {
|
|
457
|
+
if (!localContext.data[DATA_KEY_PRACTICES][restoredPractice.day]) {
|
|
458
|
+
localContext.data[DATA_KEY_PRACTICES][restoredPractice.day] = []
|
|
459
|
+
}
|
|
460
|
+
response.data.forEach((restoredPractice) => {
|
|
461
|
+
localContext.data[DATA_KEY_PRACTICES][restoredPractice.day].push({
|
|
462
|
+
id: restoredPractice.id,
|
|
463
|
+
duration_seconds: restoredPractice.duration_seconds,
|
|
464
|
+
})
|
|
465
|
+
})
|
|
436
466
|
})
|
|
437
|
-
}
|
|
467
|
+
}
|
|
438
468
|
}
|
|
439
|
-
const formattedMeta = await formatPracticeMeta(response.data)
|
|
469
|
+
const formattedMeta = await formatPracticeMeta(response.data || [])
|
|
440
470
|
const practiceDuration = formattedMeta.reduce(
|
|
441
471
|
(total, practice) => total + (practice.duration || 0),
|
|
442
472
|
0
|
|
@@ -598,7 +628,21 @@ export async function getPracticeNotes(day) {
|
|
|
598
628
|
* .catch(error => console.error("Failed to get recent activity:", error));
|
|
599
629
|
*/
|
|
600
630
|
export async function getRecentActivity({ page = 1, limit = 5, tabName = null } = {}) {
|
|
601
|
-
|
|
631
|
+
const recentActivityData = await fetchRecentUserActivities({ page, limit, tabName })
|
|
632
|
+
const contentIds = recentActivityData.data.map((p) => p.contentId).filter((id) => id !== null)
|
|
633
|
+
const contents = await addContextToContent(fetchByRailContentIds, contentIds, {
|
|
634
|
+
addNavigateTo: true,
|
|
635
|
+
addNextLesson: true,
|
|
636
|
+
})
|
|
637
|
+
recentActivityData.data = recentActivityData.data.map((practice) => {
|
|
638
|
+
const content = contents?.find((c) => c.id === practice.contentId) || {}
|
|
639
|
+
return {
|
|
640
|
+
...practice,
|
|
641
|
+
parent_id: content.parent_id || null,
|
|
642
|
+
navigateTo: content.navigateTo,
|
|
643
|
+
}
|
|
644
|
+
})
|
|
645
|
+
return recentActivityData
|
|
602
646
|
}
|
|
603
647
|
|
|
604
648
|
/**
|
|
@@ -647,10 +691,10 @@ function getStreaksAndMessage(practices) {
|
|
|
647
691
|
}
|
|
648
692
|
}
|
|
649
693
|
|
|
650
|
-
async function getUserPracticeIds(day =
|
|
694
|
+
async function getUserPracticeIds(day = dayjs().format('YYYY-MM-DD'), userId = null) {
|
|
651
695
|
let practices = {}
|
|
652
696
|
if (userId !== globalConfig.sessionConfig.userId) {
|
|
653
|
-
let data = await fetchUserPractices({ userId })
|
|
697
|
+
let data = await fetchUserPractices(0, { userId: userId })
|
|
654
698
|
practices = data?.['data']?.[DATA_KEY_PRACTICES] ?? {}
|
|
655
699
|
} else {
|
|
656
700
|
let data = await userActivityContext.getData()
|
|
@@ -677,6 +721,7 @@ function calculateStreaks(practices, includeStreakMessage = false) {
|
|
|
677
721
|
let lastActiveDay = null
|
|
678
722
|
let streakMessage = ''
|
|
679
723
|
|
|
724
|
+
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
680
725
|
let sortedPracticeDays = Object.keys(practices)
|
|
681
726
|
.map((dateStr) => {
|
|
682
727
|
const [year, month, day] = dateStr.split('-').map(Number)
|
|
@@ -728,9 +773,8 @@ function calculateStreaks(practices, includeStreakMessage = false) {
|
|
|
728
773
|
let yesterday = new Date(today)
|
|
729
774
|
yesterday.setDate(today.getDate() - 1)
|
|
730
775
|
|
|
731
|
-
let currentWeekStart = getMonday(today)
|
|
732
|
-
let lastWeekStart =
|
|
733
|
-
lastWeekStart.setDate(currentWeekStart.getDate() - 7)
|
|
776
|
+
let currentWeekStart = getMonday(today, timeZone)
|
|
777
|
+
let lastWeekStart = currentWeekStart.subtract(7, 'days')
|
|
734
778
|
|
|
735
779
|
let hasYesterdayPractice = sortedPracticeDays.some((date) => isSameDate(date, yesterday))
|
|
736
780
|
let hasCurrentWeekPractice = sortedPracticeDays.some((date) => date >= currentWeekStart)
|
|
@@ -856,15 +900,15 @@ export async function calculateLongestStreaks(userId = globalConfig.sessionConfi
|
|
|
856
900
|
}
|
|
857
901
|
}
|
|
858
902
|
|
|
859
|
-
async function formatPracticeMeta(practices) {
|
|
903
|
+
async function formatPracticeMeta(practices = []) {
|
|
860
904
|
const contentIds = practices.map((p) => p.content_id).filter((id) => id !== null)
|
|
861
|
-
const contents = await fetchByRailContentIds
|
|
862
|
-
|
|
863
|
-
|
|
905
|
+
const contents = await addContextToContent(fetchByRailContentIds, contentIds, {
|
|
906
|
+
addNavigateTo: true,
|
|
907
|
+
addNextLesson: true,
|
|
908
|
+
})
|
|
864
909
|
|
|
865
910
|
return practices.map((practice) => {
|
|
866
|
-
const
|
|
867
|
-
const content = contents.find((c) => c.id === practice.content_id) || {}
|
|
911
|
+
const content = contents && contents.length > 0 ? contents.find((c) => c.id === practice.content_id) : {}
|
|
868
912
|
|
|
869
913
|
return {
|
|
870
914
|
id: practice.id,
|
|
@@ -873,19 +917,22 @@ async function formatPracticeMeta(practices) {
|
|
|
873
917
|
thumbnail_url: practice.content_id ? content.thumbnail : practice.thumbnail_url || '',
|
|
874
918
|
duration: practice.duration_seconds || 0,
|
|
875
919
|
duration_seconds: practice.duration_seconds || 0,
|
|
876
|
-
content_url: content
|
|
920
|
+
content_url: content?.url || null,
|
|
877
921
|
title: practice.content_id ? content.title : practice.title,
|
|
878
922
|
category_id: practice.category_id,
|
|
879
923
|
instrument_id: practice.instrument_id,
|
|
880
|
-
content_type: getFormattedType(content
|
|
924
|
+
content_type: getFormattedType(content?.type || '', content?.brand || null),
|
|
881
925
|
content_id: practice.content_id || null,
|
|
882
|
-
content_brand: content
|
|
883
|
-
created_at:
|
|
926
|
+
content_brand: content?.brand || null,
|
|
927
|
+
created_at: dayjs(practice.created_at),
|
|
928
|
+
sanity_type: content?.type || null,
|
|
929
|
+
content_slug: content?.slug || null,
|
|
930
|
+
parent_id: content?.parent_id || null,
|
|
931
|
+
navigateTo: content?.navigateTo || null,
|
|
884
932
|
}
|
|
885
933
|
})
|
|
886
934
|
}
|
|
887
935
|
|
|
888
|
-
|
|
889
936
|
/**
|
|
890
937
|
* Records a new user activity in the system.
|
|
891
938
|
*
|
|
@@ -929,381 +976,464 @@ export async function deleteUserActivity(id) {
|
|
|
929
976
|
const url = `/api/user-management-system/v1/activities/${id}`
|
|
930
977
|
return await fetchHandler(url, 'DELETE')
|
|
931
978
|
}
|
|
979
|
+
|
|
932
980
|
/**
|
|
933
|
-
*
|
|
981
|
+
* Restores a specific user activity by its ID.
|
|
934
982
|
*
|
|
935
|
-
* @param {
|
|
936
|
-
* @
|
|
937
|
-
* @param {number} [options.limit=8] - Maximum number of progress rows to return.
|
|
938
|
-
* @returns {Promise<Object>} - A promise that resolves to an object containing progress rows formatted for UI.
|
|
983
|
+
* @param {number|string} id - The ID of the user activity to restore.
|
|
984
|
+
* @returns {Promise<Object>} - A promise that resolves to the API response after restoration.
|
|
939
985
|
*
|
|
940
986
|
* @example
|
|
941
|
-
*
|
|
942
|
-
* .then(
|
|
987
|
+
* restoreUserActivity(789)
|
|
988
|
+
* .then(response => console.log('Restored:', response))
|
|
943
989
|
* .catch(error => console.error(error));
|
|
944
990
|
*/
|
|
945
|
-
export async function
|
|
991
|
+
export async function restoreUserActivity(id) {
|
|
992
|
+
const url = `/api/user-management-system/v1/activities/${id}`
|
|
993
|
+
return await fetchHandler(url, 'POST')
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
async function extractPinnedItemsAndSortAllItems(
|
|
997
|
+
userPinnedItem,
|
|
998
|
+
contentsMap,
|
|
999
|
+
eligiblePlaylistItems,
|
|
1000
|
+
limit
|
|
1001
|
+
) {
|
|
1002
|
+
let pinnedItem = await popPinnedItemFromContentsOrPlaylistMap(
|
|
1003
|
+
userPinnedItem,
|
|
1004
|
+
contentsMap,
|
|
1005
|
+
eligiblePlaylistItems
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
let combined = []
|
|
1009
|
+
|
|
1010
|
+
if (pinnedItem) {
|
|
1011
|
+
pinnedItem.pinned = true
|
|
1012
|
+
combined.push(pinnedItem)
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
const progressList = Array.from(contentsMap.values())
|
|
1016
|
+
combined = [...combined, ...progressList, ...eligiblePlaylistItems]
|
|
1017
|
+
return mergeAndSortItems(combined, limit)
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function generateContentsMap(contents, playlistsContents) {
|
|
946
1021
|
const excludedTypes = new Set([
|
|
947
1022
|
'pack-bundle',
|
|
948
1023
|
'learning-path-course',
|
|
949
|
-
'learning-path-level'
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
const
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
const eligiblePlaylistItems = await getEligiblePlaylistItems(playlists);
|
|
958
|
-
const playlistEngagedOnContents = eligiblePlaylistItems.map(item => item.last_engaged_on);
|
|
959
|
-
const playlistsContents = await fetchByRailContentIds(playlistEngagedOnContents, 'progress-tracker');
|
|
960
|
-
const excludedParents = new Set();
|
|
961
|
-
const existingShows = new Set();
|
|
962
|
-
for (const item of playlistsContents) {
|
|
963
|
-
const contentId = item.id ?? item.railcontent_id;
|
|
964
|
-
excludedParents.add(contentId)
|
|
965
|
-
const parentIds = item.parent_content_data || [];
|
|
966
|
-
parentIds.forEach(id => excludedParents.add(id));
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
const progressContents = await getAllStartedOrCompleted({onlyIds: false, brand: brand, excludedIds: Array.from(excludedParents) });
|
|
970
|
-
const contents = await fetchByRailContentIds(Object.keys(progressContents), 'progress-tracker', brand);
|
|
971
|
-
const contentsMap = {};
|
|
972
|
-
contents.forEach(content => {
|
|
973
|
-
contentsMap[content.railcontent_id] = content;
|
|
974
|
-
});
|
|
975
|
-
const childToParentMap = {};
|
|
976
|
-
Object.values(contentsMap).forEach(content => {
|
|
1024
|
+
'learning-path-level',
|
|
1025
|
+
'guided-course-part',
|
|
1026
|
+
])
|
|
1027
|
+
const existingShows = new Set()
|
|
1028
|
+
const contentsMap = new Map()
|
|
1029
|
+
const childToParentMap = {}
|
|
1030
|
+
if (!contents) return contentsMap
|
|
1031
|
+
contents.forEach((content) => {
|
|
977
1032
|
if (Array.isArray(content.parent_content_data) && content.parent_content_data.length > 0) {
|
|
978
|
-
childToParentMap[content.id] =
|
|
1033
|
+
childToParentMap[content.id] =
|
|
1034
|
+
content.parent_content_data[content.parent_content_data.length - 1]
|
|
979
1035
|
}
|
|
980
|
-
})
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
const
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
if
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
existing.childIndex = id;
|
|
996
|
-
}
|
|
997
|
-
} else {
|
|
998
|
-
progressMap.set(parentId, {
|
|
999
|
-
id: parentId,
|
|
1000
|
-
raw: parentContent,
|
|
1001
|
-
state: progress.status,
|
|
1002
|
-
percent: progress.progress,
|
|
1003
|
-
progressTimestamp: progress.last_update * 1000,
|
|
1004
|
-
childIndex: id
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
continue;
|
|
1008
|
-
}
|
|
1009
|
-
// Handle standalone parents
|
|
1010
|
-
if (!progressMap.has(id)) {
|
|
1011
|
-
if(!existingShows.has(content.type)){
|
|
1012
|
-
progressMap.set(id, {
|
|
1013
|
-
id,
|
|
1014
|
-
raw: content,
|
|
1015
|
-
state: progress.status,
|
|
1016
|
-
percent: progress.progress,
|
|
1017
|
-
progressTimestamp: progress.last_update * 1000
|
|
1018
|
-
});
|
|
1036
|
+
})
|
|
1037
|
+
|
|
1038
|
+
const allRecentTypeSet = new Set(Object.values(recentTypes).flat())
|
|
1039
|
+
contents.forEach((content) => {
|
|
1040
|
+
const id = content.id
|
|
1041
|
+
const type = content.type
|
|
1042
|
+
if (
|
|
1043
|
+
excludedTypes.has(type) ||
|
|
1044
|
+
(!allRecentTypeSet.has(type) && !showsLessonTypes.includes(type))
|
|
1045
|
+
)
|
|
1046
|
+
return
|
|
1047
|
+
if (!childToParentMap[id]) {
|
|
1048
|
+
// Shows don't have a parent to link them, but need to be handled as if they're a set of children
|
|
1049
|
+
if (!existingShows.has(type)) {
|
|
1050
|
+
contentsMap.set(id, content)
|
|
1019
1051
|
}
|
|
1020
|
-
if(showsLessonTypes.includes(
|
|
1021
|
-
existingShows.add(
|
|
1052
|
+
if (showsLessonTypes.includes(type)) {
|
|
1053
|
+
existingShows.add(type)
|
|
1022
1054
|
}
|
|
1023
1055
|
}
|
|
1024
|
-
}
|
|
1025
|
-
const pinnedItem = await extractPinnedItem({
|
|
1026
|
-
brand,
|
|
1027
|
-
progressMap,
|
|
1028
|
-
playlistItems: eligiblePlaylistItems,
|
|
1029
1056
|
})
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1057
|
+
|
|
1058
|
+
// TODO this doesn't work for guided courses as the GC card takes precedence over the playlist card
|
|
1059
|
+
// https://musora.atlassian.net/browse/BEH-812
|
|
1060
|
+
if (playlistsContents) {
|
|
1061
|
+
for (const item of playlistsContents) {
|
|
1062
|
+
const contentId = item.id
|
|
1063
|
+
contentsMap.delete(contentId)
|
|
1064
|
+
const parentIds = item.parent_content_data || []
|
|
1065
|
+
parentIds.forEach((id) => contentsMap.delete(id))
|
|
1066
|
+
}
|
|
1033
1067
|
}
|
|
1068
|
+
return contentsMap
|
|
1069
|
+
}
|
|
1034
1070
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1071
|
+
/**
|
|
1072
|
+
* Fetches and combines recent user progress rows and playlists, excluding certain types and parents.
|
|
1073
|
+
*
|
|
1074
|
+
* @param {Object} [options={}] - Options for fetching progress rows.
|
|
1075
|
+
* @param {string|null} [options.brand=null] - The brand context for progress data.
|
|
1076
|
+
* @param {number} [options.limit=8] - Maximum number of progress rows to return.
|
|
1077
|
+
* @returns {Promise<Object>} - A promise that resolves to an object containing progress rows formatted for UI.
|
|
1078
|
+
*
|
|
1079
|
+
* @example
|
|
1080
|
+
* getProgressRows({ brand: 'drumeo', limit: 10 })
|
|
1081
|
+
* .then(data => console.log(data))
|
|
1082
|
+
* .catch(error => console.error(error));
|
|
1083
|
+
*/
|
|
1084
|
+
export async function getProgressRows({ brand = null, limit = 8 } = {}) {
|
|
1085
|
+
// TODO slice progress to a reasonable number, say 100
|
|
1086
|
+
|
|
1087
|
+
const [recentPlaylists, progressContents, userPinnedItem] =
|
|
1088
|
+
await Promise.all([
|
|
1089
|
+
fetchUserPlaylists(brand, { sort: '-last_progress', limit: limit }),
|
|
1090
|
+
getAllStartedOrCompleted({ onlyIds: false, brand: brand }),
|
|
1091
|
+
getUserPinnedItem(brand),
|
|
1092
|
+
])
|
|
1093
|
+
|
|
1094
|
+
const playlists = recentPlaylists?.data || []
|
|
1095
|
+
const eligiblePlaylistItems = await getEligiblePlaylistItems(playlists)
|
|
1096
|
+
const playlistEngagedOnContents = eligiblePlaylistItems.map(
|
|
1097
|
+
(item) => item.playlist.last_engaged_on
|
|
1098
|
+
)
|
|
1044
1099
|
|
|
1045
|
-
const
|
|
1100
|
+
const nonPlaylistContentIds = Object.keys(progressContents)
|
|
1101
|
+
if (userPinnedItem?.progressType === 'content') {
|
|
1102
|
+
nonPlaylistContentIds.push(userPinnedItem.id)
|
|
1103
|
+
}
|
|
1046
1104
|
|
|
1105
|
+
const [playlistsContents, contents] = await Promise.all([
|
|
1106
|
+
playlistEngagedOnContents ? addContextToContent(fetchByRailContentIds, playlistEngagedOnContents, 'progress-tracker', {
|
|
1107
|
+
addNextLesson: true,
|
|
1108
|
+
addNavigateTo: true,
|
|
1109
|
+
addProgressStatus: true,
|
|
1110
|
+
addProgressPercentage: true,
|
|
1111
|
+
addProgressTimestamp: true,
|
|
1112
|
+
}) : Promise.resolve([]),
|
|
1113
|
+
nonPlaylistContentIds ? addContextToContent(fetchByRailContentIds, nonPlaylistContentIds, 'progress-tracker', brand, {
|
|
1114
|
+
addNextLesson: true,
|
|
1115
|
+
addNavigateTo: true,
|
|
1116
|
+
addProgressStatus: true,
|
|
1117
|
+
addProgressPercentage: true,
|
|
1118
|
+
addProgressTimestamp: true,
|
|
1119
|
+
}) : Promise.resolve([]),
|
|
1120
|
+
])
|
|
1121
|
+
const contentsMap = generateContentsMap(contents, playlistsContents)
|
|
1122
|
+
let combined = await extractPinnedItemsAndSortAllItems(
|
|
1123
|
+
userPinnedItem,
|
|
1124
|
+
contentsMap,
|
|
1125
|
+
eligiblePlaylistItems,
|
|
1126
|
+
limit
|
|
1127
|
+
)
|
|
1047
1128
|
const results = await Promise.all(
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
: processContentItem(item)
|
|
1052
|
-
|
|
1053
|
-
)
|
|
1054
|
-
|
|
1129
|
+
combined
|
|
1130
|
+
.slice(0, limit)
|
|
1131
|
+
.map((item) =>
|
|
1132
|
+
item.type === 'playlist' ? processPlaylistItem(item) : processContentItem(item)
|
|
1133
|
+
)
|
|
1134
|
+
)
|
|
1055
1135
|
return {
|
|
1056
1136
|
type: TabResponseType.PROGRESS_ROWS,
|
|
1057
1137
|
displayBrowseAll: combined.length > limit,
|
|
1058
|
-
data: results
|
|
1059
|
-
}
|
|
1138
|
+
data: results,
|
|
1139
|
+
}
|
|
1060
1140
|
}
|
|
1061
1141
|
|
|
1062
|
-
async function
|
|
1063
|
-
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (contentType === 'transcription' || contentType === 'play-along' || contentType === 'jam-track') ctaText = 'Replay Song';
|
|
1069
|
-
if (contentType === 'lesson') ctaText = status === 'completed' ? 'Revisit Lesson' : 'Continue';
|
|
1070
|
-
if ((contentType === 'guided course' || contentType === 'song tutorial' || collectionLessonTypes.includes(contentType)) && status === 'completed') ctaText = 'Revisit Lessons' ;
|
|
1071
|
-
if (contentType === 'pack' && status === 'completed') {
|
|
1072
|
-
ctaText = 'View Lessons';
|
|
1073
|
-
}
|
|
1142
|
+
async function getUserPinnedItem(brand) {
|
|
1143
|
+
const userRaw = await globalConfig.localStorage.getItem('user')
|
|
1144
|
+
const user = userRaw ? JSON.parse(userRaw) : {}
|
|
1145
|
+
user.brand_pinned_progress = user.brand_pinned_progress || {}
|
|
1146
|
+
return user.brand_pinned_progress[brand] ?? null
|
|
1147
|
+
}
|
|
1074
1148
|
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
}
|
|
1097
|
-
}))
|
|
1098
|
-
);
|
|
1099
|
-
|
|
1100
|
-
const lessons = (nestedLessons.length === 0) ? data.lessons : nestedLessons
|
|
1101
|
-
const nextLesson = lessons.find(lesson => lesson.id === nextId)
|
|
1102
|
-
data.first_incomplete_child = nextLesson?.parent ?? nextLesson
|
|
1103
|
-
data.second_incomplete_child = (nextLesson?.parent) ? nextLesson : null
|
|
1104
|
-
if(data.type === 'challenge' && nextByProgress !== undefined ){
|
|
1105
|
-
const challenge = await fetchChallengeLessonData(nextByProgress)
|
|
1106
|
-
if(challenge.lesson.is_locked) {
|
|
1107
|
-
const timeRemaining = getTimeRemainingUntilLocal(challenge.lesson.unlock_date, {withTotalSeconds:true})
|
|
1108
|
-
data.is_locked = true
|
|
1109
|
-
data.time_remaining_seconds = timeRemaining.totalSeconds
|
|
1110
|
-
ctaText = 'Next lesson in ' + timeRemaining.formatted
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1149
|
+
async function processContentItem(content) {
|
|
1150
|
+
const contentType = getFormattedType(content.type, content.brand)
|
|
1151
|
+
const isLive = content.isLive ?? false
|
|
1152
|
+
let ctaText = getDefaultCTATextForContent(content, contentType)
|
|
1153
|
+
|
|
1154
|
+
content.completed_children = await getCompletedChildren(content, contentType)
|
|
1155
|
+
|
|
1156
|
+
if (content.type === 'guided-course') {
|
|
1157
|
+
const nextLessonPublishedOn = content.children.find(
|
|
1158
|
+
(child) => child.id === content.navigateTo.id
|
|
1159
|
+
)?.published_on
|
|
1160
|
+
let isLocked = new Date(nextLessonPublishedOn) > new Date()
|
|
1161
|
+
if (isLocked) {
|
|
1162
|
+
content.is_locked = true
|
|
1163
|
+
const timeRemaining = getTimeRemainingUntilLocal(nextLessonPublishedOn, {
|
|
1164
|
+
withTotalSeconds: true,
|
|
1165
|
+
})
|
|
1166
|
+
content.time_remaining_seconds = timeRemaining.totalSeconds
|
|
1167
|
+
ctaText = 'Next lesson in ' + timeRemaining.formatted
|
|
1168
|
+
} else if (!content.progressStatus || content.progressStatus === 'not-started' || content.progressPercentage === 0) {
|
|
1169
|
+
ctaText = 'Start Course'
|
|
1113
1170
|
}
|
|
1114
1171
|
}
|
|
1115
1172
|
|
|
1116
|
-
if(contentType
|
|
1117
|
-
const shows = await fetchShows(
|
|
1118
|
-
const showIds = shows.map(item => item.id)
|
|
1119
|
-
const progressOnItems = await getProgressStateByIds(showIds)
|
|
1120
|
-
const
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1173
|
+
if (contentType === 'show') {
|
|
1174
|
+
const shows = await fetchShows(content.brand, content.type)
|
|
1175
|
+
const showIds = shows.map((item) => item.id)
|
|
1176
|
+
const progressOnItems = await getProgressStateByIds(showIds)
|
|
1177
|
+
const completedShows = content.completed_children
|
|
1178
|
+
const progressTimestamp = content.progressTimestamp
|
|
1179
|
+
const wasPinned = content.pinned ?? false
|
|
1180
|
+
if (content.progressStatus === 'completed') {
|
|
1181
|
+
// this could be handled more gracefully if their was a parent content type for shows
|
|
1182
|
+
const nextByProgress = findIncompleteLesson(progressOnItems, content.id, content.type)
|
|
1183
|
+
content = shows.find((lesson) => lesson.id === nextByProgress)
|
|
1184
|
+
content.completed_children = completedShows
|
|
1185
|
+
content.progressTimestamp = progressTimestamp
|
|
1186
|
+
content.pinned = wasPinned
|
|
1124
1187
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
ctaText = 'Revisit Lessons';
|
|
1188
|
+
content.child_count = shows.length
|
|
1189
|
+
content.progressPercentage = Math.round((completedShows / shows.length) * 100)
|
|
1190
|
+
if (completedShows === shows.length) {
|
|
1191
|
+
ctaText = 'Revisit Show'
|
|
1130
1192
|
}
|
|
1131
1193
|
}
|
|
1132
1194
|
|
|
1133
1195
|
return {
|
|
1134
|
-
id:
|
|
1196
|
+
id: content.id,
|
|
1135
1197
|
progressType: 'content',
|
|
1136
1198
|
header: contentType,
|
|
1137
|
-
pinned:
|
|
1199
|
+
pinned: content.pinned ?? false,
|
|
1200
|
+
content: content,
|
|
1138
1201
|
body: {
|
|
1139
|
-
progressPercent:
|
|
1140
|
-
thumbnail:
|
|
1141
|
-
title:
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1202
|
+
progressPercent: isLive ? undefined : content.progressPercentage,
|
|
1203
|
+
thumbnail: content.thumbnail,
|
|
1204
|
+
title: content.title,
|
|
1205
|
+
isLive: isLive,
|
|
1206
|
+
badge: content.badge ?? null,
|
|
1207
|
+
isLocked: content.is_locked ?? false,
|
|
1208
|
+
subtitle: collectionLessonTypes.includes(content.type) || content.lesson_count > 1
|
|
1209
|
+
? `${content.completed_children} of ${content.lesson_count ?? content.child_count} Lessons Complete`
|
|
1210
|
+
: (contentType === 'lesson' && isLive === false) ? `${content.progressPercentage}% Complete`: `${content.difficulty_string} • ${content.artist_name}`
|
|
1147
1211
|
},
|
|
1148
|
-
cta:
|
|
1149
|
-
text:
|
|
1150
|
-
timeRemainingToUnlockSeconds:
|
|
1212
|
+
cta: {
|
|
1213
|
+
text: ctaText,
|
|
1214
|
+
timeRemainingToUnlockSeconds: content.time_remaining_seconds ?? null,
|
|
1151
1215
|
action: {
|
|
1152
|
-
type:
|
|
1153
|
-
brand:
|
|
1154
|
-
id:
|
|
1155
|
-
slug:
|
|
1156
|
-
child:
|
|
1157
|
-
|
|
1158
|
-
id: data.first_incomplete_child.id,
|
|
1159
|
-
type: data.first_incomplete_child.type,
|
|
1160
|
-
brand: data.first_incomplete_child.brand,
|
|
1161
|
-
slug: data.first_incomplete_child.slug,
|
|
1162
|
-
child: data.second_incomplete_child
|
|
1163
|
-
? {
|
|
1164
|
-
id: data.second_incomplete_child.id,
|
|
1165
|
-
type: data.second_incomplete_child.type,
|
|
1166
|
-
brand: data.second_incomplete_child.brand,
|
|
1167
|
-
slug: data.second_incomplete_child.slug
|
|
1168
|
-
}
|
|
1169
|
-
: null
|
|
1170
|
-
}
|
|
1171
|
-
: null
|
|
1172
|
-
}
|
|
1216
|
+
type: content.type,
|
|
1217
|
+
brand: content.brand,
|
|
1218
|
+
id: content.id,
|
|
1219
|
+
slug: content.slug,
|
|
1220
|
+
child: content.navigateTo,
|
|
1221
|
+
},
|
|
1173
1222
|
},
|
|
1174
|
-
|
|
1175
|
-
|
|
1223
|
+
// *1000 is to match playlists which are saved in millisecond accuracy
|
|
1224
|
+
progressTimestamp: content.progressTimestamp * 1000,
|
|
1225
|
+
}
|
|
1176
1226
|
}
|
|
1177
1227
|
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if (
|
|
1191
|
-
nextItem = playlist.items[index + 1] ?? nextItem;
|
|
1192
|
-
} else {
|
|
1193
|
-
nextItem = playlist.items[index] ?? nextItem;
|
|
1194
|
-
}
|
|
1228
|
+
function getDefaultCTATextForContent(content, contentType) {
|
|
1229
|
+
let ctaText = 'Continue'
|
|
1230
|
+
if (content.progressStatus === 'completed') {
|
|
1231
|
+
if (
|
|
1232
|
+
contentType === songs[content.brand] ||
|
|
1233
|
+
contentType === 'play along' ||
|
|
1234
|
+
contentType === 'jam track'
|
|
1235
|
+
)
|
|
1236
|
+
ctaText = 'Replay Song'
|
|
1237
|
+
if (contentType === 'lesson') ctaText = 'Revisit Lesson'
|
|
1238
|
+
if (contentType === 'song tutorial' || collectionLessonTypes.includes(content.type))
|
|
1239
|
+
ctaText = 'Revisit Lessons'
|
|
1240
|
+
if (contentType === 'pack') ctaText = 'View Lessons'
|
|
1195
1241
|
}
|
|
1242
|
+
return ctaText
|
|
1243
|
+
}
|
|
1196
1244
|
|
|
1245
|
+
async function getCompletedChildren(content, contentType) {
|
|
1246
|
+
let completedChildren = null
|
|
1247
|
+
if (contentType === 'show') {
|
|
1248
|
+
const shows = await addContextToContent(fetchShows, content.brand, content.type, {
|
|
1249
|
+
addProgressStatus: true,
|
|
1250
|
+
})
|
|
1251
|
+
completedChildren = Object.values(shows).filter(
|
|
1252
|
+
(show) => show.progressStatus === 'completed'
|
|
1253
|
+
).length
|
|
1254
|
+
} else if (content.lesson_count > 0) {
|
|
1255
|
+
const lessonIds = getLeafNodes(content)
|
|
1256
|
+
const progressOnItems = await getProgressStateByIds(lessonIds)
|
|
1257
|
+
completedChildren = Object.values(progressOnItems).filter(
|
|
1258
|
+
(value) => value === 'completed'
|
|
1259
|
+
).length
|
|
1260
|
+
}
|
|
1261
|
+
return completedChildren
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
async function processPlaylistItem(item) {
|
|
1265
|
+
const playlist = item.playlist
|
|
1197
1266
|
return {
|
|
1198
|
-
id:
|
|
1199
|
-
progressType:
|
|
1200
|
-
header:
|
|
1201
|
-
pinned:
|
|
1202
|
-
|
|
1267
|
+
id: playlist.id,
|
|
1268
|
+
progressType: 'playlist',
|
|
1269
|
+
header: 'playlist',
|
|
1270
|
+
pinned: item.pinned ?? false,
|
|
1271
|
+
playlist: playlist,
|
|
1272
|
+
body: {
|
|
1203
1273
|
first_items_thumbnail_url: playlist.first_items_thumbnail_url,
|
|
1204
|
-
title:
|
|
1205
|
-
subtitle:
|
|
1206
|
-
total_items:
|
|
1274
|
+
title: playlist.name,
|
|
1275
|
+
subtitle: `${playlist.duration_formated} • ${playlist.total_items} items • ${playlist.likes} likes • ${playlist.user.display_name}`,
|
|
1276
|
+
total_items: playlist.total_items,
|
|
1207
1277
|
},
|
|
1208
1278
|
progressTimestamp: item.progressTimestamp,
|
|
1209
|
-
cta:
|
|
1210
|
-
text:
|
|
1279
|
+
cta: {
|
|
1280
|
+
text: 'Continue',
|
|
1211
1281
|
action: {
|
|
1212
|
-
brand:
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
type:
|
|
1216
|
-
|
|
1217
|
-
|
|
1282
|
+
brand: playlist.brand,
|
|
1283
|
+
item_id: playlist.navigateTo.id ?? null,
|
|
1284
|
+
content_id: playlist.navigateTo.content_id ?? null,
|
|
1285
|
+
type: 'playlists',
|
|
1286
|
+
// TODO depreciated, maintained to avoid breaking changes
|
|
1287
|
+
id: playlist.id,
|
|
1288
|
+
},
|
|
1289
|
+
},
|
|
1218
1290
|
}
|
|
1219
1291
|
}
|
|
1220
1292
|
|
|
1221
1293
|
const getFormattedType = (type, brand) => {
|
|
1222
1294
|
for (const [key, values] of Object.entries(progressTypesMapping)) {
|
|
1223
1295
|
if (values.includes(type)) {
|
|
1224
|
-
return key === 'songs' ? songs[brand] : key
|
|
1296
|
+
return key === 'songs' ? songs[brand] : key
|
|
1225
1297
|
}
|
|
1226
1298
|
}
|
|
1227
1299
|
|
|
1228
|
-
return null
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
|
-
function
|
|
1232
|
-
const ids = []
|
|
1233
|
-
function traverse(
|
|
1234
|
-
for (const item of
|
|
1235
|
-
if (item.
|
|
1236
|
-
traverse(item.
|
|
1237
|
-
}else if (item.id) {
|
|
1238
|
-
ids.push(item.id)
|
|
1300
|
+
return null
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
function getLeafNodes(content) {
|
|
1304
|
+
const ids = []
|
|
1305
|
+
function traverse(children) {
|
|
1306
|
+
for (const item of children) {
|
|
1307
|
+
if (item.children) {
|
|
1308
|
+
traverse(item.children) // Recursively handle nested lessons
|
|
1309
|
+
} else if (item.id) {
|
|
1310
|
+
ids.push(item.id)
|
|
1239
1311
|
}
|
|
1240
1312
|
}
|
|
1241
1313
|
}
|
|
1242
|
-
if (
|
|
1243
|
-
traverse(
|
|
1314
|
+
if (content && Array.isArray(content.children)) {
|
|
1315
|
+
traverse(content.children)
|
|
1244
1316
|
}
|
|
1245
|
-
|
|
1246
|
-
return ids;
|
|
1317
|
+
return ids
|
|
1247
1318
|
}
|
|
1248
1319
|
|
|
1249
|
-
|
|
1250
1320
|
async function getEligiblePlaylistItems(playlists) {
|
|
1251
|
-
const eligible = playlists.filter(p => p.last_progress && p.last_engaged_on)
|
|
1321
|
+
const eligible = playlists.filter((p) => p.last_progress && p.last_engaged_on)
|
|
1252
1322
|
return Promise.all(
|
|
1253
|
-
eligible.map(async p => {
|
|
1254
|
-
const utcDate = new Date(p.last_progress.replace(' ', 'T') + 'Z')
|
|
1255
|
-
const timestamp = utcDate.getTime()
|
|
1323
|
+
eligible.map(async (p) => {
|
|
1324
|
+
const utcDate = new Date(p.last_progress.replace(' ', 'T') + 'Z')
|
|
1325
|
+
const timestamp = utcDate.getTime()
|
|
1256
1326
|
return {
|
|
1257
1327
|
type: 'playlist',
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1328
|
+
// Content timestamps are millisecond accurate so for comparison we bring this to the same resolution
|
|
1329
|
+
progressTimestamp: timestamp / 1000,
|
|
1330
|
+
playlist: p,
|
|
1331
|
+
id: p.id,
|
|
1332
|
+
}
|
|
1262
1333
|
})
|
|
1263
|
-
)
|
|
1334
|
+
)
|
|
1264
1335
|
}
|
|
1265
1336
|
|
|
1266
1337
|
function mergeAndSortItems(items, limit) {
|
|
1267
|
-
const seen = new Set()
|
|
1268
|
-
const deduped = []
|
|
1338
|
+
const seen = new Set()
|
|
1339
|
+
const deduped = []
|
|
1269
1340
|
|
|
1270
1341
|
for (const item of items) {
|
|
1271
|
-
const key = `${item.id}-${item.type
|
|
1342
|
+
const key = `${item.id}-${item.type}`
|
|
1272
1343
|
if (!seen.has(key)) {
|
|
1273
|
-
seen.add(key)
|
|
1274
|
-
deduped.push(item)
|
|
1344
|
+
seen.add(key)
|
|
1345
|
+
deduped.push(item)
|
|
1275
1346
|
}
|
|
1276
1347
|
}
|
|
1277
1348
|
|
|
1278
1349
|
return deduped
|
|
1279
|
-
.filter(item => typeof item.progressTimestamp === 'number' && item.progressTimestamp
|
|
1350
|
+
.filter((item) => typeof item.progressTimestamp === 'number' && item.progressTimestamp >= 0)
|
|
1280
1351
|
.sort((a, b) => {
|
|
1281
|
-
if (a.pinned && !b.pinned) return -1
|
|
1282
|
-
if (!a.pinned && b.pinned) return 1
|
|
1283
|
-
|
|
1352
|
+
if (a.pinned && !b.pinned) return -1
|
|
1353
|
+
if (!a.pinned && b.pinned) return 1
|
|
1354
|
+
// TODO pinned guided course should always be before user pinned item
|
|
1355
|
+
return b.progressTimestamp - a.progressTimestamp
|
|
1284
1356
|
})
|
|
1285
|
-
.slice(0, limit + 5)
|
|
1357
|
+
.slice(0, limit + 5)
|
|
1286
1358
|
}
|
|
1287
1359
|
|
|
1288
|
-
function findIncompleteLesson(progressOnItems, currentContentId, contentType) {
|
|
1289
|
-
const ids = Object.keys(progressOnItems).map(Number)
|
|
1290
|
-
if (contentType === '
|
|
1360
|
+
export function findIncompleteLesson(progressOnItems, currentContentId, contentType) {
|
|
1361
|
+
const ids = Object.keys(progressOnItems).map(Number)
|
|
1362
|
+
if (contentType === 'guided-course') {
|
|
1291
1363
|
// Return first incomplete lesson
|
|
1292
|
-
return ids.find(id => progressOnItems[id] !== 'completed') || ids.at(0)
|
|
1364
|
+
return ids.find((id) => progressOnItems[id] !== 'completed') || ids.at(0)
|
|
1293
1365
|
}
|
|
1294
1366
|
|
|
1295
1367
|
// For other types, find next incomplete after current
|
|
1296
|
-
const currentIndex = ids.indexOf(Number(currentContentId))
|
|
1297
|
-
if (currentIndex === -1) return null
|
|
1368
|
+
const currentIndex = ids.indexOf(Number(currentContentId))
|
|
1369
|
+
if (currentIndex === -1) return null
|
|
1298
1370
|
|
|
1299
1371
|
for (let i = currentIndex + 1; i < ids.length; i++) {
|
|
1300
|
-
const id = ids[i]
|
|
1372
|
+
const id = ids[i]
|
|
1301
1373
|
if (progressOnItems[id] !== 'completed') {
|
|
1302
|
-
return id
|
|
1374
|
+
return id
|
|
1303
1375
|
}
|
|
1304
1376
|
}
|
|
1305
1377
|
|
|
1306
|
-
return ids[0]
|
|
1378
|
+
return ids[0]
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
async function popPinnedItemFromContentsOrPlaylistMap(pinned, contentsMap, playlistItems) {
|
|
1382
|
+
if (!pinned) return null
|
|
1383
|
+
const { id, pinnedAt } = pinned
|
|
1384
|
+
let item = null
|
|
1385
|
+
const progressType = pinned.progressType ?? pinned.type;
|
|
1386
|
+
|
|
1387
|
+
if (progressType === 'content') {
|
|
1388
|
+
const pinnedId = parseInt(id)
|
|
1389
|
+
if (contentsMap.has(pinnedId)) {
|
|
1390
|
+
item = contentsMap.get(pinnedId)
|
|
1391
|
+
contentsMap.delete(pinnedId)
|
|
1392
|
+
} else {
|
|
1393
|
+
// we use fetchByRailContentIds so that we don't have the _type restriction in the query
|
|
1394
|
+
let data = await fetchByRailContentIds([id], 'progress-tracker')
|
|
1395
|
+
item = await addContextToContent(() => data[0] ?? null, {
|
|
1396
|
+
addNextLesson: true,
|
|
1397
|
+
addNavigateTo: true,
|
|
1398
|
+
addProgressStatus: true,
|
|
1399
|
+
addProgressPercentage: true,
|
|
1400
|
+
addProgressTimestamp: true,
|
|
1401
|
+
})
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
if (progressType === 'playlist') {
|
|
1405
|
+
const pinnedPlaylist = playlistItems.find((p) => p.playlist.id === id)
|
|
1406
|
+
if (pinnedPlaylist) {
|
|
1407
|
+
playlistItems = playlistItems.filter((p) => p.playlist.id !== id)
|
|
1408
|
+
item = pinnedPlaylist
|
|
1409
|
+
} else {
|
|
1410
|
+
const playlist = await fetchPlaylist(id)
|
|
1411
|
+
item = {
|
|
1412
|
+
id: id,
|
|
1413
|
+
playlist: playlist,
|
|
1414
|
+
type: 'playlist',
|
|
1415
|
+
progressTimestamp: new Date(pinnedAt).getTime(),
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
return item
|
|
1420
|
+
}
|
|
1421
|
+
|
|
1422
|
+
function popContentAndRemoveChildrenFromContentsMap(content, contentsMap) {
|
|
1423
|
+
if (!content.children || content.children.length === 0){
|
|
1424
|
+
console.warn(`content ${content.id} has no children`, content);
|
|
1425
|
+
} else {
|
|
1426
|
+
const children = content.children.map((child) => child.id)
|
|
1427
|
+
if (contentsMap.has(content.id)) {
|
|
1428
|
+
contentsMap.delete(content.id)
|
|
1429
|
+
}
|
|
1430
|
+
children.forEach((child) => {
|
|
1431
|
+
if (contentsMap.has(child)) {
|
|
1432
|
+
contentsMap.delete(child)
|
|
1433
|
+
}
|
|
1434
|
+
})
|
|
1435
|
+
}
|
|
1436
|
+
return contentsMap
|
|
1307
1437
|
}
|
|
1308
1438
|
|
|
1309
1439
|
/**
|
|
@@ -1320,16 +1450,16 @@ function findIncompleteLesson(progressOnItems, currentContentId, contentType) {
|
|
|
1320
1450
|
* .catch(error => console.error(error));
|
|
1321
1451
|
*/
|
|
1322
1452
|
export async function pinProgressRow(brand, id, progressType) {
|
|
1323
|
-
const url = `/api/user-management-system/v1/progress/pin?brand=${brand}&id=${id}&progressType=${progressType}
|
|
1453
|
+
const url = `/api/user-management-system/v1/progress/pin?brand=${brand}&id=${id}&progressType=${progressType}`
|
|
1324
1454
|
const response = await fetchHandler(url, 'PUT', null)
|
|
1325
1455
|
if (response && !response.error) {
|
|
1326
|
-
await
|
|
1456
|
+
await updateUserPinnedProgressRow(brand, {
|
|
1327
1457
|
id,
|
|
1328
1458
|
progressType,
|
|
1329
1459
|
pinnedAt: new Date().toISOString(),
|
|
1330
|
-
})
|
|
1460
|
+
})
|
|
1331
1461
|
}
|
|
1332
|
-
return response
|
|
1462
|
+
return response
|
|
1333
1463
|
}
|
|
1334
1464
|
/**
|
|
1335
1465
|
* Unpins the current pinned progress row for a user, scoped by brand.
|
|
@@ -1338,7 +1468,7 @@ export async function pinProgressRow(brand, id, progressType) {
|
|
|
1338
1468
|
* @returns {Promise<Object>} - A promise resolving to the response from the unpin API.
|
|
1339
1469
|
*
|
|
1340
1470
|
* @example
|
|
1341
|
-
* unpinProgressRow('drumeo')
|
|
1471
|
+
* unpinProgressRow('drumeo', 123456)
|
|
1342
1472
|
* .then(response => console.log(response))
|
|
1343
1473
|
* .catch(error => console.error(error));
|
|
1344
1474
|
*/
|
|
@@ -1346,84 +1476,35 @@ export async function unpinProgressRow(brand) {
|
|
|
1346
1476
|
const url = `/api/user-management-system/v1/progress/unpin?brand=${brand}`
|
|
1347
1477
|
const response = await fetchHandler(url, 'PUT', null)
|
|
1348
1478
|
if (response && !response.error) {
|
|
1349
|
-
await
|
|
1479
|
+
await updateUserPinnedProgressRow(brand, null)
|
|
1350
1480
|
}
|
|
1351
1481
|
return response
|
|
1352
1482
|
}
|
|
1353
1483
|
|
|
1354
|
-
async function
|
|
1355
|
-
const userRaw = await globalConfig.localStorage.getItem('user')
|
|
1356
|
-
const user = userRaw ? JSON.parse(userRaw) : {}
|
|
1484
|
+
async function updateUserPinnedProgressRow(brand, pinnedData) {
|
|
1485
|
+
const userRaw = await globalConfig.localStorage.getItem('user')
|
|
1486
|
+
const user = userRaw ? JSON.parse(userRaw) : {}
|
|
1357
1487
|
user.brand_pinned_progress = user.brand_pinned_progress || {}
|
|
1358
1488
|
user.brand_pinned_progress[brand] = pinnedData
|
|
1359
1489
|
await globalConfig.localStorage.setItem('user', JSON.stringify(user))
|
|
1360
1490
|
}
|
|
1361
1491
|
|
|
1362
|
-
async function
|
|
1363
|
-
const
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
const pinned = user.brand_pinned_progress[brand]
|
|
1368
|
-
if (!pinned) return null
|
|
1369
|
-
|
|
1370
|
-
const {id, progressType, pinnedAt} = pinned
|
|
1492
|
+
export async function fetchRecentActivitiesActiveTabs() {
|
|
1493
|
+
const url = `/api/user-management-system/v1/activities/tabs`
|
|
1494
|
+
try {
|
|
1495
|
+
const tabs = await fetchHandler(url, 'GET')
|
|
1496
|
+
const activitiesTabs = []
|
|
1371
1497
|
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
const item = progressMap.get(pinnedId)
|
|
1376
|
-
progressMap.delete(pinnedId)
|
|
1377
|
-
return item
|
|
1378
|
-
} else {
|
|
1379
|
-
const content = await fetchByRailContentIds([`${pinnedId}`], 'progress-tracker')
|
|
1380
|
-
const firstLessonId = getFirstLeafLessonId(content[0])
|
|
1381
|
-
return {
|
|
1382
|
-
id: pinnedId,
|
|
1383
|
-
state: 'started',
|
|
1384
|
-
percent: 0,
|
|
1385
|
-
raw: content[0],
|
|
1386
|
-
progressTimestamp: new Date(pinnedAt).getTime(),
|
|
1387
|
-
childIndex: firstLessonId
|
|
1388
|
-
}
|
|
1389
|
-
}
|
|
1390
|
-
}
|
|
1391
|
-
if (progressType === 'playlist') {
|
|
1392
|
-
const pinnedPlaylist = playlistItems.find(p => p.raw.id === id)
|
|
1393
|
-
if (pinnedPlaylist) {
|
|
1394
|
-
return pinnedPlaylist
|
|
1395
|
-
}else{
|
|
1396
|
-
const playlist = await fetchPlaylist(id)
|
|
1397
|
-
return {
|
|
1398
|
-
id: id,
|
|
1399
|
-
raw: playlist,
|
|
1400
|
-
progressTimestamp: new Date(pinnedAt).getTime(),
|
|
1401
|
-
type: 'playlist',
|
|
1402
|
-
last_engaged_on: playlist.items[0],
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
return null
|
|
1408
|
-
}
|
|
1498
|
+
tabs.forEach((tab) => {
|
|
1499
|
+
activitiesTabs.push({ name: tab.label, short_name: tab.label })
|
|
1500
|
+
})
|
|
1409
1501
|
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
return item.id || null
|
|
1415
|
-
}
|
|
1416
|
-
const found = findFirstLeaf(item.lessons)
|
|
1417
|
-
if (found) return found
|
|
1418
|
-
}
|
|
1419
|
-
return null
|
|
1502
|
+
return activitiesTabs
|
|
1503
|
+
} catch (error) {
|
|
1504
|
+
console.error('Error fetching activity tabs:', error)
|
|
1505
|
+
return []
|
|
1420
1506
|
}
|
|
1421
|
-
|
|
1422
|
-
return data.lessons ? findFirstLeaf(data.lessons) : null
|
|
1423
1507
|
}
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
1508
|
</code></pre>
|
|
1428
1509
|
</article>
|
|
1429
1510
|
</section>
|
|
@@ -1438,7 +1519,7 @@ function getFirstLeafLessonId(data) {
|
|
|
1438
1519
|
<br class="clear">
|
|
1439
1520
|
|
|
1440
1521
|
<footer>
|
|
1441
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on
|
|
1522
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Thu Oct 02 2025 11:46:05 GMT+0300 (Eastern European Summer Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
1442
1523
|
</footer>
|
|
1443
1524
|
|
|
1444
1525
|
<script>prettyPrint();</script>
|