musora-content-services 2.93.1 → 2.94.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +14 -0
- package/.coderabbit.yaml +0 -0
- package/.editorconfig +0 -0
- package/.github/pull_request_template.md +0 -0
- package/.github/workflows/conventional-commits.yaml +0 -0
- package/.github/workflows/node.js.yml +0 -0
- package/.prettierignore +0 -0
- package/.prettierrc +0 -0
- package/.yarnrc.yml +1 -0
- package/CHANGELOG.md +24 -0
- package/README.md +0 -0
- package/jest.config.js +0 -0
- package/jsdoc.json +0 -0
- package/package.json +1 -1
- package/src/contentMetaData.js +0 -0
- package/src/filterBuilder.js +0 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/src/infrastructure/http/HttpClient.ts +0 -0
- package/src/infrastructure/http/executors/FetchRequestExecutor.ts +0 -0
- package/src/infrastructure/http/index.ts +0 -0
- package/src/infrastructure/http/interfaces/HeaderProvider.ts +0 -0
- package/src/infrastructure/http/interfaces/HttpError.ts +0 -0
- package/src/infrastructure/http/interfaces/NetworkError.ts +0 -0
- package/src/infrastructure/http/interfaces/RequestExecutor.ts +0 -0
- package/src/infrastructure/http/interfaces/RequestOptions.ts +0 -0
- package/src/infrastructure/http/providers/DefaultHeaderProvider.ts +0 -0
- package/src/lib/httpHelper.js +0 -0
- package/src/lib/lastUpdated.js +0 -0
- package/src/lib/sanity/query.ts +0 -0
- package/src/services/api/types.js +0 -0
- package/src/services/api/types.ts +0 -0
- package/src/services/config.js +1 -1
- package/src/services/content/content.ts +0 -0
- package/src/services/content-org/content-org.js +0 -0
- package/src/services/content-org/guided-courses.ts +0 -0
- package/src/services/content-org/learning-paths.ts +31 -18
- package/src/services/content-org/playlists-types.js +0 -0
- package/src/services/content-org/playlists.js +0 -0
- package/src/services/content.js +0 -0
- package/src/services/contentAggregator.js +0 -0
- package/src/services/contentLikes.js +0 -0
- package/src/services/contentProgress.js +50 -13
- package/src/services/dataContext.js +0 -0
- package/src/services/dateUtils.js +0 -0
- package/src/services/eventsAPI.js +0 -0
- package/src/services/forums/categories.ts +0 -0
- package/src/services/forums/forums.ts +0 -0
- package/src/services/forums/posts.ts +0 -0
- package/src/services/forums/threads.ts +0 -0
- package/src/services/forums/types.ts +0 -0
- package/src/services/gamification/awards.ts +0 -0
- package/src/services/gamification/gamification.js +0 -0
- package/src/services/imageSRCBuilder.js +0 -0
- package/src/services/imageSRCVerify.js +0 -0
- package/src/services/liveTesting.ts +0 -0
- package/src/services/permissions/PermissionsAdapter.ts +0 -0
- package/src/services/permissions/PermissionsAdapterFactory.ts +0 -0
- package/src/services/permissions/PermissionsV1Adapter.ts +0 -0
- package/src/services/permissions/README.md +0 -0
- package/src/services/permissions/index.ts +0 -0
- package/src/services/progress-row/method-card.js +2 -1
- package/src/services/railcontent.js +0 -0
- package/src/services/recommendations.js +0 -0
- package/src/services/reporting/README.md +0 -0
- package/src/services/reporting/types.ts +0 -0
- package/src/services/sanity.js +27 -3
- package/src/services/sentry/.indexignore +0 -0
- package/src/services/sentry/index.ts +0 -0
- package/src/services/sync/.indexignore +0 -0
- package/src/services/sync/adapters/factory.ts +0 -0
- package/src/services/sync/adapters/lokijs.ts +0 -0
- package/src/services/sync/adapters/sqlite.ts +0 -0
- package/src/services/sync/concurrency-safety.ts +0 -0
- package/src/services/sync/context/index.ts +0 -0
- package/src/services/sync/context/providers/base.ts +0 -0
- package/src/services/sync/context/providers/connectivity.ts +0 -0
- package/src/services/sync/context/providers/durability.ts +0 -0
- package/src/services/sync/context/providers/index.ts +0 -0
- package/src/services/sync/context/providers/session.ts +0 -0
- package/src/services/sync/context/providers/tabs.ts +0 -0
- package/src/services/sync/context/providers/visibility.ts +0 -0
- package/src/services/sync/database/factory.ts +0 -0
- package/src/services/sync/errors/index.ts +0 -0
- package/src/services/sync/index.ts +0 -0
- package/src/services/sync/models/Base.ts +0 -0
- package/src/services/sync/models/ContentLike.ts +0 -0
- package/src/services/sync/models/ContentProgress.ts +1 -1
- package/src/services/sync/models/Practice.ts +0 -0
- package/src/services/sync/models/PracticeDayNote.ts +0 -0
- package/src/services/sync/models/index.ts +0 -0
- package/src/services/sync/repositories/base.ts +0 -0
- package/src/services/sync/repositories/content-likes.ts +0 -0
- package/src/services/sync/repositories/content-progress.ts +34 -7
- package/src/services/sync/repositories/index.ts +0 -0
- package/src/services/sync/repositories/practice-day-notes.ts +0 -0
- package/src/services/sync/repositories/practices.ts +0 -0
- package/src/services/sync/resolver.ts +0 -0
- package/src/services/sync/run-scope.ts +0 -0
- package/src/services/sync/schema/index.ts +0 -0
- package/src/services/sync/serializers/index.ts +0 -0
- package/src/services/sync/serializers/model.ts +0 -0
- package/src/services/sync/serializers/raw.ts +0 -0
- package/src/services/sync/store/push-coalescer.ts +0 -0
- package/src/services/sync/store-configs.ts +0 -0
- package/src/services/sync/strategies/base.ts +0 -0
- package/src/services/sync/strategies/index.ts +0 -0
- package/src/services/sync/strategies/initial.ts +0 -0
- package/src/services/sync/strategies/polling.ts +0 -0
- package/src/services/sync/utils/event-emitter.ts +0 -0
- package/src/services/sync/utils/index.ts +0 -0
- package/src/services/sync/utils/throttle.ts +0 -0
- package/src/services/sync/utils/timers.ts +0 -0
- package/src/services/types.js +0 -0
- package/src/services/user/account.ts +0 -0
- package/src/services/user/chat.js +0 -0
- package/src/services/user/interests.js +0 -0
- package/src/services/user/management.js +0 -0
- package/src/services/user/memberships.ts +0 -0
- package/src/services/user/notifications.js +0 -0
- package/src/services/user/payments.ts +0 -0
- package/src/services/user/permissions.js +0 -0
- package/src/services/user/profile.js +0 -0
- package/src/services/user/sessions.js +0 -0
- package/src/services/user/types.d.ts +0 -0
- package/src/services/user/types.js +0 -0
- package/src/services/user/user-management-system.js +0 -0
- package/src/services/userActivity.js +3 -2
- package/test/HttpClient.test.js +0 -0
- package/test/content.test.js +0 -0
- package/test/contentLikes.test.js +0 -0
- package/test/contentProgress.test.js +0 -0
- package/test/dataContext.test.js +0 -0
- package/test/forum.test.js +0 -0
- package/test/imageSRCBuilder.test.js +0 -0
- package/test/imageSRCVerify.test.js +0 -0
- package/test/initializeTests.js +0 -0
- package/test/learningPaths.test.js +0 -0
- package/test/lib/lastUpdated.test.js +0 -0
- package/test/live/contentProgressLive.test.js +0 -0
- package/test/live/railcontentLive.test.js +0 -0
- package/test/localStorageMock.js +0 -0
- package/test/log.js +0 -0
- package/test/mockData/mockData_fetchByRailContentIds_one_content.json +0 -0
- package/test/mockData/mockData_progress_content.json +0 -0
- package/test/mockData/mockData_sanity_progress_content.json +0 -0
- package/test/mockData/mockData_user_practices.json +0 -0
- package/test/notifications.test.js +0 -0
- package/test/progressRows.test.js +0 -0
- package/test/reporting.test.js +132 -0
- package/test/sanityQueryService.test.js +0 -0
- package/test/streakMessage.test.js +0 -0
- package/test/user/permissions.test.js +0 -0
- package/test/userActivity.test.js +0 -0
- package/tools/generate-index.cjs +0 -0
package/.coderabbit.yaml
CHANGED
|
File without changes
|
package/.editorconfig
CHANGED
|
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,30 @@
|
|
|
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.94.1](https://github.com/railroadmedia/musora-content-services/compare/v2.94.0...v2.94.1) (2025-12-03)
|
|
6
|
+
|
|
7
|
+
## [2.94.0](https://github.com/railroadmedia/musora-content-services/compare/v2.92.6...v2.94.0) (2025-12-03)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **BEH-1192:** revise fetchLearningPathProgressCheckLessons ([#604](https://github.com/railroadmedia/musora-content-services/issues/604)) ([db8cb70](https://github.com/railroadmedia/musora-content-services/commit/db8cb70ed70f4341fdd4651cdd3fea2b8047dc21))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
### Bug Fixes
|
|
16
|
+
|
|
17
|
+
* **agi:** make brand parameter less strict for now ([#603](https://github.com/railroadmedia/musora-content-services/issues/603)) ([b440b92](https://github.com/railroadmedia/musora-content-services/commit/b440b92ae1b4aab80d7e5ac5238c1e386cf09db8))
|
|
18
|
+
* method progress card subtitle ([#608](https://github.com/railroadmedia/musora-content-services/issues/608)) ([3bbfb56](https://github.com/railroadmedia/musora-content-services/commit/3bbfb56fce08e0753e7708bdf2d1356000b1c398))
|
|
19
|
+
* progress fixes and features with watermelon ([#607](https://github.com/railroadmedia/musora-content-services/issues/607)) ([005403c](https://github.com/railroadmedia/musora-content-services/commit/005403c9bbaeebe8c5ecb5315754de4448bcbf43))
|
|
20
|
+
* Pull in permission_v2 ([#602](https://github.com/railroadmedia/musora-content-services/issues/602)) ([289e32d](https://github.com/railroadmedia/musora-content-services/commit/289e32d08bd3a6448191b9f6034801e4cf8d40e5))
|
|
21
|
+
|
|
22
|
+
### [2.93.2](https://github.com/railroadmedia/musora-content-services/compare/v2.93.1...v2.93.2) (2025-12-02)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* progress fixes and features with watermelon ([#607](https://github.com/railroadmedia/musora-content-services/issues/607)) ([005403c](https://github.com/railroadmedia/musora-content-services/commit/005403c9bbaeebe8c5ecb5315754de4448bcbf43))
|
|
28
|
+
|
|
5
29
|
### [2.93.1](https://github.com/railroadmedia/musora-content-services/compare/v2.93.0...v2.93.1) (2025-12-02)
|
|
6
30
|
|
|
7
31
|
## [2.93.0](https://github.com/railroadmedia/musora-content-services/compare/v2.92.7...v2.93.0) (2025-12-02)
|
package/README.md
CHANGED
|
File without changes
|
package/jest.config.js
CHANGED
|
File without changes
|
package/jsdoc.json
CHANGED
|
File without changes
|
package/package.json
CHANGED
package/src/contentMetaData.js
CHANGED
|
File without changes
|
package/src/filterBuilder.js
CHANGED
|
File without changes
|
package/src/index.d.ts
CHANGED
|
@@ -91,6 +91,7 @@ import {
|
|
|
91
91
|
contentStatusCompleted,
|
|
92
92
|
contentStatusReset,
|
|
93
93
|
contentStatusStarted,
|
|
94
|
+
contentsStatusCompleted,
|
|
94
95
|
getAllCompleted,
|
|
95
96
|
getAllCompletedByIds,
|
|
96
97
|
getAllStarted,
|
|
@@ -250,6 +251,7 @@ import {
|
|
|
250
251
|
fetchContentRows,
|
|
251
252
|
fetchFoundation,
|
|
252
253
|
fetchHierarchy,
|
|
254
|
+
fetchLearningPathHierarchy,
|
|
253
255
|
fetchLeaving,
|
|
254
256
|
fetchLessonContent,
|
|
255
257
|
fetchLessonsFeaturingThisContent,
|
|
@@ -428,6 +430,7 @@ declare module 'musora-content-services' {
|
|
|
428
430
|
contentStatusCompleted,
|
|
429
431
|
contentStatusReset,
|
|
430
432
|
contentStatusStarted,
|
|
433
|
+
contentsStatusCompleted,
|
|
431
434
|
convertToTimeZone,
|
|
432
435
|
createComment,
|
|
433
436
|
createForumCategory,
|
|
@@ -494,6 +497,7 @@ declare module 'musora-content-services' {
|
|
|
494
497
|
fetchInterests,
|
|
495
498
|
fetchLastInteractedChild,
|
|
496
499
|
fetchLatestThreads,
|
|
500
|
+
fetchLearningPathHierarchy,
|
|
497
501
|
fetchLearningPathLessons,
|
|
498
502
|
fetchLearningPathProgressCheckLessons,
|
|
499
503
|
fetchLeaving,
|
package/src/index.js
CHANGED
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
contentStatusCompleted,
|
|
96
96
|
contentStatusReset,
|
|
97
97
|
contentStatusStarted,
|
|
98
|
+
contentsStatusCompleted,
|
|
98
99
|
getAllCompleted,
|
|
99
100
|
getAllCompletedByIds,
|
|
100
101
|
getAllStarted,
|
|
@@ -254,6 +255,7 @@ import {
|
|
|
254
255
|
fetchContentRows,
|
|
255
256
|
fetchFoundation,
|
|
256
257
|
fetchHierarchy,
|
|
258
|
+
fetchLearningPathHierarchy,
|
|
257
259
|
fetchLeaving,
|
|
258
260
|
fetchLessonContent,
|
|
259
261
|
fetchLessonsFeaturingThisContent,
|
|
@@ -427,6 +429,7 @@ export {
|
|
|
427
429
|
contentStatusCompleted,
|
|
428
430
|
contentStatusReset,
|
|
429
431
|
contentStatusStarted,
|
|
432
|
+
contentsStatusCompleted,
|
|
430
433
|
convertToTimeZone,
|
|
431
434
|
createComment,
|
|
432
435
|
createForumCategory,
|
|
@@ -493,6 +496,7 @@ export {
|
|
|
493
496
|
fetchInterests,
|
|
494
497
|
fetchLastInteractedChild,
|
|
495
498
|
fetchLatestThreads,
|
|
499
|
+
fetchLearningPathHierarchy,
|
|
496
500
|
fetchLearningPathLessons,
|
|
497
501
|
fetchLearningPathProgressCheckLessons,
|
|
498
502
|
fetchLeaving,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/lib/httpHelper.js
CHANGED
|
File without changes
|
package/src/lib/lastUpdated.js
CHANGED
|
File without changes
|
package/src/lib/sanity/query.ts
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/services/config.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,11 +7,14 @@ import { fetchByRailContentId, fetchMethodV2Structure } from '../sanity.js'
|
|
|
7
7
|
import { addContextToContent } from '../contentAggregator.js'
|
|
8
8
|
import {
|
|
9
9
|
contentStatusCompleted,
|
|
10
|
+
contentsStatusCompleted,
|
|
10
11
|
contentStatusReset,
|
|
11
12
|
getAllCompletedByIds,
|
|
12
13
|
getProgressState,
|
|
13
14
|
} from '../contentProgress.js'
|
|
14
|
-
import { STATE } from
|
|
15
|
+
import { COLLECTION_TYPE, STATE } from "../sync/models/ContentProgress";
|
|
16
|
+
import { SyncWriteDTO } from "../sync";
|
|
17
|
+
import { ContentProgress } from "../sync/models";
|
|
15
18
|
|
|
16
19
|
const BASE_PATH: string = `/api/content-org`
|
|
17
20
|
const LEARNING_PATHS_PATH = `${BASE_PATH}/v1/user/learning-paths`
|
|
@@ -22,6 +25,20 @@ interface ActiveLearningPathResponse {
|
|
|
22
25
|
active_learning_path_id: number,
|
|
23
26
|
}
|
|
24
27
|
|
|
28
|
+
interface DailySessionResponse {
|
|
29
|
+
user_id: number,
|
|
30
|
+
brand: string,
|
|
31
|
+
user_date: string
|
|
32
|
+
daily_session: DailySession[],
|
|
33
|
+
active_learning_path_id: number,
|
|
34
|
+
active_learning_path_created_at: string,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
interface DailySession {
|
|
38
|
+
content_ids: number[],
|
|
39
|
+
learning_path_id: number,
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
/**
|
|
26
43
|
* Gets today's daily session for the user.
|
|
27
44
|
* @param brand
|
|
@@ -30,7 +47,7 @@ interface ActiveLearningPathResponse {
|
|
|
30
47
|
export async function getDailySession(brand: string, userDate: Date) {
|
|
31
48
|
const stringDate = userDate.toISOString().split('T')[0]
|
|
32
49
|
const url: string = `${LEARNING_PATHS_PATH}/daily-session/get?brand=${brand}&userDate=${stringDate}`
|
|
33
|
-
return await fetchHandler(url, 'GET', null, null)
|
|
50
|
+
return await fetchHandler(url, 'GET', null, null) as DailySessionResponse
|
|
34
51
|
}
|
|
35
52
|
|
|
36
53
|
/**
|
|
@@ -47,7 +64,7 @@ export async function updateDailySession(
|
|
|
47
64
|
const stringDate = userDate.toISOString().split('T')[0]
|
|
48
65
|
const url: string = `${LEARNING_PATHS_PATH}/daily-session/create`
|
|
49
66
|
const body = { brand: brand, userDate: stringDate, keepFirstLearningPath: keepFirstLearningPath }
|
|
50
|
-
return await fetchHandler(url, 'POST', null, body)
|
|
67
|
+
return await fetchHandler(url, 'POST', null, body) as DailySessionResponse
|
|
51
68
|
}
|
|
52
69
|
|
|
53
70
|
/**
|
|
@@ -56,7 +73,7 @@ export async function updateDailySession(
|
|
|
56
73
|
*/
|
|
57
74
|
export async function getActivePath(brand: string) {
|
|
58
75
|
const url: string = `${LEARNING_PATHS_PATH}/active-path/get?brand=${brand}`
|
|
59
|
-
return await fetchHandler(url, 'GET', null, null)
|
|
76
|
+
return await fetchHandler(url, 'GET', null, null) as ActiveLearningPathResponse
|
|
60
77
|
}
|
|
61
78
|
|
|
62
79
|
/**
|
|
@@ -67,7 +84,7 @@ export async function getActivePath(brand: string) {
|
|
|
67
84
|
export async function startLearningPath(brand: string, learningPathId: number) {
|
|
68
85
|
const url: string = `${LEARNING_PATHS_PATH}/active-path/set`
|
|
69
86
|
const body = { brand: brand, learning_path_id: learningPathId }
|
|
70
|
-
return await fetchHandler(url, 'POST', null, body)
|
|
87
|
+
return await fetchHandler(url, 'POST', null, body) as ActiveLearningPathResponse
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
/**
|
|
@@ -87,9 +104,9 @@ export async function getEnrichedLearningPath(learningPathId) {
|
|
|
87
104
|
const response = (await addContextToContent(
|
|
88
105
|
fetchByRailContentId,
|
|
89
106
|
learningPathId,
|
|
90
|
-
|
|
107
|
+
COLLECTION_TYPE.LEARNING_PATH,
|
|
91
108
|
{
|
|
92
|
-
collection: { id: learningPathId, type:
|
|
109
|
+
collection: { id: learningPathId, type: COLLECTION_TYPE.LEARNING_PATH },
|
|
93
110
|
dataField: 'children',
|
|
94
111
|
dataField_includeParent: true,
|
|
95
112
|
addProgressStatus: true,
|
|
@@ -241,7 +258,7 @@ export async function fetchLearningPathProgressCheckLessons(contentIds: number[]
|
|
|
241
258
|
}
|
|
242
259
|
|
|
243
260
|
interface completeMethodIntroVideo {
|
|
244
|
-
intro_video_response:
|
|
261
|
+
intro_video_response: SyncWriteDTO<ContentProgress, any> | null,
|
|
245
262
|
active_path_response: ActiveLearningPathResponse
|
|
246
263
|
}
|
|
247
264
|
/**
|
|
@@ -267,9 +284,9 @@ export async function completeMethodIntroVideo(introVideoId: number, brand: stri
|
|
|
267
284
|
}
|
|
268
285
|
|
|
269
286
|
interface completeLearningPathIntroVideo {
|
|
270
|
-
intro_video_response:
|
|
271
|
-
learning_path_reset_response:
|
|
272
|
-
lesson_import_response:
|
|
287
|
+
intro_video_response: SyncWriteDTO<ContentProgress, any> | null,
|
|
288
|
+
learning_path_reset_response: SyncWriteDTO<ContentProgress, any> | null,
|
|
289
|
+
lesson_import_response: SyncWriteDTO<ContentProgress, any> | null
|
|
273
290
|
}
|
|
274
291
|
/**
|
|
275
292
|
* Handles completion of learning path intro video and other related actions.
|
|
@@ -286,24 +303,20 @@ export async function completeLearningPathIntroVideo(introVideoId: number, learn
|
|
|
286
303
|
|
|
287
304
|
response.intro_video_response = await completeIfNotCompleted(introVideoId)
|
|
288
305
|
|
|
289
|
-
const collection = { id: learningPathId, type:
|
|
306
|
+
const collection = { id: learningPathId, type: COLLECTION_TYPE.LEARNING_PATH }
|
|
290
307
|
|
|
291
308
|
if (!lessonsToImport) {
|
|
292
309
|
response.learning_path_reset_response = await contentStatusReset(learningPathId, collection)
|
|
293
310
|
|
|
294
311
|
} else {
|
|
295
|
-
|
|
296
|
-
for (const contentId of lessonsToImport) {
|
|
297
|
-
// todo: create bulk complete endpoint with bubbling. and set up watermelon method bubbling
|
|
298
|
-
response.lesson_import_response[contentId] = await contentStatusCompleted(contentId, collection)
|
|
299
|
-
}
|
|
312
|
+
response.lesson_import_response = await contentsStatusCompleted(lessonsToImport, collection)
|
|
300
313
|
}
|
|
301
314
|
|
|
302
315
|
return response
|
|
303
316
|
}
|
|
304
317
|
|
|
305
318
|
|
|
306
|
-
async function completeIfNotCompleted(contentId: number): Promise<
|
|
319
|
+
async function completeIfNotCompleted(contentId: number): Promise<SyncWriteDTO<ContentProgress, any> | null> {
|
|
307
320
|
const introVideoStatus = await getProgressState(contentId)
|
|
308
321
|
|
|
309
322
|
return introVideoStatus !== 'completed' ? await contentStatusCompleted(contentId) : null
|
|
File without changes
|
|
File without changes
|
package/src/services/content.js
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { fetchHierarchy } from './sanity.js'
|
|
1
|
+
import { fetchHierarchy, fetchLearningPathHierarchy } from './sanity.js'
|
|
2
2
|
import { db } from './sync'
|
|
3
|
-
import { STATE
|
|
3
|
+
import {COLLECTION_TYPE, STATE} from './sync/models/ContentProgress'
|
|
4
4
|
import { trackUserPractice, findIncompleteLesson } from './userActivity'
|
|
5
5
|
import { getNextLessonLessonParentTypes } from '../contentTypeConfig.js'
|
|
6
6
|
|
|
@@ -66,7 +66,7 @@ export async function getNavigateTo(data, collection = null) {
|
|
|
66
66
|
let incompleteChild = findIncompleteLesson(childrenStates, lastInteracted, content.type)
|
|
67
67
|
navigateToData[content.id] = buildNavigateTo(children.get(incompleteChild), null, collection)
|
|
68
68
|
}
|
|
69
|
-
} else if (['song-tutorial', 'guided-course',
|
|
69
|
+
} else if (['song-tutorial', 'guided-course', COLLECTION_TYPE.LEARNING_PATH].includes(content.type)) { // send to first incomplete
|
|
70
70
|
let incompleteChild = findIncompleteLesson(childrenStates, lastInteracted, content.type)
|
|
71
71
|
navigateToData[content.id] = buildNavigateTo(children.get(incompleteChild), null, collection)
|
|
72
72
|
} else if (twoDepthContentTypes.includes(content.type)) { // send to navigateTo child of last interacted child
|
|
@@ -268,6 +268,11 @@ async function trackProgress(contentId, collection, currentSeconds, mediaLengthS
|
|
|
268
268
|
export async function contentStatusCompleted(contentId, collection = null) {
|
|
269
269
|
return setStartedOrCompletedStatus(contentId, collection, true)
|
|
270
270
|
}
|
|
271
|
+
|
|
272
|
+
export async function contentsStatusCompleted(contentIds, collection = null) {
|
|
273
|
+
return setStartedOrCompletedStatuses(contentIds, collection, true)
|
|
274
|
+
}
|
|
275
|
+
|
|
271
276
|
export async function contentStatusStarted(contentId, collection = null) {
|
|
272
277
|
return setStartedOrCompletedStatus(contentId, collection, false)
|
|
273
278
|
}
|
|
@@ -276,12 +281,12 @@ export async function contentStatusReset(contentId, collection = null) {
|
|
|
276
281
|
}
|
|
277
282
|
|
|
278
283
|
async function saveContentProgress(contentId, collection, progress, currentSeconds) {
|
|
279
|
-
const response = await db.contentProgress.
|
|
284
|
+
const response = await db.contentProgress.recordProgress(contentId, collection, progress, currentSeconds)
|
|
280
285
|
|
|
281
286
|
// note - previous implementation explicitly did not trickle progress to children here
|
|
282
287
|
// (only to siblings/parents via le bubbles)
|
|
283
288
|
|
|
284
|
-
const bubbledProgresses = bubbleProgress(await
|
|
289
|
+
const bubbledProgresses = bubbleProgress(await getHierarchy(contentId, collection), contentId, collection)
|
|
285
290
|
await db.contentProgress.recordProgressesTentative(bubbledProgresses, collection)
|
|
286
291
|
|
|
287
292
|
return response
|
|
@@ -292,23 +297,55 @@ async function setStartedOrCompletedStatus(contentId, collection, isCompleted) {
|
|
|
292
297
|
// we explicitly pessimistically await a remote push here
|
|
293
298
|
// because awards may be generated (on server) on completion
|
|
294
299
|
// which we would want to toast the user about *in band*
|
|
295
|
-
const response = await db.contentProgress.
|
|
300
|
+
const response = await db.contentProgress.recordProgress(contentId, collection, progress)
|
|
301
|
+
|
|
302
|
+
const hierarchy = await getHierarchy(contentId, collection)
|
|
303
|
+
|
|
304
|
+
await Promise.all([
|
|
305
|
+
db.contentProgress.recordProgressesTentative(trickleProgress(hierarchy, contentId, collection, progress), collection),
|
|
306
|
+
bubbleProgress(hierarchy, contentId, collection).then(bubbledProgresses => db.contentProgress.recordProgressesTentative(bubbledProgresses, collection))
|
|
307
|
+
])
|
|
296
308
|
|
|
297
|
-
|
|
298
|
-
|
|
309
|
+
return response
|
|
310
|
+
}
|
|
299
311
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
312
|
+
async function getHierarchy(contentId, collection) {
|
|
313
|
+
if (collection && collection.type === COLLECTION_TYPE.LEARNING_PATH) {
|
|
314
|
+
return await fetchLearningPathHierarchy(contentId, collection)
|
|
315
|
+
} else {
|
|
316
|
+
return await fetchHierarchy(contentId)
|
|
304
317
|
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
async function setStartedOrCompletedStatuses(contentIds, collection, isCompleted) {
|
|
321
|
+
const progress = isCompleted ? 100 : 0
|
|
322
|
+
// we explicitly pessimistically await a remote push here
|
|
323
|
+
// because awards may be generated (on server) on completion
|
|
324
|
+
// which we would want to toast the user about *in band*
|
|
325
|
+
const response = await db.contentProgress.recordProgresses(contentIds, collection, progress)
|
|
326
|
+
|
|
327
|
+
// we assume this is used only for contents within the same hierarchy
|
|
328
|
+
const hierarchy = await getHierarchy(contentIds[0], collection)
|
|
329
|
+
|
|
330
|
+
let ids = {}
|
|
331
|
+
for (const contentId of contentIds) {
|
|
332
|
+
ids = {
|
|
333
|
+
...ids,
|
|
334
|
+
...trickleProgress(hierarchy, contentId, collection, progress),
|
|
335
|
+
...await bubbleProgress(hierarchy, contentId, collection)
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
await Promise.all([
|
|
340
|
+
db.contentProgress.recordProgressesTentative(ids, collection),
|
|
341
|
+
]);
|
|
305
342
|
|
|
306
343
|
return response
|
|
307
344
|
}
|
|
308
345
|
|
|
309
346
|
async function resetStatus(contentId, collection = null) {
|
|
310
347
|
const response = await db.contentProgress.eraseProgress(contentId, collection)
|
|
311
|
-
const hierarchy = await
|
|
348
|
+
const hierarchy = await getHierarchy(contentId, collection)
|
|
312
349
|
|
|
313
350
|
await Promise.all([
|
|
314
351
|
db.contentProgress.recordProgressesTentative(trickleProgress(hierarchy, contentId, collection, 0), collection),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -6,6 +6,7 @@ import { getActivePath, fetchLearningPathLessons } from '../content-org/learning
|
|
|
6
6
|
import { getToday } from '../dateUtils.js'
|
|
7
7
|
import { fetchMethodV2IntroVideo } from '../sanity'
|
|
8
8
|
import { getProgressState } from '../contentProgress'
|
|
9
|
+
import {COLLECTION_TYPE} from "../sync/models/ContentProgress";
|
|
9
10
|
|
|
10
11
|
export async function getMethodCard(brand) {
|
|
11
12
|
const introVideo = await fetchMethodV2IntroVideo(brand)
|
|
@@ -82,7 +83,7 @@ export async function getMethodCard(brand) {
|
|
|
82
83
|
|
|
83
84
|
return {
|
|
84
85
|
id: 1,
|
|
85
|
-
type:
|
|
86
|
+
type: COLLECTION_TYPE.LEARNING_PATH,
|
|
86
87
|
progressType: 'method',
|
|
87
88
|
header: 'Method',
|
|
88
89
|
body: learningPath,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
package/src/services/sanity.js
CHANGED
|
@@ -299,6 +299,7 @@ export async function fetchNewReleases(
|
|
|
299
299
|
"image": thumbnail.asset->url,
|
|
300
300
|
"thumbnail": thumbnail.asset->url,
|
|
301
301
|
${artistOrInstructorName()},
|
|
302
|
+
"instructor": ${instructorField},
|
|
302
303
|
"artists": instructor[]->name,
|
|
303
304
|
difficulty,
|
|
304
305
|
difficulty_string,
|
|
@@ -337,6 +338,7 @@ export async function fetchUpcomingEvents(brand, { page = 1, limit = 10 } = {})
|
|
|
337
338
|
"thumbnail": thumbnail.asset->url,
|
|
338
339
|
${artistOrInstructorName()},
|
|
339
340
|
"artists": instructor[]->name,
|
|
341
|
+
"instructor": ${instructorField},
|
|
340
342
|
difficulty,
|
|
341
343
|
difficulty_string,
|
|
342
344
|
length_in_seconds,
|
|
@@ -388,6 +390,7 @@ export async function fetchScheduledReleases(brand, { page = 1, limit = 10 }) {
|
|
|
388
390
|
"image": thumbnail.asset->url,
|
|
389
391
|
"thumbnail": thumbnail.asset->url,
|
|
390
392
|
${artistOrInstructorName()},
|
|
393
|
+
"instructor": ${instructorField},
|
|
391
394
|
"artists": instructor[]->name,
|
|
392
395
|
difficulty,
|
|
393
396
|
difficulty_string,
|
|
@@ -1523,6 +1526,25 @@ export async function fetchTopLevelParentId(railcontentId) {
|
|
|
1523
1526
|
return response['railcontent_id']
|
|
1524
1527
|
}
|
|
1525
1528
|
|
|
1529
|
+
export async function fetchLearningPathHierarchy(railcontentId, collection) {
|
|
1530
|
+
if (!collection) {
|
|
1531
|
+
return null
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
const topLevelId = collection.id
|
|
1535
|
+
|
|
1536
|
+
let response = await fetchByRailContentId(topLevelId, collection.type)
|
|
1537
|
+
if (!response) return null
|
|
1538
|
+
|
|
1539
|
+
let data = {
|
|
1540
|
+
topLevelId: topLevelId,
|
|
1541
|
+
parents: {},
|
|
1542
|
+
children: {},
|
|
1543
|
+
}
|
|
1544
|
+
populateHierarchyLookups(response, data, null)
|
|
1545
|
+
return data
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1526
1548
|
export async function fetchHierarchy(railcontentId) {
|
|
1527
1549
|
let topLevelId = await fetchTopLevelParentId(railcontentId)
|
|
1528
1550
|
const childrenFilter = await new FilterBuilder(``, { isChildrenFilter: true }).buildFilter()
|
|
@@ -1557,12 +1579,14 @@ export async function fetchHierarchy(railcontentId) {
|
|
|
1557
1579
|
}
|
|
1558
1580
|
|
|
1559
1581
|
function populateHierarchyLookups(currentLevel, data, parentId) {
|
|
1560
|
-
|
|
1582
|
+
const railcontentIdField = currentLevel.railcontent_id ? "railcontent_id" : "id";
|
|
1583
|
+
|
|
1584
|
+
let contentId = currentLevel[railcontentIdField]
|
|
1561
1585
|
let children = currentLevel['children']
|
|
1562
1586
|
|
|
1563
1587
|
data.parents[contentId] = parentId
|
|
1564
1588
|
if (children) {
|
|
1565
|
-
data.children[contentId] = children.map((child) => child[
|
|
1589
|
+
data.children[contentId] = children.map((child) => child[railcontentIdField])
|
|
1566
1590
|
for (let i = 0; i < children.length; i++) {
|
|
1567
1591
|
populateHierarchyLookups(children[i], data, contentId)
|
|
1568
1592
|
}
|
|
@@ -1572,7 +1596,7 @@ function populateHierarchyLookups(currentLevel, data, parentId) {
|
|
|
1572
1596
|
|
|
1573
1597
|
let assignments = currentLevel['assignments']
|
|
1574
1598
|
if (assignments) {
|
|
1575
|
-
let assignmentIds = assignments.map((assignment) => assignment[
|
|
1599
|
+
let assignmentIds = assignments.map((assignment) => assignment[railcontentIdField])
|
|
1576
1600
|
data.children[contentId] = (data.children[contentId] ?? []).concat(assignmentIds)
|
|
1577
1601
|
assignmentIds.forEach((assignmentId) => {
|
|
1578
1602
|
data.parents[assignmentId] = contentId
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|