musora-content-services 1.6.4 → 1.6.6

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 (124) hide show
  1. package/.github/workflows/conventional-commits.yaml +16 -0
  2. package/.github/workflows/docs.js.yml +58 -0
  3. package/.github/workflows/node.js.yml +0 -0
  4. package/.github/workflows/sync-docs.yml +56 -0
  5. package/.prettierignore +0 -0
  6. package/.prettierrc +0 -0
  7. package/CHANGELOG.md +4 -0
  8. package/README.md +3 -3
  9. package/babel.config.cjs +0 -0
  10. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  11. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  12. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  13. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  14. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  15. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  16. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  17. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  18. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  19. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  20. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  21. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  23. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  24. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  25. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  26. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  27. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  28. package/docs/scripts/collapse.js +0 -0
  29. package/docs/scripts/commonNav.js +0 -0
  30. package/docs/scripts/linenumber.js +0 -0
  31. package/docs/scripts/nav.js +0 -0
  32. package/docs/scripts/polyfill.js +0 -0
  33. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  34. package/docs/scripts/prettify/lang-css.js +0 -0
  35. package/docs/scripts/prettify/prettify.js +0 -0
  36. package/docs/scripts/search.js +0 -0
  37. package/docs/styles/jsdoc.css +0 -0
  38. package/docs/styles/prettify.css +0 -0
  39. package/docs/v2/Content-Organization.html +245 -0
  40. package/docs/v2/ContentOrganization.html +245 -0
  41. package/docs/v2/Gamification.html +245 -0
  42. package/docs/v2/UserManagement.html +269 -0
  43. package/docs/v2/UserManagementSystem.html +317 -0
  44. package/docs/v2/api_types.js.html +97 -0
  45. package/docs/v2/config.js.html +143 -0
  46. package/docs/v2/content-org_content-org.js.html +76 -0
  47. package/docs/v2/content-org_playlists-types.js.html +116 -0
  48. package/docs/v2/content-org_playlists.js.html +418 -0
  49. package/docs/v2/content.js.html +466 -0
  50. package/docs/v2/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  51. package/docs/v2/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  52. package/docs/v2/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  53. package/docs/v2/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  54. package/docs/v2/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  55. package/docs/v2/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  56. package/docs/v2/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  57. package/docs/v2/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  58. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  59. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +978 -0
  60. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  61. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  62. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  63. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  64. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +1049 -0
  65. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  66. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  67. package/docs/v2/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  68. package/docs/v2/gamification_awards.js.html +664 -0
  69. package/docs/v2/gamification_gamification.js.html +76 -0
  70. package/docs/v2/gamification_types.js.html +98 -0
  71. package/docs/v2/global.html +5812 -0
  72. package/docs/v2/global.html#User +293 -0
  73. package/docs/v2/index.html +168 -0
  74. package/docs/v2/module-Awards.html +354 -0
  75. package/docs/v2/module-Config.html +434 -0
  76. package/docs/v2/module-Content-Services-V2.html +2434 -0
  77. package/docs/v2/module-Interests.html +1066 -0
  78. package/docs/v2/module-Notifications.html +1183 -0
  79. package/docs/v2/module-Permissions.html +406 -0
  80. package/docs/v2/module-Playlists.html +2862 -0
  81. package/docs/v2/module-Railcontent-Services.html +7954 -0
  82. package/docs/v2/module-Sanity-Services.html +9608 -0
  83. package/docs/v2/module-Session-Management.html +575 -0
  84. package/docs/v2/module-Sessions.html +575 -0
  85. package/docs/v2/module-User-Activity.html +4410 -0
  86. package/docs/v2/module-User-Management.html +490 -0
  87. package/docs/v2/module-User-Permissions.html +406 -0
  88. package/docs/v2/module-UserActivity.html +4410 -0
  89. package/docs/v2/module-UserManagement.html +915 -0
  90. package/docs/v2/module-UserNotifications.html +1223 -0
  91. package/docs/v2/module-UserProfile.html +266 -0
  92. package/docs/v2/railcontent.js.html +984 -0
  93. package/docs/v2/sanity.js.html +2459 -0
  94. package/docs/v2/scripts/collapse.js +39 -0
  95. package/docs/v2/scripts/commonNav.js +28 -0
  96. package/docs/v2/scripts/linenumber.js +25 -0
  97. package/docs/v2/scripts/nav.js +12 -0
  98. package/docs/v2/scripts/polyfill.js +4 -0
  99. package/docs/v2/scripts/prettify/Apache-License-2.0.txt +202 -0
  100. package/docs/v2/scripts/prettify/lang-css.js +2 -0
  101. package/docs/v2/scripts/prettify/prettify.js +28 -0
  102. package/docs/v2/scripts/search.js +99 -0
  103. package/docs/v2/styles/jsdoc.css +776 -0
  104. package/docs/v2/styles/prettify.css +80 -0
  105. package/docs/v2/types.js.html +122 -0
  106. package/docs/v2/userActivity.js.html +1451 -0
  107. package/docs/v2/user_interests.js.html +150 -0
  108. package/docs/v2/user_management.js.html +178 -0
  109. package/docs/v2/user_notifications.js.html +192 -0
  110. package/docs/v2/user_permissions.js.html +110 -0
  111. package/docs/v2/user_profile.js.html +105 -0
  112. package/docs/v2/user_sessions.js.html +139 -0
  113. package/docs/v2/user_types.js.html +208 -0
  114. package/docs/v2/user_user-management-system.js.html +79 -0
  115. package/docs/v2/user_user-management.js.html +78 -0
  116. package/jest.config.js +0 -0
  117. package/package.json +1 -1
  118. package/src/contentTypeConfig.js +27 -12
  119. package/src/services/imageSRCBuilder.js +2 -0
  120. package/src/services/sanity.js +120 -81
  121. package/test/live/contentProgressLive.test.js +0 -0
  122. package/test/live/railcontentLive.test.js +0 -0
  123. package/test/localStorageMock.js +0 -0
  124. package/test/log.js +0 -0
