musora-content-services 2.96.2 → 2.98.0

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 (143) hide show
  1. package/.claude/settings.local.json +8 -3
  2. package/.coderabbit.yaml +0 -0
  3. package/.editorconfig +0 -0
  4. package/.github/pull_request_template.md +0 -0
  5. package/.github/workflows/conventional-commits.yaml +0 -0
  6. package/.github/workflows/docs.js.yml +0 -0
  7. package/.github/workflows/node.js.yml +0 -0
  8. package/.prettierignore +0 -0
  9. package/.prettierrc +0 -0
  10. package/.yarnrc.yml +1 -0
  11. package/CHANGELOG.md +31 -0
  12. package/README.md +0 -0
  13. package/jest.config.js +0 -0
  14. package/package.json +1 -1
  15. package/src/contentMetaData.js +0 -0
  16. package/src/index.d.ts +10 -0
  17. package/src/index.js +10 -0
  18. package/src/infrastructure/http/HttpClient.ts +0 -0
  19. package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
  20. package/src/infrastructure/http/index.ts +0 -0
  21. package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
  22. package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
  23. package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
  24. package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
  25. package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
  26. package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
  27. package/src/lib/brands.ts +0 -0
  28. package/src/lib/httpHelper.js +0 -0
  29. package/src/lib/lastUpdated.js +0 -0
  30. package/src/services/api/types.js +0 -0
  31. package/src/services/api/types.ts +0 -0
  32. package/src/services/content/content.ts +0 -0
  33. package/src/services/content-org/content-org.js +0 -0
  34. package/src/services/content-org/guided-courses.ts +0 -0
  35. package/src/services/content-org/learning-paths.ts +15 -2
  36. package/src/services/content-org/playlists-types.js +0 -0
  37. package/src/services/content-org/playlists.js +0 -0
  38. package/src/services/content.js +0 -0
  39. package/src/services/contentAggregator.js +161 -2
  40. package/src/services/contentLikes.js +0 -0
  41. package/src/services/contentProgress.js +122 -4
  42. package/src/services/dataContext.js +0 -0
  43. package/src/services/dateUtils.js +0 -0
  44. package/src/services/eventsAPI.js +0 -0
  45. package/src/services/forums/categories.ts +0 -0
  46. package/src/services/forums/forums.ts +0 -0
  47. package/src/services/forums/posts.ts +0 -0
  48. package/src/services/forums/threads.ts +0 -0
  49. package/src/services/forums/types.ts +0 -0
  50. package/src/services/gamification/gamification.js +0 -0
  51. package/src/services/imageSRCBuilder.js +0 -0
  52. package/src/services/imageSRCVerify.js +0 -0
  53. package/src/services/liveTesting.ts +0 -0
  54. package/src/services/permissions/PermissionsAdapter.ts +0 -0
  55. package/src/services/permissions/PermissionsAdapterFactory.ts +0 -0
  56. package/src/services/permissions/PermissionsV1Adapter.ts +0 -0
  57. package/src/services/permissions/PermissionsV2Adapter.ts +0 -0
  58. package/src/services/permissions/README.md +0 -0
  59. package/src/services/permissions/index.ts +0 -0
  60. package/src/services/railcontent.js +0 -0
  61. package/src/services/recommendations.js +0 -0
  62. package/src/services/reporting/README.md +0 -0
  63. package/src/services/reporting/reporting.ts +0 -0
  64. package/src/services/reporting/types.ts +0 -0
  65. package/src/services/sanity.js +6 -0
  66. package/src/services/sentry/.indexignore +0 -0
  67. package/src/services/sentry/index.ts +0 -0
  68. package/src/services/sync/.indexignore +0 -0
  69. package/src/services/sync/adapters/factory.ts +0 -0
  70. package/src/services/sync/adapters/lokijs.ts +0 -0
  71. package/src/services/sync/adapters/sqlite.ts +0 -0
  72. package/src/services/sync/concurrency-safety.ts +0 -0
  73. package/src/services/sync/context/index.ts +0 -0
  74. package/src/services/sync/context/providers/base.ts +0 -0
  75. package/src/services/sync/context/providers/connectivity.ts +0 -0
  76. package/src/services/sync/context/providers/durability.ts +0 -0
  77. package/src/services/sync/context/providers/index.ts +0 -0
  78. package/src/services/sync/context/providers/session.ts +0 -0
  79. package/src/services/sync/context/providers/tabs.ts +0 -0
  80. package/src/services/sync/context/providers/visibility.ts +0 -0
  81. package/src/services/sync/database/factory.ts +0 -0
  82. package/src/services/sync/errors/boundary.ts +0 -0
  83. package/src/services/sync/errors/index.ts +0 -0
  84. package/src/services/sync/index.ts +0 -0
  85. package/src/services/sync/models/Base.ts +0 -0
  86. package/src/services/sync/models/ContentLike.ts +0 -0
  87. package/src/services/sync/models/Practice.ts +0 -0
  88. package/src/services/sync/models/PracticeDayNote.ts +0 -0
  89. package/src/services/sync/repositories/base.ts +0 -0
  90. package/src/services/sync/repositories/content-likes.ts +0 -0
  91. package/src/services/sync/repositories/content-progress.ts +35 -10
  92. package/src/services/sync/repositories/practice-day-notes.ts +0 -0
  93. package/src/services/sync/resolver.ts +0 -0
  94. package/src/services/sync/run-scope.ts +0 -0
  95. package/src/services/sync/serializers/index.ts +0 -0
  96. package/src/services/sync/serializers/model.ts +0 -0
  97. package/src/services/sync/serializers/raw.ts +0 -0
  98. package/src/services/sync/strategies/base.ts +0 -0
  99. package/src/services/sync/strategies/index.ts +0 -0
  100. package/src/services/sync/strategies/initial.ts +0 -0
  101. package/src/services/sync/strategies/polling.ts +0 -0
  102. package/src/services/sync/telemetry/index.ts +0 -0
  103. package/src/services/sync/telemetry/sampling.ts +0 -0
  104. package/src/services/sync/utils/event-emitter.ts +0 -0
  105. package/src/services/sync/utils/index.ts +0 -0
  106. package/src/services/sync/utils/throttle.ts +0 -0
  107. package/src/services/sync/utils/timers.ts +0 -0
  108. package/src/services/types.js +0 -0
  109. package/src/services/user/account.ts +0 -0
  110. package/src/services/user/chat.js +0 -0
  111. package/src/services/user/interests.js +0 -0
  112. package/src/services/user/management.js +0 -0
  113. package/src/services/user/memberships.ts +32 -0
  114. package/src/services/user/notifications.js +0 -0
  115. package/src/services/user/payments.ts +0 -0
  116. package/src/services/user/permissions.js +0 -0
  117. package/src/services/user/profile.js +0 -0
  118. package/src/services/user/sessions.js +0 -0
  119. package/src/services/user/types.d.ts +0 -0
  120. package/src/services/user/types.js +0 -0
  121. package/src/services/user/user-management-system.js +0 -0
  122. package/test/content.test.js +0 -0
  123. package/test/contentLikes.test.js +0 -0
  124. package/test/contentProgress.test.js +0 -0
  125. package/test/dataContext.test.js +0 -0
  126. package/test/forum.test.js +0 -0
  127. package/test/imageSRCBuilder.test.js +0 -0
  128. package/test/imageSRCVerify.test.js +0 -0
  129. package/test/lib/lastUpdated.test.js +0 -0
  130. package/test/live/contentProgressLive.test.js +0 -0
  131. package/test/live/railcontentLive.test.js +0 -0
  132. package/test/localStorageMock.js +0 -0
  133. package/test/log.js +0 -0
  134. package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
  135. package/test/mockData/mockData_progress_content.json +0 -0
  136. package/test/mockData/mockData_sanity_progress_content.json +0 -0
  137. package/test/mockData/mockData_user_practices.json +0 -0
  138. package/test/notifications.test.js +0 -0
  139. package/test/progressRows.test.js +0 -0
  140. package/test/reporting.test.js +132 -0
  141. package/test/streakMessage.test.js +0 -0
  142. package/test/user/permissions.test.js +0 -0
  143. package/test/userActivity.test.js +0 -0
