musora-content-services 1.0.148 → 1.0.150

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 (54) hide show
  1. package/.github/workflows/node.js.yml +0 -0
  2. package/CHANGELOG.md +4 -0
  3. package/babel.config.js +0 -0
  4. package/docs/config.js.html +2 -2
  5. package/docs/fonts/Montserrat/Montserrat-Bold.eot +0 -0
  6. package/docs/fonts/Montserrat/Montserrat-Bold.ttf +0 -0
  7. package/docs/fonts/Montserrat/Montserrat-Bold.woff +0 -0
  8. package/docs/fonts/Montserrat/Montserrat-Bold.woff2 +0 -0
  9. package/docs/fonts/Montserrat/Montserrat-Regular.eot +0 -0
  10. package/docs/fonts/Montserrat/Montserrat-Regular.ttf +0 -0
  11. package/docs/fonts/Montserrat/Montserrat-Regular.woff +0 -0
  12. package/docs/fonts/Montserrat/Montserrat-Regular.woff2 +0 -0
  13. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.eot +0 -0
  14. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.svg +0 -0
  15. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.ttf +0 -0
  16. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff +0 -0
  17. package/docs/fonts/Source-Sans-Pro/sourcesanspro-light-webfont.woff2 +0 -0
  18. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.eot +0 -0
  19. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.svg +0 -0
  20. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.ttf +0 -0
  21. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff +0 -0
  22. package/docs/fonts/Source-Sans-Pro/sourcesanspro-regular-webfont.woff2 +0 -0
  23. package/docs/index.html +2 -2
  24. package/docs/module-Config.html +2 -2
  25. package/docs/module-Railcontent-Services.html +2593 -221
  26. package/docs/module-Sanity-Services.html +30 -30
  27. package/docs/railcontent.js.html +308 -5
  28. package/docs/sanity.js.html +7 -3
  29. package/docs/scripts/collapse.js +0 -0
  30. package/docs/scripts/commonNav.js +0 -0
  31. package/docs/scripts/linenumber.js +0 -0
  32. package/docs/scripts/nav.js +0 -0
  33. package/docs/scripts/polyfill.js +0 -0
  34. package/docs/scripts/prettify/Apache-License-2.0.txt +0 -0
  35. package/docs/scripts/prettify/lang-css.js +0 -0
  36. package/docs/scripts/prettify/prettify.js +0 -0
  37. package/docs/scripts/search.js +0 -0
  38. package/docs/styles/jsdoc.css +0 -0
  39. package/docs/styles/prettify.css +0 -0
  40. package/jest.config.js +0 -0
  41. package/jsdoc.json +0 -0
  42. package/link_mcs.sh +0 -0
  43. package/package.json +1 -1
  44. package/src/contentTypeConfig.js +2 -0
  45. package/src/index.d.ts +61 -4
  46. package/src/index.js +61 -4
  47. package/src/services/contentLikes.js +11 -11
  48. package/src/services/contentProgress.js +153 -0
  49. package/src/services/dataContext.js +3 -2
  50. package/src/services/railcontent.js +363 -6
  51. package/src/services/sanity.js +74 -0
  52. package/test/contentLikes.test.js +1 -1
  53. package/test/contentProgress.test.js +56 -0
  54. package/test/sanityQueryService.test.js +84 -49
package/src/index.d.ts CHANGED
@@ -12,6 +12,20 @@ import {
12
12
  } from './services/contentLikes.js';
13
13
 