@@ -0,0 +1,984 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+
5
+ <meta charset="utf-8">
6
+ <title>railcontent.js - Documentation</title>
7
+
8
+
9
+ <script src="scripts/prettify/prettify.js"></script>
10
+ <script src="scripts/prettify/lang-css.js"></script>
11
+ <!--[if lt IE 9]>
12
+ <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
13
+ <![endif]-->
14
+ <link type="text/css" rel="stylesheet" href="styles/prettify.css">
15
+ <link type="text/css" rel="stylesheet" href="styles/jsdoc.css">
16
+ <script src="scripts/nav.js" defer></script>
17
+
18
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
19
+ </head>
20
+ <body>
21
+
22
+ <input type="checkbox" id="nav-trigger" class="nav-trigger" />
23
+ <label for="nav-trigger" class="navicon-button x">
24
+ <div class="navicon"></div>
25
+ </label>
26
+
27
+ <label for="nav-trigger" class="overlay"></label>
28
+
29
+ <nav >
30
+
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>
33
+
34
+ </nav>
35
+
36
+ <div id="main">
37
+
38
+ <h1 class="page-title">railcontent.js</h1>
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+ <section>
47
+ <article>
48
+ <pre class="prettyprint source linenums"><code>/**
49
+ * @module Railcontent-Services
50
+ */
51
+ import { globalConfig } from './config.js'
52
+ import { fetchJSONHandler } from '../lib/httpHelper.js'
53
+ import { convertToTimeZone } from './dateUtils.js';
54
+
55
+ /**
56
+ * Exported functions that are excluded from index generation.
57
+ *
58
+ * @type {string[]}
59
+ */
60
+ const excludeFromGeneratedIndex = [
61
+ 'fetchUserLikes',
62
+ 'postContentLiked',
63
+ 'postContentUnliked',
64
+ 'postRecordWatchSession',
65
+ 'postContentStarted',
66
+ 'postContentComplete',
67
+ 'postContentReset',
68
+ 'fetchUserPermissionsData',
69
+ ]
70
+
71
+ let challengeIndexMetaDataPromise = null
72
+
73
+ /**
74
+ * Fetches the completion status of a specific lesson for the current user.
75
+ *
76
+ * @param {string} content_id - The ID of the lesson content to check.
77
+ * @returns {Promise&lt;Object|null>} - Returns the completion status object if found, otherwise null.
78
+ * @example
79
+ * fetchCurrentSongComplete('user123', 'lesson456', 'csrf-token')
80
+ * .then(status => console.log(status))
81
+ * .catch(error => console.error(error));
82
+ */
83
+ export async function fetchCompletedState(content_id) {
84
+ const url = `/content/user_progress/${globalConfig.sessionConfig.userId}?content_ids[]=${content_id}`
85
+
86
+ const headers = {
87
+ 'Content-Type': 'application/json',
88
+ Accept: 'application/json',
89
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
90
+ }
91
+
92
+ try {
93
+ const response = await fetchAbsolute(url, { headers })
94
+ const result = await response.json()
95
+
96
+ if (result &amp;&amp; result[content_id]) {
97
+ return result[content_id] // Return the correct object
98
+ } else {
99
+ return null // Handle unexpected structure
100
+ }
101
+ } catch (error) {
102
+ console.error('Fetch error:', error)
103
+ return null
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Fetches the completion status for multiple songs for the current user.
109
+ *
110
+ * @param {Array&lt;string>} contentIds - An array of content IDs to check.
111
+ * @returns {Promise&lt;Object|null>} - Returns an object containing completion statuses keyed by content ID, or null if an error occurs.
112
+ * @example
113
+ * fetchAllCompletedStates('user123', ['song456', 'song789'], 'csrf-token')
114
+ * .then(statuses => console.log(statuses))
115
+ * .catch(error => console.error(error));
116
+ */
117
+ export async function fetchAllCompletedStates(contentIds) {
118
+ const url = `/content/user_progress/${globalConfig.sessionConfig.userId}?${contentIds.map((id) => `content_ids[]=${id}`).join('&amp;')}`
119
+
120
+ const headers = {
121
+ 'Content-Type': 'application/json',
122
+ Accept: 'application/json',
123
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
124
+ }
125
+
126
+ try {
127
+ const response = await fetchAbsolute(url, { headers })
128
+ const result = await response.json()
129
+ if (result) {
130
+ return result
131
+ } else {
132
+ console.log('result not json')
133
+ }
134
+ } catch (error) {
135
+ console.error('Fetch error:', error)
136
+ return null
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Fetches a list of songs that are currently in progress for the current user.
142
+ *
143
+ * @param {string} brand - The brand associated with the songs.
144
+ * @returns {Promise&lt;Object|null>} - Returns an object containing in-progress songs if found, otherwise null.
145
+ * @example
146
+ * fetchSongsInProgress('drumeo')
147
+ * .then(songs => console.log(songs))
148
+ * .catch(error => console.error(error));
149
+ */
150
+ export async function fetchSongsInProgress(brand) {
151
+ const url = `/content/in_progress/${globalConfig.sessionConfig.userId}?content_type=song&amp;brand=${brand}`
152
+
153
+ const headers = {
154
+ 'Content-Type': 'application/json',
155
+ Accept: 'application/json',
156
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
157
+ }
158
+
159
+ try {
160
+ const response = await fetchAbsolute(url, { headers })
161
+ const result = await response.json()
162
+ if (result) {
163
+ //console.log('fetchSongsInProgress', result);
164
+ return result
165
+ } else {
166
+ console.log('result not json')
167
+ }
168
+ } catch (error) {
169
+ console.error('Fetch error:', error)
170
+ return null
171
+ }
172
+ }
173
+
174
+ /**
175
+ * Fetches a list of content that is currently in progress for the current user.
176
+ *
177
+ * @param {string} type - The content type associated with the content.
178
+ * @param {string} brand - The brand associated with the content.
179
+ * @param {number} [params.limit=20] - The limit of results per page.
180
+ * @param {number} [params.page=1] - The page number for pagination.
181
+ * @returns {Promise&lt;Object|null>} - Returns an object containing in-progress content if found, otherwise null.
182
+ * @example
183
+ * fetchContentInProgress('song', 'drumeo')
184
+ * .then(songs => console.log(songs))
185
+ * .catch(error => console.error(error));
186
+ */
187
+ export async function fetchContentInProgress(type = 'all', brand, { page, limit } = {}) {
188
+ let url
189
+ const limitString = limit ? `&amp;limit=${limit}` : ''
190
+ const pageString = page ? `&amp;page=${page}` : ''
191
+
192
+ if (type === 'all') {
193
+ url = `/content/in_progress/${globalConfig.sessionConfig.userId}?brand=${brand}${limitString}${pageString}`
194
+ } else {
195
+ url = `/content/in_progress/${globalConfig.sessionConfig.userId}?content_type=${type}&amp;brand=${brand}${limitString}${pageString}`
196
+ }
197
+ const headers = {
198
+ 'Content-Type': 'application/json',
199
+ Accept: 'application/json',
200
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
201
+ }
202
+ try {
203
+ const response = await fetchAbsolute(url, { headers })
204
+ const result = await response.json()
205
+ if (result) {
206
+ //console.log('contentInProgress', result);
207
+ return result
208
+ } else {
209
+ console.log('result not json')
210
+ }
211
+ } catch (error) {
212
+ console.error('Fetch error:', error)
213
+ return null
214
+ }
215
+ }
216
+
217
+ /**
218
+ * Fetches a list of content that has been completed for the current user.
219
+ *
220
+ * @param {string} type - The content type associated with the content.
221
+ * @param {string} brand - The brand associated with the content.
222
+ * @param {number} [params.limit=20] - The limit of results per page.
223
+ * @param {number} [params.page=1] - The page number for pagination.
224
+ * @returns {Promise&lt;Object|null>} - Returns an object containing in-progress content if found, otherwise null.
225
+ * @example
226
+ * fetchCompletedContent('song', 'drumeo')
227
+ * .then(songs => console.log(songs))
228
+ * .catch(error => console.error(error));
229
+ */
230
+ export async function fetchCompletedContent(type = 'all', brand, { page, limit } = {}) {
231
+ let url
232
+ const limitString = limit ? `&amp;limit=${limit}` : ''
233
+ const pageString = page ? `&amp;page=${page}` : ''
234
+
235
+ if (type === 'all') {
236
+ url = `/content/completed/${globalConfig.sessionConfig.userId}?brand=${brand}${limitString}${pageString}`
237
+ } else {
238
+ url = `/content/completed/${globalConfig.sessionConfig.userId}?content_type=${type}&amp;brand=${brand}${limitString}${pageString}`
239
+ }
240
+ const headers = {
241
+ 'Content-Type': 'application/json',
242
+ Accept: 'application/json',
243
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
244
+ }
245
+ try {
246
+ const response = await fetchAbsolute(url, { headers })
247
+ const result = await response.json()
248
+ if (result) {
249
+ //console.log('completed content', result);
250
+ return result
251
+ } else {
252
+ console.log('result not json')
253
+ }
254
+ } catch (error) {
255
+ console.error('Fetch error:', error)
256
+ return null
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Fetches user context data for a specific piece of content.
262
+ *
263
+ * @param {int} contentId - The content id.
264
+ * @returns {Promise&lt;Object|null>} - Returns an object containing user context data if found, otherwise null.
265
+ * @example
266
+ * fetchContentPageUserData(406548)
267
+ * .then(data => console.log(data))
268
+ * .catch(error => console.error(error));
269
+ */
270
+ export async function fetchContentPageUserData(contentId) {
271
+ let url = `/api/content/v1/${contentId}/user_data/${globalConfig.sessionConfig.userId}`
272
+ const headers = {
273
+ 'Content-Type': 'application/json',
274
+ Accept: 'application/json',
275
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
276
+ }
277
+
278
+ try {
279
+ const response = await fetchAbsolute(url, { headers })
280
+ const result = await response.json()
281
+ if (result) {
282
+ console.log('fetchContentPageUserData', result)
283
+ return result
284
+ } else {
285
+ console.log('result not json')
286
+ }
287
+ } catch (error) {
288
+ console.error('Fetch error:', error)
289
+ return null
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Fetches the ID and Type of the piece of content that would be the next one for the user
295
+ *
296
+ * @param {int} contentId - The id of the parent (method, level, or course) piece of content.
297
+ * @returns {Promise&lt;Object|null>} - Returns and Object with the id and type of the next piece of content if found, otherwise null.
298
+ */
299
+ export async function fetchNextContentDataForParent(contentId) {
300
+ let url = `/content/${contentId}/next/${globalConfig.sessionConfig.userId}`
301
+ const headers = {
302
+ 'Content-Type': 'application/json',
303
+ 'X-CSRF-TOKEN': globalConfig.sessionConfig.token,
304
+ }
305
+
306
+ try {
307
+ const response = await fetchAbsolute(url, { headers })
308
+ const result = await response.json()
309
+ if (result) {
310
+ // console.log('fetchNextContentDataForParent', result);
311
+ return result.next
312
+ } else {
313
+ console.log('fetchNextContentDataForParent result not json')
314
+ return null
315
+ }
316
+ } catch (error) {
317
+ console.error('Fetch error:', error)
318
+ return null
319
+ }
320
+ }
321
+
322
+ export async function fetchUserPermissionsData() {
323
+ let url = `/content/user/permissions`
324
+ // in the case of an unauthorized user, we return empty permissions
325
+ return (await fetchHandler(url, 'get')) ?? []
326
+ }
327
+
328
+ async function fetchDataHandler(url, dataVersion, method = 'get') {
329
+ return fetchHandler(url, method, dataVersion)
330
+ }
331
+
332
+ async function postDataHandler(url, data) {
333
+ return fetchHandler(url, 'post', null, data)
334
+ }
335
+
336
+ async function patchDataHandler(url, data) {
337
+ return fetchHandler(url, 'patch', null, data)
338
+ }
339
+
340
+ async function putDataHandler(url, data) {
341
+ return fetchHandler(url, 'put', null, data)
342
+ }
343
+
344
+ async function deleteDataHandler(url, data) {
345
+ return fetchHandler(url, 'delete')
346
+ }
347
+
348
+ export async function fetchLikeCount(contendId){
349
+ const url = `/api/content/v1/content/like_count/${contendId}`
350
+ return await fetchDataHandler(url)
351
+ }
352
+
353
+ export async function fetchUserLikes(currentVersion) {
354
+ let url = `/api/content/v1/user/likes`
355
+ return fetchDataHandler(url, currentVersion)
356
+ }
357
+
358
+ export async function postContentLiked(contentId) {
359
+ let url = `/api/content/v1/user/likes/${contentId}`
360
+ return await postDataHandler(url)
361
+ }
362
+
363
+ export async function postContentUnliked(contentId) {
364
+ let url = `/api/content/v1/user/likes/${contentId}`
365
+ return await deleteDataHandler(url)
366
+ }
367
+
368
+ export async function fetchContentProgress(currentVersion) {
369
+ let url = `/content/user/progress/all`
370
+ return fetchDataHandler(url, currentVersion)
371
+ }
372
+
373
+ export async function postRecordWatchSession(
374
+ contentId,
375
+ mediaTypeId,
376
+ mediaLengthSeconds,
377
+ currentSeconds,
378
+ secondsPlayed,
379
+ sessionId
380
+ ) {
381
+ let url = `/railtracker/v2/media-playback-session`
382
+ return postDataHandler(url, {
383
+ content_id: contentId,
384
+ media_type_id: mediaTypeId,
385
+ media_length_seconds: mediaLengthSeconds,
386
+ current_second: currentSeconds,
387
+ seconds_played: secondsPlayed,
388
+ session_id: sessionId,
389
+ })
390
+ }
391
+
392
+ /**
393
+ * Fetch enrolled user data for a given challenge. Intended to be used in the enrolled modal
394
+ *
395
+ * @param contentId - railcontent id of the challenge
396
+ * @returns {Promise&lt;any|null>}
397
+ */
398
+ export async function fetchChallengeMetadata(contentId) {
399
+ let url = `/challenges/${contentId}`
400
+ return await fetchHandler(url, 'get')
401
+ }
402
+
403
+ /**
404
+ * Fetch lesson, user, and challenge data for a given lesson
405
+ *
406
+ * @param contentId - railcontent id of the lesson
407
+ * @returns {Promise&lt;any|null>}
408
+ */
409
+ export async function fetchChallengeLessonData(contentId) {
410
+ let url = `/challenges/lessons/${contentId}`
411
+ return await fetchHandler(url, 'get')
412
+ }
413
+
414
+ /**
415
+ * Fetch all owned brand challenges for user
416
+ * @param {string|null} brand - brand
417
+ * @param {int} page - page of data to pull
418
+ * @param {int} limit - number of elements to pull
419
+ * @returns {Promise&lt;any|null>}
420
+ */
421
+ export async function fetchOwnedChallenges(brand = null, page, limit) {
422
+ let brandParam = brand ? `&amp;brand=${brand}` : ''
423
+ let pageAndLimit = `?page=${page}&amp;limit=${limit}`
424
+ let url = `/challenges/tab_owned/get${pageAndLimit}${brandParam}`
425
+ return await fetchHandler(url, 'get')
426
+ }
427
+
428
+ /**
429
+ * Fetch all completed brand challenges for user
430
+ * @param {string|null} brand - brand
431
+ * @param {int} page - page of data to pull
432
+ * @param {int} limit - number of elements to pull
433
+ * @returns {Promise&lt;any|null>}
434
+ */
435
+ export async function fetchCompletedChallenges(brand = null, page, limit) {
436
+ let brandParam = brand ? `&amp;brand=${brand}` : ''
437
+ let pageAndLimit = `?page=${page}&amp;limit=${limit}`
438
+ let url = `/challenges/tab_completed/get${pageAndLimit}${brandParam}`
439
+ return await fetchHandler(url, 'get')
440
+ }
441
+
442
+ /**
443
+ * Fetch challenge, lesson, and user metadata for a given challenge
444
+ *
445
+ * @param contentId - railcontent id of the challenge
446
+ * @returns {Promise&lt;any|null>}
447
+ */
448
+ export async function fetchUserChallengeProgress(contentId) {
449
+ let url = `/challenges/user_data/${contentId}`
450
+ return await fetchHandler(url, 'get')
451
+ }
452
+
453
+ /**
454
+ * Fetch the user's best award for this challenge
455
+ *
456
+ * @param contentId - railcontent id of the challenge
457
+ * @returns {Promise&lt;any|null>} - streamed PDF
458
+ */
459
+ export async function fetchUserAward(contentId) {
460
+ let url = `/challenges/download_award/${contentId}`
461
+ return await fetchHandler(url, 'get')
462
+ }
463
+
464
+ /**
465
+ * Get challenge duration, user progress, and status for the list of challenges
466
+ * Intended to be used on the index page for challenges
467
+ *
468
+ * @param {array} contentIds - arary of railcontent ids of the challenges
469
+ * @returns {Promise&lt;any|null>}
470
+ */
471
+ export async function fetchChallengeIndexMetadata(contentIds) {
472
+ if (!challengeIndexMetaDataPromise) {
473
+ challengeIndexMetaDataPromise = getChallengeIndexMetadataPromise()
474
+ }
475
+ let results = await challengeIndexMetaDataPromise
476
+ if (Array.isArray(contentIds)) {
477
+ results = results.filter(function (challenge) {
478
+ return contentIds.includes(challenge.content_id)
479
+ })
480
+ }
481
+ return results
482
+ }
483
+
484
+ async function getChallengeIndexMetadataPromise() {
485
+ let url = `/challenges/user_progress_for_index_page/get`
486
+ const result = await fetchHandler(url, 'get')
487
+ challengeIndexMetaDataPromise = null
488
+ return result
489
+ }
490
+
491
+ /**
492
+ * Get active brand challenges for the authorized user
493
+ *
494
+ * @returns {Promise&lt;any|null>}
495
+ */
496
+ export async function fetchChallengeUserActiveChallenges(brand = null) {
497
+ let brandParam = brand ? `?brand=${brand}` : ''
498
+ let url = `/challenges/user_active_challenges/get${brandParam}`
499
+ return await fetchHandler(url, 'get')
500
+ }
501
+
502
+ /**
503
+ * Fetch All Carousel Card Data
504
+ *
505
+ * @returns {Promise&lt;any|null>}
506
+ */
507
+ export async function fetchCarouselCardData(brand = null) {
508
+ const brandParam = brand ? `?brand=${brand}` : ''
509
+ let url = `/api/v2/content/carousel${brandParam}`
510
+ return await fetchHandler(url, 'get')
511
+ }
512
+
513
+ /**
514
+ * Fetch all completed badges for the user ordered by completion date descending
515
+ *
516
+ * @param {string|null} brand -
517
+ * @returns {Promise&lt;any|null>}
518
+ */
519
+ export async function fetchUserBadges(brand = null) {
520
+ let brandParam = brand ? `?brand=${brand}` : ''
521
+ let url = `/challenges/user_badges/get${brandParam}`
522
+ return await fetchHandler(url, 'get')
523
+ }
524
+
525
+ /**
526
+ * Enroll a user in a challenge and set the start date of the challenge to the provided day.
527
+ * Clears any existing progress data for this challenge
528
+ *
529
+ * @param {int|string} contentId - railcontent id of the challenge
530
+ * @param {string} startDate - prefered format YYYYMMDD, but any Carbon parsable string will do.
531
+ * @returns {Promise&lt;any|null>}
532
+ */
533
+ export async function postChallengesSetStartDate(contentId, startDate) {
534
+ let url = `/challenges/set_start_date/${contentId}?start_date=${startDate}`
535
+ return await fetchHandler(url, 'post')
536
+ }
537
+
538
+ /**
539
+ * Enroll the user in the provided challenge and set to unlocked
540
+ * Clears any current progress data for this challenge
541
+ *
542
+ * @param {int|string} contentId - railcontent id of the challenge
543
+ * @returns {Promise&lt;any|null>}
544
+ */
545
+ export async function postChallengesUnlock(contentId) {
546
+ let url = `/challenges/unlock/${contentId}`
547
+ return await fetchHandler(url, 'post')
548
+ }
549
+
550
+ /**
551
+ * Enroll the user in the given challenge on the challenge published_on date
552
+ * Clears any current progress data for this challenge
553
+ *
554
+ * @param {int|string} contentId - railcontent id of the challenge
555
+ * @returns {Promise&lt;any|null>}
556
+ */
557
+ export async function postChallengesEnroll(contentId) {
558
+ let url = `/challenges/enroll/${contentId}`
559
+ return await fetchHandler(url, 'post')
560
+ }
561
+
562
+ /**
563
+ * Remove the user from the provided challenge
564
+ * Clears any current progress data for this challenge
565
+ *
566
+ * @param {int|string} contentId - railcontent id of the challenge
567
+ * @returns {Promise&lt;any|null>}
568
+ */
569
+ export async function postChallengesLeave(contentId) {
570
+ let url = `/challenges/leave/${contentId}`
571
+ return await fetchHandler(url, 'post')
572
+ }
573
+
574
+ /**
575
+ * Enable enrollment notifications for the provided challenge
576
+ *
577
+ * @param {int|string} contentId - railcontent id of the challenge
578
+ * @returns {Promise&lt;any|null>}
579
+ */
580
+ export async function postChallengesEnrollmentNotification(contentId) {
581
+ let url = `/challenges/notifications/enrollment_open/${contentId}`
582
+ return await fetchHandler(url, 'post')
583
+ }
584
+
585
+ /**
586
+ * Enable community notifications for the provided challenge
587
+ *
588
+ * @param {int|string} contentId - railcontent id of the challenge
589
+ * @returns {Promise&lt;any|null>}
590
+ */
591
+ export async function postChallengesCommunityNotification(contentId) {
592
+ let url = `/challenges/notifications/community_reminders/${contentId}`
593
+ return await fetchHandler(url, 'post')
594
+ }
595
+
596
+ /**
597
+ * Enable solo notifications for the provided challenge
598
+ *
599
+ * @param {int|string} contentId - railcontent id of the challenge
600
+ * @returns {Promise&lt;any|null>}
601
+ */
602
+ export async function postChallengesSoloNotification(contentId) {
603
+ let url = `/challenges/notifications/solo_reminders/${contentId}`
604
+ return await fetchHandler(url, 'post')
605
+ }
606
+
607
+ /**
608
+ * Hide challenge completed award bannare
609
+ *
610
+ * @param {int|string} contentId - railcontent id of the challenge
611
+ * @returns {Promise&lt;any|null>}
612
+ */
613
+ export async function postChallengesHideCompletedBanner(contentId) {
614
+ let url = `/challenges/hide_completed_banner/${contentId}`
615
+ return await fetchHandler(url, 'post')
616
+ }
617
+
618
+ export async function postContentComplete(contentId) {
619
+ let url = `/api/content/v1/user/progress/complete/${contentId}`
620
+ return postDataHandler(url)
621
+ }
622
+
623
+ export async function postContentReset(contentId) {
624
+ let url = `/api/content/v1/user/progress/reset/${contentId}`
625
+ return postDataHandler(url)
626
+ }
627
+
628
+ /**
629
+ * Set a user's StudentView Flag
630
+ *
631
+ * @param {int|string} userId - id of the user (must be currently authenticated)
632
+ * @param {bool} enable - truthy value to enable student view
633
+ * @returns {Promise&lt;any|null>}
634
+ */
635
+ export async function setStudentViewForUser(userId, enable) {
636
+ let url = `/user-management-system/user/update/${userId}`
637
+ let data = { use_student_view: enable ? 1 : 0 }
638
+ return await patchDataHandler(url, data)
639
+ }
640
+
641
+ /**
642
+ * Fetch the top comment for a given content
643
+ *
644
+ * @param {int} railcontentId - The railcontent id to fetch.
645
+ * @returns {Promise&lt;Object|null>} - A promise that resolves to an comment object
646
+ */
647
+ export async function fetchTopComment(railcontentId) {
648
+ const url = `/api/content/v1/${railcontentId}/comments?filter=top`
649
+ return await fetchHandler(url)
650
+ }
651
+
652
+ /**
653
+ *
654
+ * @param {int} railcontentId
655
+ * @param {int} page
656
+ * @param {int} limit
657
+ * @returns {Promise&lt;*|null>}
658
+ */
659
+ export async function fetchComments(railcontentId, page = 1, limit = 20) {
660
+ const url = `/api/content/v1/${railcontentId}/comments?page=${page}&amp;limit=${limit}`
661
+ return await fetchHandler(url)
662
+ }
663
+
664
+ /**
665
+ *
666
+ * @param {int} commentId
667
+ * @param {int} page
668
+ * @param {int} limit
669
+ * @returns {Promise&lt;*|null>}
670
+ */
671
+ export async function fetchCommentRelies(commentId, page = 1, limit = 20) {
672
+ const url = `/api/content/v1/comments/${commentId}/replies?page=${page}&amp;limit=${limit}`
673
+ return await fetchHandler(url)
674
+ }
675
+
676
+ /**
677
+ * @param {int} commentId
678
+ * @returns {Promise&lt;*|null>}
679
+ */
680
+ export async function deleteComment(commentId) {
681
+ const url = `/api/content/v1/comments/${commentId}`
682
+ return await fetchHandler(url, 'DELETE')
683
+ }
684
+
685
+ /**
686
+ * @param {int} commentId
687
+ * @param {string} comment
688
+ * @returns {Promise&lt;*|null>}
689
+ */
690
+ export async function replyToComment(commentId, comment) {
691
+ const data = { comment: comment }
692
+ const url = `/api/content/v1/comments/${commentId}/reply`
693
+ return await postDataHandler(url, data)
694
+ }
695
+
696
+ /**
697
+ * @param {int} railcontentId
698
+ * @param {string} comment
699
+ * @returns {Promise&lt;*|null>}
700
+ */
701
+ export async function createComment(railcontentId, comment) {
702
+ const data = {
703
+ comment: comment,
704
+ content_id: railcontentId,
705
+ }
706
+ const url = `/api/content/v1/comments/store`
707
+ return await postDataHandler(url, data)
708
+ }
709
+
710
+ /**
711
+ * @param {int} commentId
712
+ * @returns {Promise&lt;*|null>}
713
+ */
714
+ export async function assignModeratorToComment(commentId) {
715
+ const url = `/api/content/v1/comments/${commentId}/assign_moderator`
716
+ return await postDataHandler(url)
717
+ }
718
+
719
+ /**
720
+ * @param {int} commentId
721
+ * @returns {Promise&lt;*|null>}
722
+ */
723
+ export async function unassignModeratorToComment(commentId) {
724
+ const url = `/api/content/v1/comments/${commentId}/unassign_moderator`
725
+ return await postDataHandler(url)
726
+ }
727
+
728
+ /**
729
+ * @param {int} commentId
730
+ * @returns {Promise&lt;*|null>}
731
+ */
732
+ export async function likeComment(commentId) {
733
+ const url = `/api/content/v1/comments/${commentId}/like`
734
+ return await postDataHandler(url)
735
+ }
736
+
737
+ /**
738
+ * @param {int} commentId
739
+ * @returns {Promise&lt;*|null>}
740
+ */
741
+ export async function unlikeComment(commentId) {
742
+ const url = `/api/content/v1/comments/${commentId}/like`
743
+ return await deleteDataHandler(url)
744
+ }
745
+
746
+ /**
747
+ * @param {int} commentId
748
+ * @returns {Promise&lt;*|null>}
749
+ */
750
+ export async function closeComment(commentId) {
751
+ const url = `/api/content/v1/comments/${commentId}`
752
+ const data = {
753
+ conversation_status: 'closed',
754
+ }
755
+ return await patchDataHandler(url, data)
756
+ }
757
+
758
+ /**
759
+ * @param {int} commentId
760
+ * @returns {Promise&lt;*|null>}
761
+ */
762
+ export async function openComment(commentId) {
763
+ const url = `/api/content/v1/comments/${commentId}`
764
+ const data = {
765
+ conversation_status: 'open',
766
+ }
767
+ return await patchDataHandler(url, data)
768
+ }
769
+
770
+ /**
771
+ * @param {int} commentId
772
+ * @param {string} comment
773
+ * @returns {Promise&lt;*|null>}
774
+ */
775
+ export async function editComment(commentId, comment) {
776
+ const url = `/api/content/v1/comments/${commentId}`
777
+ const data = {
778
+ comment: comment,
779
+ }
780
+ return await putDataHandler(url, data)
781
+ }
782
+
783
+ /**
784
+ * @param {int} commentId
785
+ * @param {string} issue
786
+ * @returns {Promise&lt;*|null>}
787
+ */
788
+ export async function reportComment(commentId, issue) {
789
+ const url = `/api/content/v1/comments/${commentId}/report`
790
+ const data = {
791
+ issue: issue,
792
+ }
793
+ return await postDataHandler(url, data)
794
+ }
795
+
796
+ export async function fetchUserPractices({ currentVersion, userId } = {}) {
797
+ const params = new URLSearchParams();
798
+ if (userId) params.append('user_id', userId);
799
+ const query = params.toString() ? `?${params.toString()}` : '';
800
+ const url = `/api/user/practices/v1/practices${query}`;
801
+ const response = await fetchDataHandler(url, currentVersion);
802
+ const { data, version } = response;
803
+ const userPractices = data;
804
+
805
+ const userTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
806
+
807
+
808
+ const formattedPractices = userPractices.reduce((acc, practice) => {
809
+ // Convert UTC date to user's local date (still a Date object)
810
+ const utcDate = new Date(practice.day);
811
+ const localDate = convertToTimeZone(utcDate, userTimeZone);
812
+
813
+ const userTimeZoneDay =
814
+ localDate.getFullYear() + '-' +
815
+ String(localDate.getMonth() + 1).padStart(2, '0') + '-' +
816
+ String(localDate.getDate()).padStart(2, '0');
817
+
818
+ if (!acc[userTimeZoneDay]) {
819
+ acc[userTimeZoneDay] = [];
820
+ }
821
+
822
+ acc[userTimeZoneDay].push({
823
+ id: practice.id,
824
+ duration_seconds: practice.duration_seconds,
825
+ });
826
+
827
+ return acc;
828
+ }, {});
829
+
830
+ return {
831
+ data: {
832
+ practices: formattedPractices,
833
+ },
834
+ version,
835
+ };
836
+ }
837
+
838
+ export async function logUserPractice(practiceDetails) {
839
+ const url = `/api/user/practices/v1/practices`
840
+ return await fetchHandler(url, 'POST', null, practiceDetails)
841
+ }
842
+ export async function fetchUserPracticeMeta(practiceIds, userId = null) {
843
+ if (practiceIds.length == 0) {
844
+ return []
845
+ }
846
+ const params = new URLSearchParams();
847
+ practiceIds.forEach(id => params.append('practice_ids[]', id));
848
+
849
+ if (userId !== null) {
850
+ params.append('user_id', userId);
851
+ }
852
+ const url = `/api/user/practices/v1/practices?${params.toString()}`
853
+ return await fetchHandler(url, 'GET', null)
854
+ }
855
+
856
+ /**
857
+ * Fetches user practice notes for a specific date.
858
+ * @param {string} date - The date for which to fetch practice notes (format: YYYY-MM-DD).
859
+ * @returns {Promise&lt;Object|null>} - A promise that resolves to an object containing the practice notes if found, otherwise null.
860
+ *
861
+ * @example
862
+ * fetchUserPracticeNotes('2025-04-10')
863
+ * .then(notes => console.log(notes))
864
+ * .catch(error => console.error(error));
865
+ */
866
+ export async function fetchUserPracticeNotes(date) {
867
+ const url = `/api/user/practices/v1/notes?date=${date}`
868
+ return await fetchHandler(url, 'GET', null)
869
+ }
870
+
871
+
872
+ /**
873
+ * Get the id and slug of last interacted child. Only valid for certain content types
874
+ *
875
+ * @async
876
+ * @function fetchLastInteractedChild
877
+ * @param {array} content_ids - Content ids of to get the last interacted child of
878
+ *
879
+ *
880
+ * @returns {Promise&lt;Object>} - keyed object per valid content ids with the child
881
+ *
882
+ * @example
883
+ * try {
884
+ * const response = await fetchLastInteractedChild([191369, 410427]);
885
+ * console.log('child id', response[191369].content_id)
886
+ * console.log('child slug', response[191369].slug)
887
+ * } catch (error) {
888
+ * console.error('Failed to get children', error);
889
+ * }
890
+ */
891
+ export async function fetchLastInteractedChild(content_ids) {
892
+ const params = new URLSearchParams();
893
+ content_ids.forEach(id => params.append('content_ids[]', id));
894
+ const url = `/api/content/v1/user/last_interacted_child?${params.toString()}`
895
+ return await fetchHandler(url, 'GET', null)
896
+ }
897
+
898
+ /**
899
+ * @typedef {Object} Activity
900
+ * @property {string} id - Unique identifier for the activity.
901
+ * @property {string} type - Type of activity (e.g., "lesson_completed").
902
+ * @property {string} timestamp - ISO 8601 string of when the activity occurred.
903
+ * @property {Object} meta - Additional metadata related to the activity.
904
+ */
905
+
906
+ /**
907
+ * @typedef {Object} PaginatedActivities
908
+ * @property {number} currentPage
909
+ * @property {number} totalPages
910
+ * @property {Activity[]} data
911
+ */
912
+
913
+ /**
914
+ * Fetches a paginated list of recent user activities.
915
+ * @param {Object} [params={}] - Optional parameters.
916
+ * @param {number} [params.page=1] - The page number for pagination.
917
+ * @param {number} [params.limit=10] - The number of results per page.
918
+ * @param {string|null} [params.tabName=null] - Optional filter for activity type/tab.
919
+ * @returns {Promise&lt;PaginatedActivities>} - A promise that resolves to a paginated object of user activities.
920
+ *
921
+ * @example
922
+ * fetchRecentUserActivities({ page: 2, limit: 5 })
923
+ * .then(activities => console.log(activities))
924
+ * .catch(error => console.error(error));
925
+ */
926
+ export async function fetchRecentUserActivities({
927
+ page = 1,
928
+ limit = 5,
929
+ tabName = null
930
+ } = {}) {
931
+ let pageAndLimit = `?page=${page}&amp;limit=${limit}`
932
+ let tabParam = tabName ? `&amp;tabName=${tabName}` : ''
933
+ const url = `/api/user-management-system/v1/activities/all${pageAndLimit}${tabParam}`
934
+ return await fetchHandler(url, 'GET', null)
935
+ }
936
+
937
+
938
+ function fetchAbsolute(url, params) {
939
+ if (globalConfig.sessionConfig.authToken) {
940
+ params.headers['Authorization'] = `Bearer ${globalConfig.sessionConfig.authToken}`
941
+ }
942
+
943
+ if (globalConfig.baseUrl) {
944
+ if (url.startsWith('/')) {
945
+ return fetch(globalConfig.baseUrl + url, params)
946
+ }
947
+ }
948
+ return fetch(url, params)
949
+ }
950
+ export async function fetchHandler(url, method = 'get', dataVersion = null, body = null) {
951
+ return fetchJSONHandler(
952
+ url,
953
+ globalConfig.sessionConfig.token,
954
+ globalConfig.baseUrl,
955
+ method,
956
+ dataVersion,
957
+ body
958
+ )
959
+ }
960
+ </code></pre>
961
+ </article>
962
+ </section>
963
+
964
+
965
+
966
+
967
+
968
+
969
+ </div>
970
+
971
+ <br class="clear">
972
+
973
+ <footer>
974
+ 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.
975
+ </footer>
976
+
977
+ <script>prettyPrint();</script>
978
+ <script src="scripts/polyfill.js"></script>
979
+ <script src="scripts/linenumber.js"></script>
980
+
981
+
982
+
983
+ </body>
984
+ </html>