@@ -1,9 +1,14 @@
1
1
  {
2
2
  "permissions": {
3
3
  "allow": [
4
- "Bash(rg:*)",
5
- "Bash(npm run lint:*)"
4
+ "Read(//app/musora-platform-backend/**)",
5
+ "Read(//app/musora-platform-frontend/**)",
6
+ "Bash(find:*)",
7
+ "Bash(sed:*)",
8
+ "Read(//app/**)",
9
+ "Bash(cat:*)"
6
10
  ],
7
- "deny": []
11
+ "deny": [],
12
+ "ask": []
8
13
  }
9
14
  }
package/.coderabbit.yaml CHANGED
File without changes
package/.editorconfig CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
package/.prettierignore CHANGED
File without changes
package/.prettierrc CHANGED
File without changes
package/.yarnrc.yml ADDED
@@ -0,0 +1 @@
1
+ nodeLinker: node-modules
package/CHANGELOG.md CHANGED
@@ -2,6 +2,37 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [2.98.0](https://github.com/railroadmedia/musora-content-services/compare/v2.94.7...v2.98.0) (2025-12-09)
6
+
7
+
8
+ ### Features
9
+
10
+ * **BEH-1195:** Awards System ([#606](https://github.com/railroadmedia/musora-content-services/issues/606)) ([a6ffd92](https://github.com/railroadmedia/musora-content-services/commit/a6ffd92ae518c8e530d4ef743f2649725fddb285))
11
+ * **BEH-1458:** method addcontexttocontent ([#633](https://github.com/railroadmedia/musora-content-services/issues/633)) ([95a2b0a](https://github.com/railroadmedia/musora-content-services/commit/95a2b0ae3f011e17f92b78ad4fcd1ebe7a2342ca))
12
+ * handle learning path complete logic ([#628](https://github.com/railroadmedia/musora-content-services/issues/628)) ([e5266d2](https://github.com/railroadmedia/musora-content-services/commit/e5266d2219597fc295dab97cec7b7ce8ac80a705))
13
+ * **MU2E2-219:** New method for membership upgrade ([0548f25](https://github.com/railroadmedia/musora-content-services/commit/0548f2531dd67abda63c0768ae0005448a27c163))
14
+ * **MU2E2-219:** New method for membership upgrade ([42f2cd8](https://github.com/railroadmedia/musora-content-services/commit/42f2cd825e70a9afeb9f13bd09a0709c003b3c26))
15
+
16
+
17
+ ### Bug Fixes
18
+
19
+ * **agi:** interface return types ([cc0cfae](https://github.com/railroadmedia/musora-content-services/commit/cc0cfae1282266567ac46a6eb072f76ffde7032e))
20
+ * **BR-330:** Wraps artists name in double quotes where its interpolated in the query ([8787399](https://github.com/railroadmedia/musora-content-services/commit/8787399e032d3522e5126ea9506f28cc5cdac22a))
21
+ * collection type check ([#619](https://github.com/railroadmedia/musora-content-services/issues/619)) ([df520a4](https://github.com/railroadmedia/musora-content-services/commit/df520a497c24091b41d207992a8a71daf0fc98e5))
22
+ * duplicate awards from wrong collection type handling ([#627](https://github.com/railroadmedia/musora-content-services/issues/627)) ([f6dfbf7](https://github.com/railroadmedia/musora-content-services/commit/f6dfbf778ad8e7d37d8e0f66ecd1aa5bec323330))
23
+ * make agi responses standard ([#630](https://github.com/railroadmedia/musora-content-services/issues/630)) ([0301133](https://github.com/railroadmedia/musora-content-services/commit/03011331cb835412af80111a97e6ae83fd6e56d5))
24
+ * method progress card cta ([#622](https://github.com/railroadmedia/musora-content-services/issues/622)) ([6788b48](https://github.com/railroadmedia/musora-content-services/commit/6788b48353d4122e1ca674a5fdd32abbfa8bb642))
25
+ * **onboarding:** update hard-coded data ([e33f309](https://github.com/railroadmedia/musora-content-services/commit/e33f309d0b051829e58f631f4b1368501bafc72b))
26
+ * show next learning path lesson logic for fetch learning path lessons ([#626](https://github.com/railroadmedia/musora-content-services/issues/626)) ([2925a5b](https://github.com/railroadmedia/musora-content-services/commit/2925a5be42715af7f77720209ef78f118ae95bf4))
27
+ * watermelon fixes round [#4](https://github.com/railroadmedia/musora-content-services/issues/4) ([#621](https://github.com/railroadmedia/musora-content-services/issues/621)) ([aa1e834](https://github.com/railroadmedia/musora-content-services/commit/aa1e834dbb287e4b581b67690038e086eb7a4d55))
28
+
29
+ ## [2.97.0](https://github.com/railroadmedia/musora-content-services/compare/v2.96.2...v2.97.0) (2025-12-09)
30
+
31
+
32
+ ### Features
33
+
34
+ * **BEH-1458:** method addcontexttocontent ([#633](https://github.com/railroadmedia/musora-content-services/issues/633)) ([95a2b0a](https://github.com/railroadmedia/musora-content-services/commit/95a2b0ae3f011e17f92b78ad4fcd1ebe7a2342ca))
35
+
5
36
  ### [2.96.2](https://github.com/railroadmedia/musora-content-services/compare/v2.96.1...v2.96.2) (2025-12-09)
6
37
 
7
38
  ### [2.96.1](https://github.com/railroadmedia/musora-content-services/compare/v2.96.0...v2.96.1) (2025-12-09)
package/README.md CHANGED
File without changes
package/jest.config.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "musora-content-services",
3
- "version": "2.96.2",
3
+ "version": "2.98.0",
4
4
  "description": "A package for Musoras content services ",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
File without changes
package/src/index.d.ts CHANGED
@@ -90,6 +90,7 @@ import {
90
90
 
91
91
  import {
92
92
  addContextToContent,
93
+ addContextToLearningPaths,
93
94
  getNavigateToForPlaylists
94
95
  } from './services/contentAggregator.js';
95
96
 
@@ -111,10 +112,13 @@ import {
111
112
  getAllStartedOrCompleted,
112
113
  getLastInteractedOf,
113
114
  getNavigateTo,
115
+ getNavigateToForMethod,
114
116
  getProgressDataByIds,
117
+ getProgressDataByIdsAndCollections,
115
118
  getProgressState,
116
119
  getProgressStateByIds,
117
120
  getResumeTimeSecondsByIds,
121
+ getResumeTimeSecondsByIdsAndCollections,
118
122
  getStartedOrCompletedProgressOnly,
119
123
  recordWatchSession
120
124
  } from './services/contentProgress.js';
@@ -348,6 +352,7 @@ import {
348
352
  import {
349
353
  fetchMemberships,
350
354
  fetchRechargeTokens,
355
+ getUpgradePrice,
351
356
  restorePurchases,
352
357
  upgradeSubscription
353
358
  } from './services/user/memberships.ts';
@@ -426,6 +431,7 @@ import {
426
431
  declare module 'musora-content-services' {
427
432
  export {
428
433
  addContextToContent,
434
+ addContextToLearningPaths,
429
435
  addItemToPlaylist,
430
436
  applyCloudflareWrapper,
431
437
  applySanityTransformations,
@@ -590,6 +596,7 @@ declare module 'musora-content-services' {
590
596
  getMethodCard,
591
597
  getMonday,
592
598
  getNavigateTo,
599
+ getNavigateToForMethod,
593
600
  getNavigateToForPlaylists,
594
601
  getNewAndUpcoming,
595
602
  getOnboardingRecommendedContent,
@@ -597,6 +604,7 @@ declare module 'musora-content-services' {
597
604
  getPracticeNotes,
598
605
  getPracticeSessions,
599
606
  getProgressDataByIds,
607
+ getProgressDataByIdsAndCollections,
600
608
  getProgressRows,
601
609
  getProgressState,
602
610
  getProgressStateByIds,
@@ -605,6 +613,7 @@ declare module 'musora-content-services' {
605
613
  getRecommendedForYou,
606
614
  getReportIssueOptions,
607
615
  getResumeTimeSecondsByIds,
616
+ getResumeTimeSecondsByIdsAndCollections,
608
617
  getSanityDate,
609
618
  getScheduleContentRows,
610
619
  getSortOrder,
@@ -612,6 +621,7 @@ declare module 'musora-content-services' {
612
621
  getTabResults,
613
622
  getTimeRemainingUntilLocal,
614
623
  getToday,
624
+ getUpgradePrice,
615
625
  getUserData,
616
626
  getUserMonthlyStats,
617
627
  getUserSignature,
package/src/index.js CHANGED
@@ -94,6 +94,7 @@ import {
94
94
 
95
95
  import {
96
96
  addContextToContent,
97
+ addContextToLearningPaths,
97
98
  getNavigateToForPlaylists
98
99
  } from './services/contentAggregator.js';
99
100
 
@@ -115,10 +116,13 @@ import {
115
116
  getAllStartedOrCompleted,
116
117
  getLastInteractedOf,
117
118
  getNavigateTo,
119
+ getNavigateToForMethod,
118
120
  getProgressDataByIds,
121
+ getProgressDataByIdsAndCollections,
119
122
  getProgressState,
120
123
  getProgressStateByIds,
121
124
  getResumeTimeSecondsByIds,
125
+ getResumeTimeSecondsByIdsAndCollections,
122
126
  getStartedOrCompletedProgressOnly,
123
127
  recordWatchSession
124
128
  } from './services/contentProgress.js';
@@ -352,6 +356,7 @@ import {
352
356
  import {
353
357
  fetchMemberships,
354
358
  fetchRechargeTokens,
359
+ getUpgradePrice,
355
360
  restorePurchases,
356
361
  upgradeSubscription
357
362
  } from './services/user/memberships.ts';
@@ -425,6 +430,7 @@ import {
425
430
 
426
431
  export {
427
432
  addContextToContent,
433
+ addContextToLearningPaths,
428
434
  addItemToPlaylist,
429
435
  applyCloudflareWrapper,
430
436
  applySanityTransformations,
@@ -589,6 +595,7 @@ export {
589
595
  getMethodCard,
590
596
  getMonday,
591
597
  getNavigateTo,
598
+ getNavigateToForMethod,
592
599
  getNavigateToForPlaylists,
593
600
  getNewAndUpcoming,
594
601
  getOnboardingRecommendedContent,
@@ -596,6 +603,7 @@ export {
596
603
  getPracticeNotes,
597
604
  getPracticeSessions,
598
605
  getProgressDataByIds,
606
+ getProgressDataByIdsAndCollections,
599
607
  getProgressRows,
600
608
  getProgressState,
601
609
  getProgressStateByIds,
@@ -604,6 +612,7 @@ export {
604
612
  getRecommendedForYou,
605
613
  getReportIssueOptions,
606
614
  getResumeTimeSecondsByIds,
615
+ getResumeTimeSecondsByIdsAndCollections,
607
616
  getSanityDate,
608
617
  getScheduleContentRows,
609
618
  getSortOrder,
@@ -611,6 +620,7 @@ export {
611
620
  getTabResults,
612
621
  getTimeRemainingUntilLocal,
613
622
  getToday,
623
+ getUpgradePrice,
614
624
  getUserData,
615
625
  getUserMonthlyStats,
616
626
  getUserSignature,
File without changes
File without changes
File without changes
package/src/lib/brands.ts CHANGED
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -15,6 +15,7 @@ import {
15
15
  import { COLLECTION_TYPE, STATE } from '../sync/models/ContentProgress'
16
16
  import { SyncWriteDTO } from '../sync'
17
17
  import { ContentProgress } from '../sync/models'
18
+ import { CollectionParameter } from '../sync/repositories/content-progress'
18
19
 
19
20
  const BASE_PATH: string = `/api/content-org`
20
21
  const LEARNING_PATHS_PATH = `${BASE_PATH}/v1/user/learning-paths`
@@ -39,6 +40,11 @@ interface DailySession {
39
40
  learning_path_id: number
40
41
  }
41
42
 
43
+ interface CollectionObject {
44
+ id: number
45
+ type: COLLECTION_TYPE.LEARNING_PATH
46
+ }
47
+
42
48
  /**
43
49
  * Gets today's daily session for the user.
44
50
  * @param brand
@@ -308,10 +314,11 @@ export async function completeLearningPathIntroVideo(
308
314
 
309
315
  response.intro_video_response = await completeIfNotCompleted(introVideoId)
310
316
 
311
- const collection = { id: learningPathId, type: COLLECTION_TYPE.LEARNING_PATH }
317
+ const collection: CollectionObject = { id: learningPathId, type: COLLECTION_TYPE.LEARNING_PATH }
312
318
 
313
319
  if (!lessonsToImport) {
314
- response.learning_path_reset_response = await contentStatusReset(learningPathId, collection)
320
+ response.learning_path_reset_response = await resetIfPossible(learningPathId, collection)
321
+
315
322
  } else {
316
323
  response.lesson_import_response = await contentsStatusCompleted(lessonsToImport, collection)
317
324
  }
@@ -327,6 +334,12 @@ async function completeIfNotCompleted(
327
334
  return introVideoStatus !== 'completed' ? await contentStatusCompleted(contentId) : null
328
335
  }
329
336
 
337
+ async function resetIfPossible(contentId: number, collection: CollectionParameter = null): Promise<SyncWriteDTO<ContentProgress, any> | null> {
338
+ const status = await getProgressState(contentId, collection)
339
+
340
+ return status !== '' ? await contentStatusReset(contentId, collection) : null
341
+ }
342
+
330
343
  export async function onContentCompletedLearningPathListener(event) {
331
344
  console.log('if')
332
345
  if (event?.collection?.type !== 'learning-path-v2') return
File without changes
File without changes
File without changes
@@ -1,11 +1,15 @@
1
1
  import {
2
2
  getNavigateTo,
3
+ getNavigateToForMethod,
3
4
  getProgressDataByIds,
5
+ getProgressDataByIdsAndCollections,
4
6
  getProgressStateByIds,
5
7
  getResumeTimeSecondsByIds,
8
+ getResumeTimeSecondsByIdsAndCollections,
6
9
  } from './contentProgress'
7
10
  import { isContentLikedByIds } from './contentLikes'
8
11
  import { fetchLastInteractedChild, fetchLikeCount } from './railcontent'
12
+ import {COLLECTION_TYPE} from "./sync/models/ContentProgress";
9
13
 
10
14
  /**
11
15
  * Combine sanity data with BE contextual data.
@@ -54,12 +58,11 @@ import { fetchLastInteractedChild, fetchLikeCount } from './railcontent'
54
58
  *
55
59
  */
56
60
 
57
- // need to add method support.
58
- // this means returning collection_type and collection_id
59
61
  export async function addContextToContent(dataPromise, ...dataArgs) {
60
62
  const lastArg = dataArgs[dataArgs.length - 1]
61
63
  const options = typeof lastArg === 'object' && !Array.isArray(lastArg) ? lastArg : {}
62
64
 
65
+ // todo: merge addProgressData with addResumeTimeSeconds to one watermelon call
63
66
  const {
64
67
  collection = null, // this is needed for different collection types like learning paths. has .id and .type
65
68
  dataField = null,
@@ -115,6 +118,121 @@ export async function addContextToContent(dataPromise, ...dataArgs) {
115
118
  return await processItems(data, addContext, dataField, isDataAnArray, dataField_includeParent)
116
119
  }
117
120
 
121
+ /**
122
+ * Enriches method content (learning paths) with contextual data.
123
+ *
124
+ * Key behaviors:
125
+ * 1. Enriches all learning paths in a method structure
126
+ * 2. Auto-sets collection for learning-path-v2 items when no collection specified
127
+ * 3. Enriches intro videos when dataField_includeIntroVideo is true
128
+ *
129
+ * @param dataPromise - promise or method that provides sanity data
130
+ * @param dataArgs - Arguments to pass to the dataPromise
131
+ * @param options - Same as addContextToContent, plus:
132
+ * @param options.dataField_includeIntroVideo - If true, adds progress to intro_video field where it exists
133
+ *
134
+ * @returns {Promise<Object | false>} - Enriched data or false if no data found
135
+ *
136
+ * @example
137
+ * // Enrich method structure with all learning paths
138
+ * const method = await addContextToMethodContent(fetchMethodV2Structure, brand, {
139
+ * dataField: 'learningPaths',
140
+ * dataField_includeIntroVideo: true,
141
+ * addProgressStatus: true,
142
+ * addProgressPercentage: true,
143
+ * })
144
+ *
145
+ * @example
146
+ * // Enrich single learning path with intro video
147
+ * const lp = await addContextToMethodContent(fetchByRailContentId, lpId, 'learning-path-v2', {
148
+ * collection: { id: lpId, type: 'learning-path-v2' },
149
+ * dataField: 'children',
150
+ * dataField_includeParent: true,
151
+ * dataField_includeIntroVideo: true,
152
+ * addProgressStatus: true,
153
+ * })
154
+ */
155
+ export async function addContextToLearningPaths(dataPromise, ...dataArgs) {
156
+ const lastArg = dataArgs[dataArgs.length - 1]
157
+ const options = typeof lastArg === 'object' && !Array.isArray(lastArg) ? lastArg : {}
158
+
159
+ // todo: merge addProgressData with addResumeTimeSeconds to one watermelon call
160
+ const {
161
+ dataField = null,
162
+ dataField_includeParent = false,
163
+ dataField_includeIntroVideo = false,
164
+ addProgressPercentage = false,
165
+ addProgressStatus = false,
166
+ addProgressTimestamp = false,
167
+ addIsLiked = false,
168
+ addLikeCount = false,
169
+ addResumeTimeSeconds = false,
170
+ addNavigateTo = false,
171
+ } = options
172
+
173
+ const dataParam = lastArg === options ? dataArgs.slice(0, -1) : dataArgs
174
+
175
+ let data = await dataPromise(...dataParam)
176
+ const isDataAnArray = Array.isArray(data)
177
+ if (isDataAnArray && data.length === 0) return data
178
+ if (!data) return false
179
+
180
+ let items = extractItemsWithCollectionFromMethodData(data, dataField, isDataAnArray, dataField_includeParent, dataField_includeIntroVideo) ?? []
181
+ if (items.length === 0) return data
182
+
183
+ let ids = items.map((item) => (
184
+ {
185
+ contentId: item.content?.id,
186
+ collection: item.collection
187
+ })
188
+ ).filter(obj => obj.contentId)
189
+
190
+ const justIds = ids.map(obj => obj.contentId)
191
+
192
+ const [
193
+ progressData,
194
+ isLikedData,
195
+ resumeTimeData,
196
+ navigateToData,
197
+ ] = await Promise.all([
198
+ addProgressPercentage || addProgressStatus || addProgressTimestamp
199
+ ? getProgressDataByIdsAndCollections(ids) : Promise.resolve(null),
200
+ addIsLiked ? isContentLikedByIds(justIds) : Promise.resolve(null),
201
+ addResumeTimeSeconds ? getResumeTimeSecondsByIdsAndCollections(ids) : Promise.resolve(null),
202
+ addNavigateTo ? getNavigateToForMethod(items) : Promise.resolve(null),
203
+ ])
204
+
205
+ const addContext = async (item) => {
206
+ const itemId = item.id || 0
207
+ const enrichedItem = {
208
+ ...item,
209
+ ...(addProgressPercentage ? { progressPercentage: progressData?.[itemId]?.progress } : {}),
210
+ ...(addProgressStatus ? { progressStatus: progressData?.[itemId]?.status } : {}),
211
+ ...(addProgressTimestamp ? { progressTimestamp: progressData?.[itemId]?.last_update } : {}),
212
+ ...(addIsLiked ? { isLiked: isLikedData?.[itemId] } : {}),
213
+ ...(addLikeCount && ids.length === 1 ? { likeCount: await fetchLikeCount(itemId) } : {}),
214
+ ...(addResumeTimeSeconds ? { resumeTime: resumeTimeData?.[itemId] } : {}),
215
+ ...(addNavigateTo ? { navigateTo: navigateToData?.[itemId] } : {}),
216
+ }
217
+
218
+ // Enrich intro_video if it exists and flag is set
219
+ if (dataField_includeIntroVideo && item?.intro_video?.id) {
220
+ enrichedItem.intro_video = {
221
+ ...item.intro_video,
222
+ ...(addProgressPercentage ? { progressPercentage: progressData?.[item.intro_video.id]?.progress } : {}),
223
+ ...(addProgressStatus ? { progressStatus: progressData?.[item.intro_video.id]?.status } : {}),
224
+ ...(addProgressTimestamp ? { progressTimestamp: progressData?.[item.intro_video.id]?.last_update } : {}),
225
+ ...(addIsLiked ? { isLiked: isLikedData?.[item.intro_video.id] } : {}),
226
+ ...(addResumeTimeSeconds ? { resumeTime: resumeTimeData?.[item.intro_video.id] } : {}),
227
+ }
228
+ }
229
+
230
+ return enrichedItem
231
+ }
232
+
233
+ return await processItems(data, addContext, dataField, isDataAnArray, dataField_includeParent)
234
+ }
235
+
118
236
  export async function getNavigateToForPlaylists(data, { dataField = null } = {}) {
119
237
  let playlists = extractItemsFromData(data, dataField, false, false)
120
238
  let allIds = []
@@ -191,6 +309,47 @@ function extractItemsFromData(data, dataField, isParentArray, includeParent) {
191
309
  return items
192
310
  }
193
311
 
312
+ function extractItemsWithCollectionFromMethodData(data, dataField, isDataAnArray, includeParent, includeIntroVideo) {
313
+ let items = [] // array of tuples {}
314
+
315
+ const extractLearningPathItems = (item) => {
316
+ if (item.type === COLLECTION_TYPE.LEARNING_PATH) {
317
+ const c = {type: COLLECTION_TYPE.LEARNING_PATH, id: item.id}
318
+
319
+ if (!dataField || (dataField && includeParent)) {
320
+ items.push(...getDataTuple([item], c))
321
+ }
322
+ if (includeIntroVideo) {
323
+ items.push(...getDataTuple([item.intro_video], null))
324
+ }
325
+ if (dataField) {
326
+ items.push(...getDataTuple(item[dataField], c))
327
+ }
328
+ } else { // is a lesson id, cant determine which collection it belongs to
329
+ // do not add it as we cant determine collection
330
+ // items.push(...getDataTuple([data], collection))
331
+ }
332
+ }
333
+
334
+ if (isDataAnArray) {
335
+ for (const item of data) {
336
+ extractLearningPathItems(item)
337
+ }
338
+ } else {
339
+ extractLearningPathItems(data)
340
+ }
341
+ return items
342
+
343
+ function getDataTuple(data, collection) {
344
+ const tuples = []
345
+ for (const item of data) {
346
+ const coll = collection || null
347
+ tuples.push({content: item, collection: coll})
348
+ }
349
+ return tuples
350
+ }
351
+ }
352
+
194
353
  async function processItems(data, addContext, dataField, isParentArray, includeParent) {
195
354
  if (dataField) {
196
355
  if (isParentArray) {
File without changes