14
14
  import {
15
+ contentStatusCompleted,
16
+ contentStatusReset,
17
+ contentStatusStarted,
18
+ getProgressPercentage,
19
+ getProgressState,
20
+ getResumeTimeSeconds,
21
+ recordWatchSession
22
+ } from './services/contentProgress.js';
23
+
24
+ import {
25
+ createPlaylist,
26
+ deletePlaylist,
27
+ deletePlaylistLike,
28
+ duplicatePlaylist,
15
29
  fetchAllCompletedStates,
16
30
  fetchChallengeLessonData,
17
31
  fetchChallengeMetadata,
@@ -19,17 +33,31 @@ import {
19
33
  fetchCompletedState,
20
34
  fetchContentInProgress,
21
35
  fetchContentPageUserData,
36
+ fetchContentProgress,
22
37
  fetchHandler,
38
+ fetchPlaylist,
39
+ fetchPlaylistItems,
23
40
  fetchSongsInProgress,
24
41
  fetchUserAward,
25
42
  fetchUserChallengeProgress,
43
+ fetchUserLikes,
26
44
  fetchUserPermissions,
45
+ fetchUserPlaylists,
46
+ likePlaylist,
27
47
  postChallengesCommunityNotification,
28
48
  postChallengesEnroll,
29
49
  postChallengesEnrollmentNotification,
30
50
  postChallengesLeave,
31
51
  postChallengesSetStartDate,
32
- postChallengesUnlock
52
+ postChallengesUnlock,
53
+ postContentCompleted,
54
+ postContentLiked,
55
+ postContentReset,
56
+ postContentStarted,
57
+ postContentUnliked,
58
+ postRecordWatchSession,
59
+ updatePlaylist,
60
+ updatePlaylistItem
33
61
  } from './services/railcontent.js';
34
62
 
35
63
  import {
@@ -49,6 +77,7 @@ import {
49
77
  fetchCourseOverview,
50
78
  fetchFoundation,
51
79
  fetchGenreLessons,
80
+ fetchHierarchy,
52
81
  fetchLessonContent,
53
82
  fetchLiveEvent,
54
83
  fetchMetadata,
@@ -64,6 +93,7 @@ import {
64
93
  fetchPackChildren,
65
94
  fetchPackData,
66
95
  fetchParentByRailContentId,
96
+ fetchParentForDownload,
67
97
  fetchRelatedLessons,
68
98
  fetchRelatedMethodLessons,
69
99
  fetchRelatedSongs,
@@ -74,14 +104,21 @@ import {
74
104
  fetchSongById,
75
105
  fetchSongCount,
76
106
  fetchSongFilterOptions,
107
+ fetchTopLevelParentId,
77
108
  fetchUpcomingEvents,
78
109
  fetchWorkouts,
79
- getSortOrder,
80
- fetchParentForDownload,
110
+ getSortOrder
81
111
  } from './services/sanity.js';
82
112
 
83
113
  declare module 'musora-content-services' {
84
114
  export {
115
+ contentStatusCompleted,
116
+ contentStatusReset,
117
+ contentStatusStarted,
118
+ createPlaylist,
119
+ deletePlaylist,
120
+ deletePlaylistLike,
121
+ duplicatePlaylist,
85
122
  fetchAll,
86
123
  fetchAllCompletedStates,
87
124
  fetchAllFilterOptions,
@@ -102,10 +139,12 @@ declare module 'musora-content-services' {
102
139
  fetchCompletedState,
103
140
  fetchContentInProgress,
104
141
  fetchContentPageUserData,
142
+ fetchContentProgress,
105
143
  fetchCourseOverview,
106
144
  fetchFoundation,
107
145
  fetchGenreLessons,
108
146
  fetchHandler,
147
+ fetchHierarchy,
109
148
  fetchLessonContent,
110
149
  fetchLiveEvent,
111
150
  fetchMetadata,
@@ -121,6 +160,9 @@ declare module 'musora-content-services' {
121
160
  fetchPackChildren,
122
161
  fetchPackData,
123
162
  fetchParentByRailContentId,
163
+ fetchParentForDownload,
164
+ fetchPlaylist,
165
+ fetchPlaylistItems,
124
166
  fetchRelatedLessons,
125
167
  fetchRelatedMethodLessons,
126
168
  fetchRelatedSongs,
@@ -132,23 +174,38 @@ declare module 'musora-content-services' {
132
174
  fetchSongCount,
133
175
  fetchSongFilterOptions,
134
176
  fetchSongsInProgress,
177
+ fetchTopLevelParentId,
135
178
  fetchUpcomingEvents,
136
179
  fetchUserAward,
137
180
  fetchUserChallengeProgress,
181
+ fetchUserLikes,
138
182
  fetchUserPermissions,
183
+ fetchUserPlaylists,
139
184
  fetchWorkouts,
185
+ getProgressPercentage,
186
+ getProgressState,
187
+ getResumeTimeSeconds,
140
188
  getSortOrder,
141
189
  globalConfig,
142
190
  initializeService,
143
191
  isContentLiked,
144
192
  likeContent,
193
+ likePlaylist,
145
194
  postChallengesCommunityNotification,
146
195
  postChallengesEnroll,
147
196
  postChallengesEnrollmentNotification,
148
197
  postChallengesLeave,
149
198
  postChallengesSetStartDate,
150
199
  postChallengesUnlock,
200
+ postContentCompleted,
201
+ postContentLiked,
202
+ postContentReset,
203
+ postContentStarted,
204
+ postContentUnliked,
205
+ postRecordWatchSession,
206
+ recordWatchSession,
151
207
  unlikeContent,
152
- fetchParentForDownload,
208
+ updatePlaylist,
209
+ updatePlaylistItem,
153
210
  }
154
211
  }
package/src/index.js CHANGED
@@ -12,6 +12,20 @@ import {
12
12
  } from './services/contentLikes.js';
13
13
 
14
14
  import {
15
+ contentStatusCompleted,
16
+ contentStatusReset,
17
+ contentStatusStarted,
18
+ getProgressPercentage,
19
+ getProgressState,
20
+ getResumeTimeSeconds,
21
+ recordWatchSession
22
+ } from './services/contentProgress.js';
23
+
24
+ import {
25
+ createPlaylist,
26
+ deletePlaylist,
27
+ deletePlaylistLike,
28
+ duplicatePlaylist,
15
29
  fetchAllCompletedStates,
16
30
  fetchChallengeLessonData,
17
31
  fetchChallengeMetadata,
@@ -19,17 +33,31 @@ import {
19
33
  fetchCompletedState,
20
34
  fetchContentInProgress,
21
35
  fetchContentPageUserData,
36
+ fetchContentProgress,
22
37
  fetchHandler,
38
+ fetchPlaylist,
39
+ fetchPlaylistItems,
23
40
  fetchSongsInProgress,
24
41
  fetchUserAward,
25
42
  fetchUserChallengeProgress,
43
+ fetchUserLikes,
26
44
  fetchUserPermissions,
45
+ fetchUserPlaylists,
46
+ likePlaylist,
27
47
  postChallengesCommunityNotification,
28
48
  postChallengesEnroll,
29
49
  postChallengesEnrollmentNotification,
30
50
  postChallengesLeave,
31
51
  postChallengesSetStartDate,
32
- postChallengesUnlock
52
+ postChallengesUnlock,
53
+ postContentCompleted,
54
+ postContentLiked,
55
+ postContentReset,
56
+ postContentStarted,
57
+ postContentUnliked,
58
+ postRecordWatchSession,
59
+ updatePlaylist,
60
+ updatePlaylistItem
33
61
  } from './services/railcontent.js';
34
62
 
35
63
  import {
@@ -49,6 +77,7 @@ import {
49
77
  fetchCourseOverview,
50
78
  fetchFoundation,
51
79
  fetchGenreLessons,
80
+ fetchHierarchy,
52
81
  fetchLessonContent,
53
82
  fetchLiveEvent,
54
83
  fetchMetadata,
@@ -64,6 +93,7 @@ import {
64
93
  fetchPackChildren,
65
94
  fetchPackData,
66
95
  fetchParentByRailContentId,
96
+ fetchParentForDownload,
67
97
  fetchRelatedLessons,
68
98
  fetchRelatedMethodLessons,
69
99
  fetchRelatedSongs,
@@ -74,13 +104,20 @@ import {
74
104
  fetchSongById,
75
105
  fetchSongCount,
76
106
  fetchSongFilterOptions,
107
+ fetchTopLevelParentId,
77
108
  fetchUpcomingEvents,
78
109
  fetchWorkouts,
79
- getSortOrder,
80
- fetchParentForDownload,
110
+ getSortOrder
81
111
  } from './services/sanity.js';
82
112
 
83
113
  export {
114
+ contentStatusCompleted,
115
+ contentStatusReset,
116
+ contentStatusStarted,
117
+ createPlaylist,
118
+ deletePlaylist,
119
+ deletePlaylistLike,
120
+ duplicatePlaylist,
84
121
  fetchAll,
85
122
  fetchAllCompletedStates,
86
123
  fetchAllFilterOptions,
@@ -101,10 +138,12 @@ export {
101
138
  fetchCompletedState,
102
139
  fetchContentInProgress,
103
140
  fetchContentPageUserData,
141
+ fetchContentProgress,
104
142
  fetchCourseOverview,
105
143
  fetchFoundation,
106
144
  fetchGenreLessons,
107
145
  fetchHandler,
146
+ fetchHierarchy,
108
147
  fetchLessonContent,
109
148
  fetchLiveEvent,
110
149
  fetchMetadata,
@@ -120,6 +159,9 @@ export {
120
159
  fetchPackChildren,
121
160
  fetchPackData,
122
161
  fetchParentByRailContentId,
162
+ fetchParentForDownload,
163
+ fetchPlaylist,
164
+ fetchPlaylistItems,
123
165
  fetchRelatedLessons,
124
166
  fetchRelatedMethodLessons,
125
167
  fetchRelatedSongs,
@@ -131,22 +173,37 @@ export {
131
173
  fetchSongCount,
132
174
  fetchSongFilterOptions,
133
175
  fetchSongsInProgress,
176
+ fetchTopLevelParentId,
134
177
  fetchUpcomingEvents,
135
178
  fetchUserAward,
136
179
  fetchUserChallengeProgress,
180
+ fetchUserLikes,
137
181
  fetchUserPermissions,
182
+ fetchUserPlaylists,
138
183
  fetchWorkouts,
184
+ getProgressPercentage,
185
+ getProgressState,
186
+ getResumeTimeSeconds,
139
187
  getSortOrder,
140
188
  globalConfig,
141
189
  initializeService,
142
190
  isContentLiked,
143
191
  likeContent,
192
+ likePlaylist,
144
193
  postChallengesCommunityNotification,
145
194
  postChallengesEnroll,
146
195
  postChallengesEnrollmentNotification,
147
196
  postChallengesLeave,
148
197
  postChallengesSetStartDate,
149
198
  postChallengesUnlock,
199
+ postContentCompleted,
200
+ postContentLiked,
201
+ postContentReset,
202
+ postContentStarted,
203
+ postContentUnliked,
204
+ postRecordWatchSession,
205
+ recordWatchSession,
150
206
  unlikeContent,
151
- fetchParentForDownload,
207
+ updatePlaylist,
208
+ updatePlaylistItem,
152
209
  };
@@ -1,5 +1,5 @@
1
1
  import {fetchUserLikes, postContentLiked, postContentUnliked} from "./railcontent";
2
- import {DataContext, ContentVersionKey} from "./dataContext";
2
+ import {DataContext, ContentLikesVersionKey} from "./dataContext";
3
3
 
4
4
  /**
5
5
  * Exported functions that are excluded from index generation.
@@ -8,7 +8,7 @@ import {DataContext, ContentVersionKey} from "./dataContext";
8
8
  */
9
9
  const excludeFromGeneratedIndex = [];
10
10
 
11
- export let dataContext = new DataContext(ContentVersionKey, fetchUserLikes);
11
+ export let dataContext = new DataContext(ContentLikesVersionKey, fetchUserLikes);
12
12
 
13
13
  export async function isContentLiked(contentId) {
14
14
  contentId = parseInt(contentId);
@@ -19,12 +19,12 @@ export async function isContentLiked(contentId) {
19
19
  export async function likeContent(contentId) {
20
20
  contentId = parseInt(contentId);
21
21
  await dataContext.update(
22
- function (context) {
23
- if (!context.data.includes(contentId)) {
24
- context.data.push(contentId);
22
+ function (localContext) {
23
+ if (!localContext.data.includes(contentId)) {
24
+ localContext.data.push(contentId);
25
25
  }
26
26
  },
27
- async function(){
27
+ async function () {
28
28
  return postContentLiked(contentId);
29
29
  }
30
30
  );
@@ -33,15 +33,15 @@ export async function likeContent(contentId) {
33
33
  export async function unlikeContent(contentId) {
34
34
  contentId = parseInt(contentId);
35
35
  await dataContext.update(
36
- function (context) {
37
- if (context.data.includes(contentId)) {
38
- const index = context.data.indexOf(contentId);
36
+ function (localContext) {
37
+ if (localContext.data.includes(contentId)) {
38
+ const index = localContext.data.indexOf(contentId);
39
39
  if (index > -1) { // only splice array when item is found
40
- context.data.splice(index, 1); // 2nd parameter means remove one item only
40
+ localContext.data.splice(index, 1); // 2nd parameter means remove one item only
41
41
  }
42
42
  }
43
43
  },
44
- async function(){
44
+ async function () {
45
45
  return postContentUnliked(contentId);
46
46
  }
47
47
  );
@@ -0,0 +1,153 @@
1
+ import {
2
+ fetchContentProgress,
3
+ postContentCompleted,
4
+ postContentReset,
5
+ postContentStarted,
6
+ postRecordWatchSession
7
+ } from "./railcontent";
8
+ import {DataContext, ContentProgressVersionKey} from "./dataContext";
9
+ import {fetchHierarchy, fetchParentByRailContentId} from "./sanity";
10
+
11
+ const STATE_STARTED = 'started';
12
+ const STATE_COMPLETED = 'completed';
13
+ const DATA_KEY_STATUS = 's';
14
+ const DATA_KEY_PROGRESS = 'p';
15
+ const DATA_KEY_RESUME_TIME = 't';
16
+ export let dataContext = new DataContext(ContentProgressVersionKey, fetchContentProgress());
17
+
18
+ export async function getProgressPercentage(contentId) {
19
+ let data = await dataContext.getData();
20
+ return data[contentId]?.[DATA_KEY_PROGRESS] ?? 0;
21
+ }
22
+
23
+ export async function getProgressState(contentId) {
24
+ let data = await dataContext.getData();
25
+ return data[contentId]?.[DATA_KEY_STATUS] ?? 0;
26
+ }
27
+
28
+ export async function getResumeTimeSeconds(contentId) {
29
+ let data = await dataContext.getData();
30
+ return data[contentId]?.[DATA_KEY_RESUME_TIME] ?? 0;
31
+ }
32
+
33
+ export async function contentStatusStarted(contentId) {
34
+ await dataContext.update(
35
+ function (localContext) {
36
+ let data = localContext.data[contentId] ?? [];
37
+ let progress = data?.[DATA_KEY_PROGRESS] ?? 0;
38
+ let status = data?.[DATA_KEY_STATUS] ?? 0;
39
+
40
+ if (status !== STATE_COMPLETED && progress !== 100) {
41
+ status = STATE_STARTED;
42
+ }
43
+
44
+ data[DATA_KEY_STATUS] = status;
45
+ localContext.data[contentId] = data;
46
+ },
47
+ async function () {
48
+ return postContentStarted(contentId);
49
+ });
50
+ }
51
+
52
+
53
+ export async function contentStatusCompleted(contentId) {
54
+ await dataContext.update(
55
+ function (localContext) {
56
+ let hierarchy = fetchHierarchy(contentId);
57
+ completeStatusInLocalContext(contentId, localContext, hierarchy);
58
+ },
59
+ async function () {
60
+ return postContentCompleted(contentId);
61
+ });
62
+ }
63
+
64
+ function completeStatusInLocalContext(contentId, localContext, hierarchy) {
65
+ let data = localContext.data[contentId] ?? [];
66
+ data[DATA_KEY_STATUS] = STATE_COMPLETED;
67
+ data[DATA_KEY_PROGRESS] = 100;
68
+ localContext.data[contentId] = data;
69
+
70
+ let children = hierarchy.children[contentId] ?? [];
71
+ for(let i = 0; i < children.length; i++) {
72
+ let childId = children[i];
73
+ completeStatusInLocalContext(childId, localContext, hierarchy);
74
+ }
75
+ }
76
+
77
+ export async function contentStatusReset(contentId) {
78
+ await dataContext.update(
79
+ function (localContext) {
80
+ const index = localContext.data.indexOf(contentId);
81
+ if (index > -1) { // only splice array when item is found
82
+ localContext.data.splice(index, 1); // 2nd parameter means remove one item only
83
+ }
84
+ },
85
+ async function () {
86
+ return postContentReset(contentId);
87
+ });
88
+ }
89
+
90
+
91
+ export async function recordWatchSession({
92
+ mediaId,
93
+ mediaType,
94
+ mediaCategory,
95
+ watchPositionSeconds,
96
+ totalDurationSeconds,
97
+ sessionToken,
98
+ brand,
99
+ contentId = null
100
+ }) {
101
+ await dataContext.update(
102
+ async function (localContext) {
103
+ if (contentId) {
104
+ let data = localContext.data[contentId] ?? [];
105
+ let progress = data?.[DATA_KEY_PROGRESS] ?? 0;
106
+ let status = data?.[DATA_KEY_STATUS] ?? 0;
107
+
108
+ if (status !== STATE_COMPLETED && progress !== 100) {
109
+ status = STATE_STARTED;
110
+ progress = Math.min(99, Math.round(watchPositionSeconds ?? 0 / Math.max(1, totalDurationSeconds ?? 0) * 100));
111
+ }
112
+
113
+ data[DATA_KEY_PROGRESS] = progress;
114
+ data[DATA_KEY_STATUS] = status;
115
+ data[DATA_KEY_RESUME_TIME] = watchPositionSeconds;
116
+ localContext.data[contentId] = data;
117
+
118
+ let hierarchy = await fetchHierarchy(contentId);
119
+ bubbleProgress(hierarchy, contentId, localContext);
120
+ }
121
+ },
122
+ async function () {
123
+ return postRecordWatchSession({
124
+ mediaId,
125
+ mediaType,
126
+ mediaCategory,
127
+ watchPositionSeconds,
128
+ totalDurationSeconds,
129
+ sessionToken,
130
+ brand,
131
+ contentId
132
+ });
133
+ }
134
+ );
135
+ }
136
+
137
+ function bubbleProgress(hierarchy, contentId, localContext) {
138
+ let parentId = hierarchy.parents[contentId];
139
+ if (!parentId) return;
140
+ let data = localContext.data[parentId] ?? [];
141
+ let progress = data[DATA_KEY_PROGRESS];
142
+ let status = data[DATA_KEY_STATUS];
143
+ if (status !== STATE_COMPLETED && progress !== 100) {
144
+ let childProgress = hierarchy.children[parentId].map(function (childId) {
145
+ return localContext.data[childId]?.[DATA_KEY_PROGRESS] ?? 0;
146
+ });
147
+ progress = Math.round(childProgress.reduce((a, b) => a + b, 0) / childProgress.length);
148
+ data[DATA_KEY_PROGRESS] = progress;
149
+ localContext.data[parentId] = data;
150
+ }
151
+ bubbleProgress(hierarchy, parentId, localContext);
152
+ }
153
+
@@ -8,7 +8,8 @@ import {globalConfig} from "./config";
8
8
  const excludeFromGeneratedIndex = [];
9
9
 
10
10
  //These constants need to match MWP UserDataVersionKeyEnum enum
11
- export const ContentVersionKey = 0;
11
+ export const ContentLikesVersionKey = 0;
12
+ export const ContentProgressVersionKey = 1;
12
13
 
13
14
  let cache = null;
14
15
 
@@ -78,7 +79,7 @@ export class DataContext {
78
79
  async update(localUpdateFunction, serverUpdateFunction) {
79
80
  await this.ensureLocalContextLoaded();
80
81
  if (this.context) {
81
- localUpdateFunction(this.context);
82
+ await localUpdateFunction(this.context);
82
83
  this.context.version++;
83
84
  let data = JSON.stringify(this.context);
84
85
  cache.setItem(this.localStorageKey, data);