musora-content-services 1.6.7 → 1.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.github/workflows/conventional-commits.yaml +0 -0
  2. package/.github/workflows/docs.js.yml +0 -0
  3. package/.github/workflows/sync-docs.yml +0 -0
  4. package/CHANGELOG.md +4 -0
  5. package/README.md +0 -0
  6. package/docs/algolia.js.html +0 -0
  7. package/docs/config.js.html +0 -0
  8. package/docs/index.html +0 -0
  9. package/docs/module-Config.html +0 -0
  10. package/docs/module-Railcontent-Services.html +0 -0
  11. package/docs/module-Sanity-Services.html +0 -0
  12. package/docs/module-Search-Engine-Services.html +0 -0
  13. package/docs/railcontent.js.html +0 -0
  14. package/docs/sanity.js.html +0 -0
  15. package/docs/v2/ContentOrganization.html +2 -2
  16. package/docs/v2/Forums.html +269 -0
  17. package/docs/v2/Gamification.html +2 -2
  18. package/docs/v2/TestUser.html +260 -0
  19. package/docs/v2/UserManagementSystem.html +2 -2
  20. package/docs/v2/api_types.js.html +2 -2
  21. package/docs/v2/config.js.html +2 -8
  22. package/docs/v2/content-org_content-org.js.html +2 -2
  23. package/docs/v2/content-org_guided-courses.ts.html +110 -0
  24. package/docs/v2/content-org_learning-paths.ts.html +123 -0
  25. package/docs/v2/content-org_playlists-types.js.html +14 -2
  26. package/docs/v2/content-org_playlists.js.html +39 -17
  27. package/docs/v2/content.js.html +111 -52
  28. package/docs/v2/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  29. package/docs/v2/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  30. package/docs/v2/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  31. package/docs/v2/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  32. package/docs/v2/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  33. package/docs/v2/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  34. package/docs/v2/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  35. package/docs/v2/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  36. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  37. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  38. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  39. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  40. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  41. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  42. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  43. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  44. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  45. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  46. package/docs/v2/forums_categories.ts.html +137 -0
  47. package/docs/v2/forums_discussions.js.html +95 -0
  48. package/docs/v2/forums_forum.js.html +95 -0
  49. package/docs/v2/forums_forums.ts.html +160 -0
  50. package/docs/v2/forums_posts.ts.html +258 -0
  51. package/docs/v2/forums_threads.ts.html +271 -0
  52. package/docs/v2/gamification_awards.js.html +35 -534
  53. package/docs/v2/gamification_awards.ts.html +181 -0
  54. package/docs/v2/gamification_gamification.js.html +2 -2
  55. package/docs/v2/gamification_types.js.html +7 -25
  56. package/docs/v2/global.html +849 -690
  57. package/docs/v2/index.html +2 -3
  58. package/docs/v2/liveTesting.ts.html +102 -0
  59. package/docs/v2/module-Accounts.html +2094 -0
  60. package/docs/v2/module-Awards.html +396 -14
  61. package/docs/v2/module-Categories.html +711 -0
  62. package/docs/v2/module-Config.html +4 -8
  63. package/docs/v2/module-Content-Services-V2.html +182 -49
  64. package/docs/v2/module-ForumCategories.html +687 -0
  65. package/docs/v2/module-ForumDiscussions.html +370 -0
  66. package/docs/v2/module-Forums.html +15393 -0
  67. package/docs/v2/module-GuidedCourses.html +1304 -0
  68. package/docs/v2/module-Interests.html +2 -2
  69. package/docs/v2/module-Onboarding.html +670 -0
  70. package/docs/v2/module-Payments.html +250 -0
  71. package/docs/v2/module-Permissions.html +2 -2
  72. package/docs/v2/module-Playlists.html +212 -44
  73. package/docs/v2/module-Railcontent-Services.html +689 -2175
  74. package/docs/v2/module-Sanity-Services.html +480 -925
  75. package/docs/v2/module-Sessions.html +3 -3
  76. package/docs/v2/module-Threads.html +1119 -0
  77. package/docs/v2/module-UserActivity.html +193 -23
  78. package/docs/v2/module-UserChat.html +410 -0
  79. package/docs/v2/module-UserManagement.html +1028 -11
  80. package/docs/v2/module-UserMemberships.html +829 -0
  81. package/docs/v2/module-UserNotifications.html +1399 -27
  82. package/docs/v2/module-UserProfile.html +106 -2
  83. package/docs/v2/railcontent.js.html +87 -236
  84. package/docs/v2/sanity.js.html +342 -415
  85. package/docs/v2/scripts/collapse.js +0 -0
  86. package/docs/v2/scripts/commonNav.js +0 -0
  87. package/docs/v2/scripts/linenumber.js +0 -0
  88. package/docs/v2/scripts/nav.js +0 -0
  89. package/docs/v2/scripts/polyfill.js +0 -0
  90. package/docs/v2/scripts/prettify/Apache-License-2.0.txt +0 -0
  91. package/docs/v2/scripts/prettify/lang-css.js +0 -0
  92. package/docs/v2/scripts/prettify/prettify.js +0 -0
  93. package/docs/v2/scripts/search.js +0 -0
  94. package/docs/v2/styles/jsdoc.css +0 -0
  95. package/docs/v2/styles/prettify.css +0 -0
  96. package/docs/v2/userActivity.js.html +532 -451
  97. package/docs/v2/user_account.ts.html +237 -0
  98. package/docs/v2/user_chat.js.html +98 -0
  99. package/docs/v2/user_interests.js.html +2 -2
  100. package/docs/v2/user_management.js.html +82 -2
  101. package/docs/v2/user_memberships.js.html +144 -0
  102. package/docs/v2/user_memberships.ts.html +292 -0
  103. package/docs/v2/user_notifications.js.html +203 -21
  104. package/docs/v2/user_onboarding.ts.html +183 -0
  105. package/docs/v2/user_payments.ts.html +97 -0
  106. package/docs/v2/user_permissions.js.html +2 -2
  107. package/docs/v2/user_profile.js.html +12 -2
  108. package/docs/v2/user_sessions.js.html +33 -2
  109. package/docs/v2/user_types.js.html +16 -2
  110. package/docs/v2/user_user-management-system.js.html +2 -2
  111. package/jsdoc.json +0 -0
  112. package/link_mcs.sh +0 -0
  113. package/package.json +1 -1
  114. package/src/contentMetaData.js +0 -0
  115. package/src/contentTypeConfig.js +3 -3
  116. package/src/filterBuilder.js +0 -0
  117. package/src/index.d.ts +0 -0
  118. package/src/index.js +0 -0
  119. package/src/services/algolia.js +0 -0
  120. package/src/services/config.js +0 -0
  121. package/src/services/contentLikes.js +0 -0
  122. package/src/services/contentProgress.js +0 -0
  123. package/src/services/dataContext.js +0 -0
  124. package/src/services/imageSRCBuilder.js +1 -1
  125. package/src/services/imageSRCVerify.js +1 -1
  126. package/src/services/lastUpdated.js +0 -0
  127. package/src/services/railcontent.js +0 -0
  128. package/src/services/recommendations.js +0 -0
  129. package/src/services/userPermissions.js +0 -0
  130. package/test/config.test.js +0 -0
  131. package/test/contentLikes.test.js +0 -0
  132. package/test/contentProgress.test.js +0 -0
  133. package/test/imageSRCBuilder.test.js +1 -1
  134. package/test/imageSRCVerify.test.js +4 -4
  135. package/test/initializeTests.js +0 -0
  136. package/test/lastUpdated.test.js +0 -0
  137. package/test/sanityQueryService.test.js +0 -0
  138. package/test/userPermissions.test.js +0 -0
  139. package/tools/generate-index.cjs +0 -0
  140. package/.yarnrc.yml +0 -1
  141. package/docs/v2/Content-Organization.html +0 -245
  142. package/docs/v2/UserManagement.html +0 -269
  143. package/docs/v2/global.html#User +0 -293
  144. package/docs/v2/module-Notifications.html +0 -1183
  145. package/docs/v2/module-Session-Management.html +0 -575
  146. package/docs/v2/module-User-Activity.html +0 -4410
  147. package/docs/v2/module-User-Management.html +0 -490
  148. package/docs/v2/module-User-Permissions.html +0 -406
  149. package/docs/v2/types.js.html +0 -122
  150. 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#~deleteAccount">deleteAccount</a></li><li data-type='method'><a href="module-Accounts.html#~numberOfActiveUsers">numberOfActiveUsers</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#.getLegacyMethods">getLegacyMethods</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-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#~jumpToPost">jumpToPost</a></li><li data-type='method'><a href="module-Forums.html#~likePost">likePost</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#~search">search</a></li><li data-type='method'><a href="module-Forums.html#~unfollowThread">unfollowThread</a></li><li data-type='method'><a href="module-Forums.html#~unlikePost">unlikePost</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#~updatePost">updatePost</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><ul class='methods'><li data-type='method'><a href="module-GuidedCourses.html#~getActivePath">getActivePath</a></li><li data-type='method'><a href="module-GuidedCourses.html#~getDailySession">getDailySession</a></li><li data-type='method'><a href="module-GuidedCourses.html#~updateActivePath">updateActivePath</a></li><li data-type='method'><a href="module-GuidedCourses.html#~updateDailySession">updateDailySession</a></li></ul></li><li></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-Onboarding.html">Onboarding</a><ul class='methods'><li data-type='method'><a href="module-Onboarding.html#~startOnboarding">startOnboarding</a></li><li data-type='method'><a href="module-Onboarding.html#~updateOnboarding">updateOnboarding</a></li><li data-type='method'><a href="module-Onboarding.html#~userOnboardingForBrand">userOnboardingForBrand</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#.fetchMethodV2IntroVideo">fetchMethodV2IntroVideo</a></li><li data-type='method'><a href="module-Sanity-Services.html#.fetchMethodV2Structure">fetchMethodV2Structure</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#.getUserSignature">getUserSignature</a></li><li data-type='method'><a href="module-UserManagement.html#.isUsernameAvailable">isUsernameAvailable</a></li><li data-type='method'><a href="module-UserManagement.html#.setUserSignature">setUserSignature</a></li><li data-type='method'><a href="module-UserManagement.html#.toggleSignaturePrivate">toggleSignaturePrivate</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#~restorePurchases">restorePurchases</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>Interfaces</h3><ul><li><a href="TestUser.html">TestUser</a></li></ul><h3>Global</h3><ul><li><a href="global.html#createTestUser">createTestUser</a></li></ul>
33
33
 
34
34
  </nav>
35
35
 
@@ -50,10 +50,13 @@
50
50
  */
51
51
  import {
52
52
  artistOrInstructorName,
53
+ instructorField,
54
+ chapterField,
53
55
  assignmentsField,
54
56
  descriptionField,
55
57
  resourcesField,
56
58
  contentTypeConfig,
59
+ getIntroVideoFields,
57
60
  DEFAULT_FIELDS,
58
61
  getFieldsForContentType,
59
62
  filtersToGroq,
@@ -61,30 +64,30 @@ import {
61
64
  showsTypes,
62
65
  getNewReleasesTypes,
63
66
  coachLessonsTypes,
67
+ getFieldsForContentTypeWithFilteredChildren,
64
68
  getChildFieldsForContentType,
65
69
  SONG_TYPES,
66
70
  } from '../contentTypeConfig.js'
67
- import { fetchSimilarItems } from './recommendations.js'
71
+ import {fetchSimilarItems, recommendations} from './recommendations.js'
68
72
  import { processMetadata, typeWithSortOrder } from '../contentMetaData.js'
69
73
 
70
74
  import { globalConfig } from './config.js'
71
75
 
72
76
  import {
73
- fetchCompletedChallenges,
74
- fetchOwnedChallenges,
75
77
  fetchNextContentDataForParent,
76
78
  fetchHandler,
77
79
  } from './railcontent.js'
78
80
  import { arrayToStringRepresentation, FilterBuilder } from '../filterBuilder.js'
79
81
  import { fetchUserPermissions } from './user/permissions.js'
80
82
  import { getAllCompleted, getAllStarted, getAllStartedOrCompleted } from './contentProgress.js'
83
+ import {fetchRecentActivitiesActiveTabs} from "./userActivity.js";
81
84
 
82
85
  /**
83
86
  * Exported functions that are excluded from index generation.
84
87
  *
85
88
  * @type {string[]}
86
89
  */
87
- const excludeFromGeneratedIndex = ['handleCustomFetchAll', 'fetchRelatedByLicense']
90
+ const excludeFromGeneratedIndex = ['fetchRelatedByLicense']
88
91
 
89
92
  /**
90
93
  * Fetch a song by its document ID from Sanity.
@@ -120,18 +123,19 @@ export async function fetchSongById(documentId) {
120
123
  * @returns {Promise&lt;Object|null>}
121
124
  */
122
125
  export async function fetchLeaving(brand, { pageNumber = 1, contentPerPage = 20 } = {}) {
123
- const nextQuarter = getNextAndPreviousQuarterDates()['next']
124
- const filterString = `brand == '${brand}' &amp;&amp; quarter_removed == '${nextQuarter}'`
126
+ const today = new Date()
127
+ const isoDateOnly = getDateOnly(today)
128
+ const filterString = `brand == '${brand}' &amp;&amp; quarter_removed > '${isoDateOnly}'`
125
129
  const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
126
130
  const sortOrder = {
127
- sortOrder: 'published_on desc, id desc',
131
+ sortOrder: 'quarter_removed asc, published_on desc, id desc',
128
132
  start: startEndOrder['start'],
129
133
  end: startEndOrder['end'],
130
134
  }
131
135
  const query = await buildQuery(
132
136
  filterString,
133
137
  { pullFutureContent: false, availableContentStatuses: ['published'] },
134
- getFieldsForContentType(),
138
+ getFieldsForContentType('leaving'),
135
139
  sortOrder
136
140
  )
137
141
  return fetchSanity(query, true)
@@ -146,11 +150,12 @@ export async function fetchLeaving(brand, { pageNumber = 1, contentPerPage = 20
146
150
  * @returns {Promise&lt;Object|null>}
147
151
  */
148
152
  export async function fetchReturning(brand, { pageNumber = 1, contentPerPage = 20 } = {}) {
149
- const nextQuarter = getNextAndPreviousQuarterDates()['next']
150
- const filterString = `brand == '${brand}' &amp;&amp; quarter_published == '${nextQuarter}'`
153
+ const today = new Date()
154
+ const isoDateOnly = getDateOnly(today)
155
+ const filterString = `brand == '${brand}' &amp;&amp; quarter_published >= '${isoDateOnly}'`
151
156
  const startEndOrder = getQueryFromPage(pageNumber, contentPerPage)
152
157
  const sortOrder = {
153
- sortOrder: 'published_on desc, id desc',
158
+ sortOrder: 'quarter_published asc, published_on desc, id desc',
154
159
  start: startEndOrder['start'],
155
160
  end: startEndOrder['end'],
156
161
  }
@@ -203,41 +208,6 @@ function getQueryFromPage(pageNumber, contentPerPage) {
203
208
  return result
204
209
  }
205
210
 
206
- /**
207
- * returns array of next and previous quarter dates as strings
208
- *
209
- * @returns {string[]}
210
- */
211
- function getNextAndPreviousQuarterDates() {
212
- const january = 1
213
- const april = 4
214
- const july = 7
215
- const october = 10
216
- const month = new Date().getMonth()
217
- let year = new Date().getFullYear()
218
- let nextQuarter = ''
219
- let prevQuarter = ''
220
- if (month &lt; april) {
221
- nextQuarter = `${year}-0${april}-01`
222
- prevQuarter = `${year}-0${january}-01`
223
- } else if (month &lt; july) {
224
- nextQuarter = `${year}-0${july}-01`
225
- prevQuarter = `${year}-0${april}-01`
226
- } else if (month &lt; october) {
227
- nextQuarter = `${year}-${october}-01`
228
- prevQuarter = `${year}-0${july}-01`
229
- } else {
230
- prevQuarter = `${year}-${october}-01`
231
- year++
232
- nextQuarter = `${year}-0${january}-01`
233
- }
234
-
235
- let result = []
236
- result['next'] = nextQuarter
237
- result['previous'] = prevQuarter
238
- return result
239
- }
240
-
241
211
  /**
242
212
  * Fetch all artists with lessons available for a specific brand.
243
213
  *
@@ -394,8 +364,8 @@ export async function fetchNewReleases(
394
364
  const start = (page - 1) * limit
395
365
  const end = start + limit
396
366
  const sortOrder = getSortOrder(sort, brand)
397
- const nextQuarter = getNextAndPreviousQuarterDates()['next']
398
- const filter = `_type in ${typesString} &amp;&amp; brand == '${brand}' &amp;&amp; show_in_new_feed == true &amp;&amp; (!defined(quarter_published) || quarter_published != '${nextQuarter}')`
367
+ const now = getDateOnly()
368
+ const filter = `_type in ${typesString} &amp;&amp; brand == '${brand}' &amp;&amp; (status == 'published' &amp;&amp; show_in_new_feed == true &amp;&amp; published_on &lt;= '${now}')`
399
369
  const fields = `
400
370
  "id": railcontent_id,
401
371
  title,
@@ -411,12 +381,7 @@ export async function fetchNewReleases(
411
381
  web_url_path,
412
382
  "permission_id": permission[]->railcontent_id,
413
383
  `
414
- const filterParams = { allowsPullSongsContent: false }
415
- const query = await buildQuery(filter, filterParams, fields, {
416
- sortOrder: sortOrder,
417
- start,
418
- end: end,
419
- })
384
+ const query = buildRawQuery(filter, fields, {sortOrder: sortOrder, start, end: end})
420
385
  return fetchSanity(query, true)
421
386
  }
422
387
 
@@ -435,8 +400,6 @@ export async function fetchNewReleases(
435
400
  * .catch(error => console.error(error));
436
401
  */
437
402
  export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {}) {
438
- const liveTypes = getUpcomingEventsTypes(brand)
439
- const typesString = arrayToStringRepresentation(liveTypes)
440
403
  const now = getSanityDate(new Date())
441
404
  const start = (page - 1) * limit
442
405
  const end = start + limit
@@ -454,9 +417,11 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
454
417
  "type": _type,
455
418
  web_url_path,
456
419
  "permission_id": permission[]->railcontent_id,
420
+ live_event_start_time,
421
+ live_event_end_time,
457
422
  "isLive": live_event_start_time &lt;= '${now}' &amp;&amp; live_event_end_time >= '${now}'`
458
423
  const query = buildRawQuery(
459
- `_type in ${typesString} &amp;&amp; brand == '${brand}' &amp;&amp; published_on > '${now}' &amp;&amp; status == 'scheduled'`,
424
+ `defined(live_event_start_time) &amp;&amp; live_event_start_time >= '${now}' &amp;&amp; (!defined(live_event_end_time) || live_event_end_time >= '${now}' ) &amp;&amp; brand == '${brand}' &amp;&amp; status == 'scheduled'`,
460
425
  fields,
461
426
  {
462
427
  sortOrder: 'published_on asc',
@@ -490,7 +455,7 @@ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
490
455
  const now = getSanityDate(new Date())
491
456
  const start = (page - 1) * limit
492
457
  const end = start + limit
493
- const query = `*[_type in [${typesString}] &amp;&amp; brand == '${brand}' &amp;&amp; status in ['published','scheduled'] &amp;&amp; published_on > '${now}']{
458
+ const query = `*[_type in [${typesString}] &amp;&amp; brand == '${brand}' &amp;&amp; status in ['published','scheduled'] &amp;&amp; (!defined(live_event_end_time) || live_event_end_time &lt; '${now}' ) &amp;&amp; published_on > '${now}']{
494
459
  "id": railcontent_id,
495
460
  title,
496
461
  "image": thumbnail.asset->url,
@@ -521,12 +486,12 @@ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
521
486
  * .catch(error => console.error(error));
522
487
  */
523
488
  export async function fetchByRailContentId(id, contentType) {
524
- const fields = getFieldsForContentType(contentType)
525
- const childFields = getChildFieldsForContentType(contentType)
489
+ const fields = await getFieldsForContentTypeWithFilteredChildren(contentType)
490
+ const lessonFields = getChildFieldsForContentType(contentType)
526
491
  const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
527
492
  const entityFieldsString = ` ${fields}
528
493
  'child_count': coalesce(count(child[${childrenFilter}]->), 0) ,
529
- "lessons": child[${childrenFilter}]->{${childFields}},
494
+ "lessons": child[${childrenFilter}]->{${lessonFields}},
530
495
  'length_in_seconds': coalesce(
531
496
  math::sum(
532
497
  select(
@@ -550,7 +515,7 @@ export async function fetchByRailContentId(id, contentType) {
550
515
  /**
551
516
  * Fetch content by an array of Railcontent IDs.
552
517
  *
553
- * @param {Array&lt;string>} ids - The array of Railcontent IDs of the content to fetch.
518
+ * @param {Array&lt;string|number>} ids - The array of Railcontent IDs of the content to fetch.
554
519
  * @param {string} [contentType] - The content type the IDs to add needed fields to the response.
555
520
  * @returns {Promise&lt;Array&lt;Object>|null>} - A promise that resolves to an array of content objects or null if not found.
556
521
  *
@@ -559,19 +524,42 @@ export async function fetchByRailContentId(id, contentType) {
559
524
  * .then(contents => console.log(contents))
560
525
  * .catch(error => console.error(error));
561
526
  */
562
- export async function fetchByRailContentIds(ids, contentType = undefined, brand= undefined) {
563
- if (!ids) {
527
+ export async function fetchByRailContentIds(ids, contentType = undefined, brand = undefined, includePermissionsAndStatusFilter = false) {
528
+ if (!ids?.length) {
564
529
  return []
565
530
  }
531
+ ids = [...new Set(ids.filter(item => item !== null &amp;&amp; item !== undefined))];
566
532
  const idsString = ids.join(',')
567
533
  const brandFilter = brand ? ` &amp;&amp; brand == "${brand}"` : ''
534
+ const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {pullFutureContent: true}).buildFilter()
535
+ const fields = await getFieldsForContentTypeWithFilteredChildren(contentType, true)
536
+ const baseFilter = `railcontent_id in [${idsString}]${brandFilter}`
537
+ const finalFilter = includePermissionsAndStatusFilter ? await new FilterBuilder(baseFilter).buildFilter() : baseFilter
568
538
  const query = `*[
569
- railcontent_id in [${idsString}]${brandFilter}
539
+ ${finalFilter}
570
540
  ]{
571
- ${getFieldsForContentType(contentType)}
541
+ ${fields}
542
+ 'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
543
+ live_event_start_time,
544
+ live_event_end_time,
572
545
  }`
573
546
 
574
- const results = await fetchSanity(query, true)
547
+ console.log('ids query', query)
548
+ const customPostProcess = (results) => {
549
+ const now = getSanityDate(new Date(), false);
550
+ const liveProcess = (result) => {
551
+ if (result.live_event_start_time &amp;&amp; result.live_event_end_time) {
552
+ result.isLive =
553
+ result.live_event_start_time &lt;= now &amp;&amp;
554
+ result.live_event_end_time >= now;
555
+ } else {
556
+ result.isLive = false;
557
+ }
558
+ return result;
559
+ };
560
+ return results.map(liveProcess);
561
+ }
562
+ const results = await fetchSanity(query, true, { customPostProcess: customPostProcess })
575
563
 
576
564
  const sortFuction = function compare(a, b) {
577
565
  const indexA = ids.indexOf(a['id'])
@@ -582,11 +570,35 @@ export async function fetchByRailContentIds(ids, contentType = undefined, brand=
582
570
  }
583
571
 
584
572
  // Sort results to match the order of the input IDs
585
- const sortedResults = results.sort(sortFuction)
573
+ const sortedResults = results?.sort(sortFuction) ?? null
586
574
 
587
575
  return sortedResults
588
576
  }
589
577
 
578
+ export async function fetchContentRows(brand, pageName, contentRowSlug)
579
+ {
580
+ if (pageName === 'lessons') pageName = 'lesson'
581
+ if (pageName === 'songs') pageName = 'song'
582
+ const rowString = contentRowSlug ? ` &amp;&amp; slug.current == "${contentRowSlug.toLowerCase()}"` : ''
583
+ const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`, {pullFutureContent: true}).buildFilter()
584
+ const childFilter = await new FilterBuilder('', {isChildrenFilter: true}).buildFilter()
585
+ const query = `*[_type == 'recommended-content-row' &amp;&amp; brand == '${brand}' &amp;&amp; type == '${pageName}'${rowString}]{
586
+ brand,
587
+ name,
588
+ 'slug': slug.current,
589
+ 'content': content[${childFilter}]->{
590
+ 'children': child[${childFilter}]->{ 'id': railcontent_id,
591
+ 'type': _type, brand, 'thumbnail': thumbnail.asset->url,
592
+ 'children': child[${childFilter}]->{'id': railcontent_id}, },
593
+ ${getFieldsForContentType('tab-data')}
594
+ 'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
595
+ },
596
+ }`
597
+ return fetchSanity(query, true)
598
+ }
599
+
600
+
601
+
590
602
  /**
591
603
  * Fetch all content for a specific brand and type with pagination, search, and grouping options.
592
604
  * @param {string} brand - The brand for which to fetch content.
@@ -635,21 +647,6 @@ export async function fetchAll(
635
647
  progress = 'all',
636
648
  } = {}
637
649
  ) {
638
- let customResults = await handleCustomFetchAll(brand, type, {
639
- page,
640
- limit,
641
- searchTerm,
642
- sort,
643
- includedFields,
644
- groupBy,
645
- progressIds,
646
- useDefaultFields,
647
- customFields,
648
- progress,
649
- })
650
- if (customResults) {
651
- return customResults
652
- }
653
650
  let config = contentTypeConfig[type] ?? {}
654
651
  let additionalFields = config?.fields ?? []
655
652
  let isGroupByOneToOne = (groupBy ? config?.relationships?.[groupBy]?.isOneToOne : false) ?? false
@@ -672,9 +669,7 @@ export async function fetchAll(
672
669
  } else {
673
670
  typeFilter = type
674
671
  ? `&amp;&amp; _type == '${type}'`
675
- : progress === 'in progress' || progress === 'completed'
676
- ? " &amp;&amp; (_type != 'challenge-part' &amp;&amp; _type != 'challenge')"
677
- : ''
672
+ : ''
678
673
  }
679
674
 
680
675
  // Construct the search filter
@@ -717,7 +712,7 @@ export async function fetchAll(
717
712
  'head_shot_picture_url': thumbnail_url.asset->url,
718
713
  'web_url_path': '/${brand}/${webUrlPath}/'+name+'?included_fieds[]=type,${type}',
719
714
  'all_lessons_count': count(*[${lessonsFilterWithRestrictions}]._id),
720
- 'lessons': *[${lessonsFilterWithRestrictions}]{
715
+ 'children': *[${lessonsFilterWithRestrictions}]{
721
716
  ${fieldsString},
722
717
  ${groupBy}
723
718
  }[0...20]
@@ -737,7 +732,7 @@ export async function fetchAll(
737
732
  'head_shot_picture_url': thumbnail_url.asset->url,
738
733
  'web_url_path': select(defined(web_url_path)=> web_url_path +'?included_fieds[]=type,${type}',!defined(web_url_path)=> '/${brand}${webUrlPath}/'+name+'/${webUrlPathType}'),
739
734
  'all_lessons_count': count(*[${lessonsFilterWithRestrictions}]._id),
740
- 'lessons': *[${lessonsFilterWithRestrictions}]{
735
+ 'children': *[${lessonsFilterWithRestrictions}]{
741
736
  ${fieldsString},
742
737
  'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
743
738
  ${groupBy}
@@ -747,8 +742,8 @@ export async function fetchAll(
747
742
  filter = `brand == "${brand}" ${typeFilter} ${searchFilter} ${includedFieldsFilter} ${progressFilter} ${customFilter}`
748
743
  const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
749
744
  entityFieldsString = ` ${fieldsString},
750
- 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
751
- 'length_in_seconds': coalesce(
745
+ 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
746
+ 'length_in_seconds': coalesce(
752
747
  math::sum(
753
748
  select(
754
749
  child[${childrenFilter}]->length_in_seconds
@@ -772,152 +767,6 @@ export async function fetchAll(
772
767
  return fetchSanity(query, true)
773
768
  }
774
769
 
775
- /**
776
- * Fetch all content that requires custom handling or a distinct external call
777
- * @param {string} brand - The brand for which to fetch content.
778
- * @param {string} type - The content type to fetch (e.g., 'song', 'artist').
779
- * @param {Object} params - Parameters for pagination, filtering, sorting, and grouping.
780
- * @param {number} [params.page=1] - The page number for pagination.
781
- * @param {number} [params.limit=10] - The number of items per page.
782
- * @param {string} [params.searchTerm=""] - The search term to filter content by title or artist.
783
- * @param {string} [params.sort="-published_on"] - The field to sort the content by.
784
- * @param {Array&lt;string>} [params.includedFields=[]] - The fields to include in the query.
785
- * @param {string} [params.groupBy=""] - The field to group the results by (e.g., 'artist', 'genre').
786
- * @param {Array&lt;string>} [params.progressIds=undefined] - An array of railcontent IDs to filter the results by. Used for filtering by progress.
787
- * @param {boolean} [params.useDefaultFields=true] - use the default sanity fields for content Type
788
- * @param {Array&lt;string>} [params.customFields=[]] - An array of sanity fields to include in the request
789
- * @param {string} [params.progress="all"] - An string representing which progress filter to use ("all", "in progress", "complete", "not started").
790
- * @returns {Promise&lt;Object|null>} - The fetched content data or null if not found.
791
- */
792
- async function handleCustomFetchAll(
793
- brand,
794
- type,
795
- {
796
- page = 1,
797
- limit = 10,
798
- searchTerm = '',
799
- sort = '-published_on',
800
- includedFields = [],
801
- groupBy = '',
802
- progressIds = undefined,
803
- useDefaultFields = true,
804
- customFields = [],
805
- progress = 'all',
806
- } = {}
807
- ) {
808
- if (type === 'challenge') {
809
- if (groupBy === 'completed') {
810
- const completedIds = await fetchCompletedChallenges(brand, page, limit)
811
- return fetchAll(brand, type, {
812
- page,
813
- limit,
814
- searchTerm,
815
- sort,
816
- includedFields,
817
- groupBy: '',
818
- progressIds: completedIds,
819
- useDefaultFields,
820
- customFields,
821
- progress,
822
- })
823
- } else if (groupBy === 'owned') {
824
- const ownedIds = await fetchOwnedChallenges(brand, page, limit)
825
- return fetchAll(brand, type, {
826
- page,
827
- limit,
828
- searchTerm,
829
- sort,
830
- includedFields,
831
- groupBy: '',
832
- progressIds: ownedIds,
833
- useDefaultFields,
834
- customFields,
835
- progress,
836
- })
837
- } else if (groupBy === 'difficulty_string') {
838
- return fetchChallengesByDifficulty(
839
- brand,
840
- type,
841
- page,
842
- limit,
843
- searchTerm,
844
- sort,
845
- includedFields,
846
- groupBy,
847
- progressIds,
848
- useDefaultFields,
849
- customFields,
850
- progress
851
- )
852
- }
853
- }
854
- return null
855
- }
856
-
857
- async function fetchChallengesByDifficulty(
858
- brand,
859
- type,
860
- page,
861
- limit,
862
- searchTerm,
863
- sort,
864
- includedFields,
865
- groupBy,
866
- progressIds,
867
- useDefaultFields,
868
- customFields,
869
- progress
870
- ) {
871
- let config = contentTypeConfig['challenge'] ?? {}
872
- let additionalFields = config?.fields ?? []
873
-
874
- // Construct the search filter
875
- const searchFilter = searchTerm
876
- ? groupBy !== ''
877
- ? `&amp;&amp; (^.name match "${searchTerm}*" || title match "${searchTerm}*")`
878
- : `&amp;&amp; (artist->name match "${searchTerm}*" || instructor[]->name match "${searchTerm}*" || title match "${searchTerm}*" || name match "${searchTerm}*")`
879
- : ''
880
-
881
- // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
882
- const includedFieldsFilter = includedFields.length > 0 ? filtersToGroq(includedFields) : ''
883
-
884
- // limits the results to supplied progressIds for started &amp; completed filters
885
- const progressFilter = await getProgressFilter(progress, progressIds)
886
-
887
- let fields = useDefaultFields
888
- ? customFields.concat(DEFAULT_FIELDS, additionalFields)
889
- : customFields
890
- let fieldsString = fields.join(',')
891
-
892
- const lessonsFilter = `_type == 'challenge' &amp;&amp; brand == '${brand}' &amp;&amp; ^.name == difficulty_string ${searchFilter} ${includedFieldsFilter} ${progressFilter}`
893
- const lessonsFilterWithRestrictions = await new FilterBuilder(lessonsFilter).buildFilter()
894
-
895
- const query = `{
896
- "entity": [
897
- {"name": "All"},
898
- {"name": "Novice"},
899
- {"name": "Beginner"},
900
- {"name": "Intermediate"},
901
- {"name": "Advanced"},
902
- {"name": "Expert"}]
903
- {
904
- 'id': 0,
905
- name,
906
- 'all_lessons_count': count(*[${lessonsFilterWithRestrictions}]._id),
907
- 'lessons': *[${lessonsFilterWithRestrictions}]{
908
- ${fieldsString},
909
- name
910
- }[0...20]
911
- },
912
- "total": 0
913
- }`
914
- let data = await fetchSanity(query, true)
915
- data.entity = data.entity.filter(function (difficulty) {
916
- return difficulty.lessons.length > 0
917
- })
918
- return data
919
- }
920
-
921
770
  async function getProgressFilter(progress, progressIds) {
922
771
  switch (progress) {
923
772
  case 'all':
@@ -935,11 +784,11 @@ async function getProgressFilter(progress, progressIds) {
935
784
  return `&amp;&amp; !(railcontent_id in [${ids.join(',')}])`
936
785
  }
937
786
  case 'recent': {
938
- const ids = await getAllStartedOrCompleted()
787
+ const ids = progressIds !== undefined ? progressIds : await getAllStartedOrCompleted()
939
788
  return `&amp;&amp; (railcontent_id in [${ids.join(',')}])`
940
789
  }
941
790
  case 'incomplete': {
942
- const ids = await getAllStarted()
791
+ const ids = progressIds !== undefined ? progressIds :await getAllStarted()
943
792
  return `&amp;&amp; railcontent_id in [${ids.join(',')}]`
944
793
  }
945
794
  default:
@@ -948,17 +797,17 @@ async function getProgressFilter(progress, progressIds) {
948
797
  }
949
798
 
950
799
  export function getSortOrder(sort = '-published_on', brand, groupBy) {
951
- // Determine the sort order
800
+ const sanitizedSort = sort?.trim() || '-published_on'
801
+ let isDesc = sanitizedSort.startsWith('-')
802
+ const sortField = isDesc ? sanitizedSort.substring(1) : sanitizedSort
803
+
952
804
  let sortOrder = ''
953
- const isDesc = sort.startsWith('-')
954
- sort = isDesc ? sort.substring(1) : sort
955
- switch (sort) {
805
+
806
+ switch (sortField) {
956
807
  case 'slug':
957
- sortOrder = groupBy ? 'name' : 'title'
958
- break
959
- case 'name':
960
- sortOrder = sort
808
+ sortOrder = groupBy ? 'name' : '!defined(title), lower(title)'
961
809
  break
810
+
962
811
  case 'popularity':
963
812
  if (groupBy == 'artist' || groupBy == 'genre') {
964
813
  sortOrder = isDesc ? `coalesce(popularity.${brand}, -1)` : 'popularity'
@@ -966,11 +815,17 @@ export function getSortOrder(sort = '-published_on', brand, groupBy) {
966
815
  sortOrder = isDesc ? 'coalesce(popularity, -1)' : 'popularity'
967
816
  }
968
817
  break
969
- case 'published_on':
970
- default:
818
+
819
+ case 'recommended':
971
820
  sortOrder = 'published_on'
821
+ isDesc = true
822
+ break
823
+
824
+ default:
825
+ sortOrder = sortField
972
826
  break
973
827
  }
828
+
974
829
  sortOrder += isDesc ? ' desc' : ' asc'
975
830
  return sortOrder
976
831
  }
@@ -1290,6 +1145,7 @@ export async function jumpToContinueContent(railcontentId) {
1290
1145
  /**
1291
1146
  * Fetch the page data for a specific lesson by Railcontent ID.
1292
1147
  * @param {string} railContentId - The Railcontent ID of the current lesson.
1148
+ * @parent {boolean} addParent - Whether to include parent content data in the response.
1293
1149
  * @returns {Promise&lt;Object|null>} - The fetched page data or null if found.
1294
1150
  *
1295
1151
  * @example
@@ -1297,80 +1153,61 @@ export async function jumpToContinueContent(railcontentId) {
1297
1153
  * .then(data => console.log(data))
1298
1154
  * .catch(error => console.error(error));
1299
1155
  */
1300
- export async function fetchLessonContent(railContentId) {
1156
+ export async function fetchLessonContent(railContentId, { addParent = false } = {}) {
1301
1157
  const filterParams = { isSingle: true, pullFutureContent: true }
1302
- // Format changes made to the `fields` object may also need to be reflected in Musora-web-platform SanityGateway.php $fields object
1303
- // Currently only for challenges and challenge lessons
1304
- // If you're unsure, message Adrian, or just add them.
1305
- const fields = `title,
1306
- published_on,
1307
- "type":_type,
1308
- "resources": ${resourcesField},
1309
- difficulty,
1310
- difficulty_string,
1311
- brand,
1312
- status,
1313
- soundslice,
1314
- instrumentless,
1315
- railcontent_id,
1316
- "id":railcontent_id,
1317
- slug, artist->,
1318
- "thumbnail":thumbnail.asset->url,
1319
- "url": web_url_path,
1320
- soundslice_slug,
1321
- "description": description[0].children[0].text,
1322
- "chapters": chapter[]{
1323
- chapter_description,
1324
- chapter_timecode,
1325
- "chapter_thumbnail_url": chapter_thumbnail_url.asset->url
1326
- },
1327
- 'artist': { 'name': artist->name, 'thumbnail': artist->thumbnail_url.asset->url},
1328
- "instructors":instructor[]->name,
1329
- "instructor": instructor[]->{
1330
- "id":railcontent_id,
1331
- name,
1332
- short_bio,
1333
- "biography": short_bio[0].children[0].text,
1334
- web_url_path,
1335
- "coach_card_image": coach_card_image.asset->url,
1336
- "coach_profile_image":thumbnail_url.asset->url
1337
- },
1338
- ${assignmentsField}
1339
- video,
1340
- length_in_seconds,
1341
- mp3_no_drums_no_click_url,
1342
- mp3_no_drums_yes_click_url,
1343
- mp3_yes_drums_no_click_url,
1344
- mp3_yes_drums_yes_click_url,
1345
- "permission_id": permission[]->railcontent_id,
1346
- "parent_content_data": parent_content_data[]{
1347
- "id": id,
1348
- "title": *[railcontent_id == ^.id][0].title,
1349
- "web_url_path": *[railcontent_id == ^.id][0].web_url_path,
1350
- "slug":*[railcontent_id == ^.id][0].slug,
1351
- "type": *[railcontent_id == ^.id][0]._type,
1352
- },
1353
- sort,
1354
- xp,
1355
- stbs,ds2stbs, bdsStbs,
1356
- ...select(
1357
- defined(live_event_start_time) &amp;&amp; defined(live_event_end_time) => {
1358
- "live_event_start_time": live_event_start_time,
1359
- "live_event_end_time": live_event_end_time,
1360
- "live_event_youtube_id": live_event_youtube_id,
1361
- "videoId": coalesce(live_event_youtube_id, video.external_id),
1362
- }
1363
- )`
1158
+
1159
+ const parentQuery = addParent
1160
+ ? `"parent_content_data": *[railcontent_id in [...(^.parent_content_data[].id)]]{
1161
+ "id": railcontent_id,
1162
+ title,
1163
+ slug,
1164
+ "type": _type,
1165
+ "logo" : logo_image_url.asset->url,
1166
+ "dark_mode_logo": dark_mode_logo_url.asset->url,
1167
+ "light_mode_logo": light_mode_logo_url.asset->url,
1168
+ "badge": *[references(^._id) &amp;&amp; _type == 'content-award'][0].badge.asset->url,
1169
+ },`
1170
+ : ''
1171
+
1172
+ const fields = `${getFieldsForContentType()}
1173
+ "resources": ${resourcesField},
1174
+ soundslice,
1175
+ instrumentless,
1176
+ soundslice_slug,
1177
+ "description": ${descriptionField},
1178
+ "chapters": ${chapterField},
1179
+ "instructors":instructor[]->name,
1180
+ "instructor": ${instructorField},
1181
+ ${assignmentsField}
1182
+ video,
1183
+ length_in_seconds,
1184
+ mp3_no_drums_no_click_url,
1185
+ mp3_no_drums_yes_click_url,
1186
+ mp3_yes_drums_no_click_url,
1187
+ mp3_yes_drums_yes_click_url,
1188
+ "permission_id": permission[]->railcontent_id,
1189
+ ${parentQuery}
1190
+ ...select(
1191
+ defined(live_event_start_time) => {
1192
+ "live_event_start_time": live_event_start_time,
1193
+ "live_event_end_time": live_event_end_time,
1194
+ "live_event_stream_id": live_event_stream_id,
1195
+ "videoId": coalesce(live_event_stream_id, video.external_id),
1196
+ "live_event_is_global": live_global_event == true
1197
+ }
1198
+ )
1199
+ `
1200
+
1364
1201
  const query = await buildQuery(`railcontent_id == ${railContentId}`, filterParams, fields, {
1365
1202
  isSingle: true,
1366
1203
  })
1367
1204
  const chapterProcess = (result) => {
1368
1205
  const now = getSanityDate(new Date(), false)
1369
- if(result.live_event_start_time &amp;&amp; result.live_event_start_time){
1206
+ if (result.live_event_start_time &amp;&amp; result.live_event_end_time) {
1370
1207
  result.isLive = result.live_event_start_time &lt;= now &amp;&amp; result.live_event_end_time >= now
1371
1208
  }
1372
1209
  const chapters = result.chapters ?? []
1373
- if (chapters.length == 0) return result
1210
+ if (chapters.length === 0) return result
1374
1211
  result.chapters = chapters.map((chapter, index) => ({
1375
1212
  ...chapter,
1376
1213
  chapter_thumbnail_url: `https://musora-web-platform.s3.amazonaws.com/chapters/${result.brand}/Chapter${index + 1}.jpg`,
@@ -1382,15 +1219,23 @@ export async function fetchLessonContent(railContentId) {
1382
1219
  }
1383
1220
 
1384
1221
  /**
1222
+ * Returns a list of recommended content based on the provided railContentId.
1223
+ * If no recommendations found in recsys, falls back to fetching related lessons.
1385
1224
  *
1386
1225
  * @param railContentId
1387
1226
  * @param brand
1388
1227
  * @param count
1389
- * @returns {Promise&lt;Array&lt;Object>|null>}
1228
+ * @returns {Promise&lt;Array&lt;Object>>}
1390
1229
  */
1391
1230
  export async function fetchRelatedRecommendedContent(railContentId, brand, count = 10) {
1392
1231
  const recommendedItems = await fetchSimilarItems(railContentId, brand, count)
1393
- return fetchByRailContentIds(recommendedItems)
1232
+ if (recommendedItems &amp;&amp; recommendedItems.length > 0) {
1233
+ return fetchByRailContentIds(recommendedItems, 'tab-data', brand, true)
1234
+ }
1235
+
1236
+ return await fetchRelatedLessons(railContentId, brand).then((result) =>
1237
+ result.related_lessons?.splice(0, count)
1238
+ )
1394
1239
  }
1395
1240
 
1396
1241
  /**
@@ -1430,7 +1275,8 @@ export async function fetchLessonsFeaturingThisContent(railcontentId, brand, cou
1430
1275
  */
1431
1276
  async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, count) {
1432
1277
  const typeCheck = `@->_type in [${arrayJoinWithQuotes(SONG_TYPES)}]`
1433
- const typeCheckString = onlyUseSongTypes ? `${typeCheck}` : `!(${typeCheck})`
1278
+ let typeCheckString = `@->brand == '${brand}' &amp;&amp; `
1279
+ typeCheckString += onlyUseSongTypes ? `${typeCheck}` : `!(${typeCheck})`
1434
1280
  const contentFromLicenseFilter = `_type == 'license' &amp;&amp; references(^._id)].content[${typeCheckString} &amp;&amp; @->railcontent_id != ${railcontentId}`
1435
1281
  let filterSongTypesWithSameLicense = await new FilterBuilder(contentFromLicenseFilter, {
1436
1282
  isChildrenFilter: true,
@@ -1447,45 +1293,85 @@ async function fetchRelatedByLicense(railcontentId, brand, onlyUseSongTypes, cou
1447
1293
  "related_by_license" :
1448
1294
  *[${filterSongTypesWithSameLicense}]->{${queryFields}}|order(published_on desc, title asc)[0...${count}],
1449
1295
  }[0...1]`
1450
-
1451
1296
  const results = await fetchSanity(query, false)
1452
- return results['related_by_license'] ?? []
1297
+ return results ? results['related_by_license'] ?? [] : []
1453
1298
  }
1454
1299
 
1455
1300
  /**
1456
- * Fetch related lessons for a specific lesson by RailContent ID and type.
1301
+ * Fetch sibling lessons to a specific lesson
1457
1302
  * @param {string} railContentId - The RailContent ID of the current lesson.
1458
1303
  * @param {string} brand - The current brand.
1459
1304
  * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched related lessons data or null if not found.
1460
1305
  */
1461
- export async function fetchRelatedLessons(railContentId, brand) {
1462
- const filterSameTypeAndSortOrder = await new FilterBuilder(
1463
- `_type==^._type &amp;&amp; _type in ${JSON.stringify(typeWithSortOrder)} &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id !=${railContentId}`
1464
- ).buildFilter()
1465
- const filterSameType = await new FilterBuilder(
1466
- `_type==^._type &amp;&amp; !(_type in ${JSON.stringify(typeWithSortOrder)}) &amp;&amp; !(defined(parent_type)) &amp;&amp; brand == "${brand}" &amp;&amp; railcontent_id !=${railContentId}`
1467
- ).buildFilter()
1468
- const filterSongSameArtist = await new FilterBuilder(
1469
- `_type=="song" &amp;&amp; _type==^._type &amp;&amp; brand == "${brand}" &amp;&amp; references(^.artist->_id) &amp;&amp; railcontent_id !=${railContentId}`
1306
+ export async function fetchSiblingContent(railContentId, brand= null)
1307
+ {
1308
+ const filterGetParent = await new FilterBuilder(`references(^._id) &amp;&amp; _type == ^.parent_type`, {
1309
+ pullFutureContent: true
1310
+ }).buildFilter()
1311
+ const filterForParentList = await new FilterBuilder(`references(^._id) &amp;&amp; _type == ^.parent_type`, {
1312
+ pullFutureContent: true,
1313
+ isParentFilter: true,
1314
+ }).buildFilter()
1315
+
1316
+ const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1317
+
1318
+ const brandString = brand ? ` &amp;&amp; brand == "${brand}"` : ''
1319
+ const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, artist->, "permission_id": permission[]->railcontent_id, "genre": genre[]->name, "parent_id": parent_content_data[0].id`
1320
+
1321
+ const query = `*[railcontent_id == ${railContentId}${brandString}]{
1322
+ _type, parent_type, 'parent_id': parent_content_data[0].id, railcontent_id,
1323
+ 'for-calculations': *[${filterGetParent}][0]{
1324
+ 'siblings-list': child[]->railcontent_id,
1325
+ 'parents-list': *[${filterForParentList}][0].child[]->railcontent_id
1326
+ },
1327
+ "related_lessons" : *[${filterGetParent}][0].child[${childrenFilter}]->{${queryFields}}
1328
+ }`
1329
+
1330
+ let result = await fetchSanity(query, false)
1331
+
1332
+ //there's no way in sanity to retrieve the index of an array, so we must calculate after fetch
1333
+ if (result['for-calculations'] &amp;&amp; result['for-calculations']['parents-list']) {
1334
+ const calc = result['for-calculations']
1335
+ const parentCount = calc['parents-list'].length
1336
+ const currentParentIndex = calc['parents-list'].indexOf(result['parent_id']) + 1
1337
+ const siblingCount = calc['siblings-list'].length
1338
+ const currentSiblingIndex = calc['siblings-list'].indexOf(result['railcontent_id']) + 1
1339
+
1340
+ delete result['for-calculations']
1341
+ result = { ...result, parentCount, currentParentIndex, siblingCount, currentSiblingIndex }
1342
+ return result
1343
+ } else {
1344
+ delete result['for-calculations']
1345
+ return result
1346
+ }
1347
+ }
1348
+
1349
+ /**
1350
+ * Fetch lessons related to a specific lesson by RailContent ID and type.
1351
+ * @param {string} railContentId - The RailContent ID of the current lesson.
1352
+ * @returns {Promise&lt;Array&lt;Object>|null>} - The fetched related lessons data or null if not found.
1353
+ */
1354
+ export async function fetchRelatedLessons(railContentId)
1355
+ {
1356
+ const defaultFilterFields = `_type==^._type &amp;&amp; brand == ^.brand &amp;&amp; railcontent_id != ${railContentId}`
1357
+
1358
+ const filterSameArtist = await new FilterBuilder(
1359
+ `${defaultFilterFields} &amp;&amp; references(^.artist->_id)`
1470
1360
  ).buildFilter()
1471
- const filterSongSameGenre = await new FilterBuilder(
1472
- `_type=="song" &amp;&amp; _type==^._type &amp;&amp; brand == "${brand}" &amp;&amp; references(^.genre[]->_id) &amp;&amp; railcontent_id !=${railContentId}`
1361
+ const filterSameGenre = await new FilterBuilder(
1362
+ `${defaultFilterFields} &amp;&amp; references(^.genre[]->_id)`
1473
1363
  ).buildFilter()
1474
- const filterNeighbouringSiblings = await new FilterBuilder(`references(^._id)`).buildFilter()
1475
- const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
1476
- const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, web_url_path, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, "genre": genre[]->name`
1477
- const queryFieldsWithSort = queryFields + ', sort'
1478
- const query = `*[railcontent_id == ${railContentId} &amp;&amp; brand == "${brand}" &amp;&amp; (!defined(permission) || references(*[_type=='permission']._id))]{
1364
+
1365
+ const queryFields = `_id, "id":railcontent_id, published_on, "instructor": instructor[0]->name, title, "thumbnail":thumbnail.asset->url, length_in_seconds, status, "type": _type, difficulty, difficulty_string, railcontent_id, artist->,"permission_id": permission[]->railcontent_id,_type, "genre": genre[]->name`
1366
+
1367
+ const query = `*[railcontent_id == ${railContentId} &amp;&amp; (!defined(permission) || references(*[_type=='permission']._id))]{
1479
1368
  _type, parent_type, railcontent_id,
1480
1369
  "related_lessons" : array::unique([
1481
- ...(*[${filterNeighbouringSiblings}][0].child[${childrenFilter}]->{${queryFields}}),
1482
- ...(*[${filterSongSameArtist}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
1483
- ...(*[${filterSongSameGenre}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
1484
- ...(*[${filterSameTypeAndSortOrder}]{${queryFieldsWithSort}}|order(sort asc, title asc)[0...10]),
1485
- ...(*[${filterSameType}]{${queryFields}}|order(published_on desc, title asc)[0...10])
1486
- ,
1370
+ ...(*[${filterSameArtist}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
1371
+ ...(*[${filterSameGenre}]{${queryFields}}|order(published_on desc, title asc)[0...10]),
1487
1372
  ])[0...10]}`
1488
- return fetchSanity(query, false)
1373
+
1374
+ return await fetchSanity(query, false)
1489
1375
  }
1490
1376
 
1491
1377
  /**
@@ -1507,12 +1393,10 @@ export async function fetchAllPacks(
1507
1393
  const sortOrder = getSortOrder(sort, brand)
1508
1394
  const filter = `(_type == 'pack' || _type == 'semester-pack') &amp;&amp; brand == '${brand}' &amp;&amp; title match "${searchTerm}*"`
1509
1395
  const filterParams = {}
1510
- const fields = getFieldsForContentType('pack')
1511
1396
  const start = (page - 1) * limit
1512
1397
  const end = start + limit
1513
1398
 
1514
- const query = await buildQuery(filter, filterParams, getFieldsForContentType('pack'), {
1515
- logo_image_url: 'logo_image_url.asset->url',
1399
+ const query = await buildQuery(filter, filterParams, await getFieldsForContentTypeWithFilteredChildren('pack'), {
1516
1400
  sortOrder: sortOrder,
1517
1401
  start,
1518
1402
  end,
@@ -1530,6 +1414,7 @@ export async function fetchPackAll(railcontentId, type = 'pack') {
1530
1414
  }
1531
1415
 
1532
1416
  export async function fetchLiveEvent(brand, forcedContentId = null) {
1417
+ const LIVE_EXTRA_MINUTES = 30
1533
1418
  //calendarIDs taken from addevent.php
1534
1419
  // TODO import instructor calendars to Sanity
1535
1420
  let defaultCalendarID = ''
@@ -1551,8 +1436,11 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1551
1436
  }
1552
1437
  let startDateTemp = new Date()
1553
1438
  let endDateTemp = new Date()
1554
- startDateTemp = new Date(startDateTemp.setMinutes(startDateTemp.getMinutes() + 15))
1555
- endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - 15))
1439
+
1440
+ startDateTemp = new Date(
1441
+ startDateTemp.setMinutes(startDateTemp.getMinutes() + LIVE_EXTRA_MINUTES)
1442
+ )
1443
+ endDateTemp = new Date(endDateTemp.setMinutes(endDateTemp.getMinutes() - LIVE_EXTRA_MINUTES))
1556
1444
 
1557
1445
  // See LiveStreamEventService.getCurrentOrNextLiveEvent for some nice complicated logic which I don't think is actually importart
1558
1446
  // this has some +- on times
@@ -1564,7 +1452,7 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1564
1452
  'id': railcontent_id,
1565
1453
  live_event_start_time,
1566
1454
  live_event_end_time,
1567
- live_event_youtube_id,
1455
+ live_event_stream_id,
1568
1456
  railcontent_id,
1569
1457
  published_on,
1570
1458
  'event_coach_url' : instructor[0]->web_url_path,
@@ -1573,18 +1461,15 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1573
1461
  "thumbnail": thumbnail.asset->url,
1574
1462
  ${artistOrInstructorName()},
1575
1463
  difficulty_string,
1576
- "instructors": instructor[]->{
1577
- name,
1578
- web_url_path,
1579
- },
1580
- 'videoId': coalesce(live_event_youtube_id, video.external_id),
1464
+ "instructors": ${instructorField},
1465
+ 'videoId': coalesce(live_event_stream_id, video.external_id),
1581
1466
  } | order(live_event_start_time)[0...1]`
1582
1467
  : `*[status == 'scheduled' &amp;&amp; brand == '${brand}' &amp;&amp; defined(live_event_start_time) &amp;&amp; live_event_start_time &lt;= '${getSanityDate(startDateTemp, false)}' &amp;&amp; live_event_end_time >= '${getSanityDate(endDateTemp, false)}']{
1583
1468
  'slug': slug.current,
1584
1469
  'id': railcontent_id,
1585
1470
  live_event_start_time,
1586
1471
  live_event_end_time,
1587
- live_event_youtube_id,
1472
+ live_event_stream_id,
1588
1473
  railcontent_id,
1589
1474
  published_on,
1590
1475
  'event_coach_url' : instructor[0]->web_url_path,
@@ -1597,7 +1482,7 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1597
1482
  name,
1598
1483
  web_url_path,
1599
1484
  },
1600
- 'videoId': coalesce(live_event_youtube_id, video.external_id),
1485
+ 'videoId': coalesce(live_event_stream_id, video.external_id),
1601
1486
  } | order(live_event_start_time)[0...1]`
1602
1487
 
1603
1488
  return await fetchSanity(query, false, { processNeedAccess: false })
@@ -1610,12 +1495,13 @@ export async function fetchLiveEvent(brand, forcedContentId = null) {
1610
1495
  *
1611
1496
  * @example
1612
1497
  * fetchPackData(404048)
1613
- * .then(challenge => console.log(challenge))
1498
+ * .then(pack => console.log(pack))
1614
1499
  * .catch(error => console.error(error));
1615
1500
  */
1616
1501
  export async function fetchPackData(id) {
1617
- const query = `*[railcontent_id == ${id}]{
1618
- ${getFieldsForContentType('pack')}
1502
+ const builder = await new FilterBuilder(`railcontent_id == ${id}`).buildFilter()
1503
+ const query = `*[${builder}]{
1504
+ ${await getFieldsForContentTypeWithFilteredChildren('pack')}
1619
1505
  } [0...1]`
1620
1506
  return fetchSanity(query, false)
1621
1507
  }
@@ -1659,28 +1545,6 @@ export async function fetchCoachLessons(
1659
1545
  return fetchSanity(query, true)
1660
1546
  }
1661
1547
 
1662
- /**
1663
- * Fetch the data needed for the Course Overview screen.
1664
- * @param {string} id - The Railcontent ID of the course
1665
- * @returns {Promise&lt;Object|null>} - The course information and lessons or null if not found.
1666
- *
1667
- * @example
1668
- * fetchParentForDownload('course123')
1669
- * .then(course => console.log(course))
1670
- * .catch(error => console.error(error));
1671
- */
1672
- export async function fetchParentForDownload(id) {
1673
- const query = buildRawQuery(
1674
- `railcontent_id == ${id}`,
1675
- getFieldsForContentType('parent-download'),
1676
- {
1677
- isSingle: true,
1678
- }
1679
- )
1680
-
1681
- return fetchSanity(query, false)
1682
- }
1683
-
1684
1548
  /**
1685
1549
  * Fetch the data needed for the coach screen.
1686
1550
  * @param {string} id - The Railcontent ID of the coach
@@ -1969,14 +1833,10 @@ export async function fetchSanity(
1969
1833
  return null
1970
1834
  }
1971
1835
 
1972
- if (globalConfig.sanityConfig.debug) {
1973
- console.log('fetchSanity Query:', query)
1974
- }
1975
1836
  const perspective = globalConfig.sanityConfig.perspective ?? 'published'
1976
1837
  const api = globalConfig.sanityConfig.useCachedAPI ? 'apicdn' : 'api'
1977
1838
  const url = `https://${globalConfig.sanityConfig.projectId}.${api}.sanity.io/v${globalConfig.sanityConfig.version}/data/query/${globalConfig.sanityConfig.dataset}?perspective=${perspective}`
1978
1839
  const headers = {
1979
- Authorization: `Bearer ${globalConfig.sanityConfig.token}`,
1980
1840
  'Content-Type': 'application/json',
1981
1841
  }
1982
1842
 
@@ -2001,10 +1861,10 @@ export async function fetchSanity(
2001
1861
  }
2002
1862
  const result = await response.json()
2003
1863
  if (result.result) {
2004
- if (globalConfig.sanityConfig.debug) {
2005
- console.log('fetchSanity Results:', result)
2006
- }
2007
1864
  let results = isList ? result.result : result.result[0]
1865
+ if (!results) {
1866
+ throw new Error('No results found')
1867
+ }
2008
1868
  results = processNeedAccess
2009
1869
  ? await needsAccessDecorator(results, userPermissions, isAdmin)
2010
1870
  : results
@@ -2013,7 +1873,7 @@ export async function fetchSanity(
2013
1873
  throw new Error('No results found')
2014
1874
  }
2015
1875
  } catch (error) {
2016
- console.error('fetchSanity: Fetch error:', error)
1876
+ console.error('fetchSanity: Fetch error:', {error, query})
2017
1877
  return null
2018
1878
  }
2019
1879
  }
@@ -2104,7 +1964,12 @@ export async function fetchShowsData(brand) {
2104
1964
  * .catch(error => console.error(error));
2105
1965
  */
2106
1966
  export async function fetchMetadata(brand, type) {
2107
- const processedData = processMetadata(brand, type, true)
1967
+ let processedData = processMetadata(brand, type, true)
1968
+ if(processedData?.onlyAvailableTabs === true) {
1969
+ const activeTabs = await fetchRecentActivitiesActiveTabs()
1970
+ processedData.tabs = activeTabs
1971
+ }
1972
+
2108
1973
  return processedData ? processedData : {}
2109
1974
  }
2110
1975
 
@@ -2146,6 +2011,10 @@ function getSanityDate(date, roundToHourForCaching = true) {
2146
2011
  return date.toISOString()
2147
2012
  }
2148
2013
 
2014
+ function getDateOnly(date = new Date()) {
2015
+ return date.toISOString().split('T')[0]
2016
+ }
2017
+
2149
2018
  const merge = (a, b, predicate = (a, b) => a === b) => {
2150
2019
  const c = [...a] // copy to avoid side effects
2151
2020
  // add all items from B to copy C if they're not already present
@@ -2199,12 +2068,12 @@ async function buildQuery(
2199
2068
  function buildEntityAndTotalQuery(
2200
2069
  filter = '',
2201
2070
  fields = '...',
2202
- { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false }
2071
+ { sortOrder = 'published_on desc', start = 0, end = 10, isSingle = false, withoutPagination = false }
2203
2072
  ) {
2204
- const sortString = sortOrder ? `order(${sortOrder})` : ''
2205
- const countString = isSingle ? '[0...1]' : `[${start}...${end}]`
2073
+ const sortString = sortOrder ? ` | order(${sortOrder})` : ''
2074
+ const countString = isSingle ? '[0...1]' : (withoutPagination ? ``: `[${start}...${end}]`)
2206
2075
  const query = `{
2207
- "entity": *[${filter}] | ${sortString}${countString}
2076
+ "entity": *[${filter}] ${sortString}${countString}
2208
2077
  {
2209
2078
  ${fields}
2210
2079
  },
@@ -2323,19 +2192,31 @@ export async function fetchTabData(
2323
2192
  ) {
2324
2193
  const start = (page - 1) * limit
2325
2194
  const end = start + limit
2326
-
2327
2195
  // Construct the included fields filter, replacing 'difficulty' with 'difficulty_string'
2328
2196
  const includedFieldsFilter =
2329
2197
  includedFields.length > 0 ? filtersToGroq(includedFields, [], pageName) : ''
2330
2198
 
2331
- // limits the results to supplied progressIds for started &amp; completed filters
2332
- const progressFilter = await getProgressFilter(progress, progressIds)
2199
+ let sortOrder = getSortOrder(sort, brand, '')
2333
2200
 
2334
- // Determine the sort order
2335
- const sortOrder = getSortOrder(sort, brand, '')
2201
+ switch (progress) {
2202
+ case 'recent':
2203
+ progressIds = await getAllStartedOrCompleted({ brand, onlyIds: true });
2204
+ sortOrder = null;
2205
+ break;
2206
+ case 'incomplete':
2207
+ progressIds = await getAllStarted();
2208
+ sortOrder = null;
2209
+ break;
2210
+ case 'completed':
2211
+ progressIds = await getAllCompleted();
2212
+ sortOrder = null;
2213
+ break;
2214
+ }
2336
2215
 
2337
- let fields = DEFAULT_FIELDS
2338
- let fieldsString = fields.join(',')
2216
+ // limits the results to supplied progressIds for started &amp; completed filters
2217
+ const progressFilter = await getProgressFilter(progress, progressIds)
2218
+ const fieldsString = getFieldsForContentType('tab-data');
2219
+ const now = getSanityDate(new Date())
2339
2220
 
2340
2221
  // Determine the group by clause
2341
2222
  let query = ''
@@ -2344,9 +2225,14 @@ export async function fetchTabData(
2344
2225
 
2345
2226
  filter = `brand == "${brand}" ${includedFieldsFilter} ${progressFilter}`
2346
2227
  const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
2347
- entityFieldsString = ` ${fieldsString},
2348
- 'lesson_count': coalesce(count(child[${childrenFilter}]->), 0) ,
2349
- 'length_in_seconds': coalesce(
2228
+ const childrenFields = await getChildFieldsForContentType('tab-data')
2229
+ const lessonCountFilter = await new FilterBuilder(`_id in ^.child[]._ref`).buildFilter()
2230
+ entityFieldsString =
2231
+ ` ${fieldsString}
2232
+ 'children': child[${childrenFilter}]->{ ${childrenFields} 'children': child[${childrenFilter}]->{ ${childrenFields} }, },
2233
+ 'isLive': live_event_start_time &lt;= "${now}" &amp;&amp; live_event_end_time >= "${now}",
2234
+ 'lesson_count': coalesce(count(*[${lessonCountFilter}]), 0),
2235
+ 'length_in_seconds': coalesce(
2350
2236
  math::sum(
2351
2237
  select(
2352
2238
  child[${childrenFilter}]->length_in_seconds
@@ -2354,15 +2240,27 @@ export async function fetchTabData(
2354
2240
  ),
2355
2241
  length_in_seconds
2356
2242
  ),`
2357
-
2358
2243
  const filterWithRestrictions = await new FilterBuilder(filter, {}).buildFilter()
2359
2244
  query = buildEntityAndTotalQuery(filterWithRestrictions, entityFieldsString, {
2360
2245
  sortOrder: sortOrder,
2361
2246
  start: start,
2362
- end: end,
2247
+ end: end
2363
2248
  })
2364
2249
 
2365
- return fetchSanity(query, true)
2250
+ let results = await fetchSanity(query, true);
2251
+
2252
+ if (['recent', 'incomplete', 'completed'].includes(progress) &amp;&amp; results.entity.length > 1) {
2253
+ const orderMap = new Map(progressIds.map((id, index) => [id, index]))
2254
+ results.entity = results.entity
2255
+ .sort((a, b) => {
2256
+ const aIdx = orderMap.get(a.id) ?? Number.MAX_SAFE_INTEGER;
2257
+ const bIdx = orderMap.get(b.id) ?? Number.MAX_SAFE_INTEGER;
2258
+ return aIdx - bIdx || new Date(b.published_on) - new Date(a.published_on);
2259
+ })
2260
+ .slice(start, end);
2261
+ }
2262
+
2263
+ return results;
2366
2264
  }
2367
2265
 
2368
2266
  export async function fetchRecent(
@@ -2397,12 +2295,13 @@ export async function fetchScheduledAndNewReleases(
2397
2295
  const sortOrder = getSortOrder(sort, brand)
2398
2296
 
2399
2297
  const query = `
2400
- *[_type in [${typesString}] &amp;&amp; brand == '${brand}' &amp;&amp; ((status in ['published','scheduled'] &amp;&amp; published_on > '${now}')||(show_in_new_feed == true)) ]
2298
+ *[_type in [${typesString}] &amp;&amp; brand == '${brand}' &amp;&amp; ((status in ['published','scheduled'] )||(show_in_new_feed == true)) ]
2401
2299
  [${start}...${end}]
2402
2300
  | order(published_on asc) {
2403
2301
  "id": railcontent_id,
2404
2302
  title,
2405
2303
  "image": thumbnail.asset->url,
2304
+ "thumbnail": thumbnail.asset->url,
2406
2305
  ${artistOrInstructorName()},
2407
2306
  "artists": instructor[]->name,
2408
2307
  difficulty,
@@ -2418,20 +2317,48 @@ export async function fetchScheduledAndNewReleases(
2418
2317
  return fetchSanity(query, true)
2419
2318
  }
2420
2319
 
2421
- export async function fetchShows(
2422
- brand,
2423
- type,
2424
- sort = 'sort'
2425
- ) {
2320
+ export async function fetchShows(brand, type, sort = 'sort') {
2426
2321
  const sortOrder = getSortOrder(sort, brand)
2427
2322
  const filter = `_type == '${type}' &amp;&amp; brand == '${brand}'`
2428
2323
  const filterParams = {}
2429
2324
 
2430
2325
  const query = await buildQuery(filter, filterParams, getFieldsForContentType(type), {
2431
- sortOrder: sortOrder
2326
+ sortOrder: sortOrder,
2327
+ end: 100, // Adrian: added for homepage progress rows, this should be handled gracefully
2432
2328
  })
2433
2329
  return fetchSanity(query, true)
2434
2330
  }
2331
+
2332
+ /**
2333
+ * Fetch the method intro video for a given brand.
2334
+ * @param brand
2335
+ * @returns {Promise&lt;*|null>}
2336
+ */
2337
+ export async function fetchMethodV2IntroVideo(brand) {
2338
+ const type = "method-intro";
2339
+ const filter = `_type == '${type}' &amp;&amp; brand == '${brand}'`;
2340
+ const fields = getIntroVideoFields();
2341
+
2342
+ const query = `*[${filter}] { ${fields.join(", ")} }`;
2343
+ return fetchSanity(query, false);
2344
+ }
2345
+
2346
+ /**
2347
+ * Fetch the structure (just ids) of the Method for a given brand.
2348
+ * @param brand
2349
+ * @returns {Promise&lt;*|null>}
2350
+ */
2351
+ export async function fetchMethodV2Structure(brand) {
2352
+ const _type = "method-v2";
2353
+ const query = `*[_type == '${_type}' &amp;&amp; brand == '${brand}'][0...1]{
2354
+ 'sanity_id': _id,
2355
+ 'learningPaths': child[]->{
2356
+ 'id': railcontent_id,
2357
+ 'children': child[]->railcontent_id
2358
+ }
2359
+ }`;
2360
+ return await fetchSanity(query, false);
2361
+ }
2435
2362
  </code></pre>
2436
2363
  </article>
2437
2364
  </section>
@@ -2446,7 +2373,7 @@ export async function fetchShows(
2446
2373
  <br class="clear">
2447
2374
 
2448
2375
  <footer>
2449
- Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Mon Jun 16 2025 14:26:22 GMT+0300 (Eastern European Summer Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2376
+ Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.3</a> on Tue Nov 04 2025 18:21:55 GMT+0000 (Coordinated Universal Time) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
2450
2377
  </footer>
2451
2378
 
2452
2379
  <script>prettyPrint();</script>